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 // -Added support for SQL statement logging and database cataloging
11 // -Added QUERY_ONLY mode support to reduce default number of cursors
12 // -Added additional SQL logging code
13 // -Added DEBUG-ONLY tracking of wxTable objects to detect orphaned DB connections
14 // -Set ODBC option to only read committed writes to the DB so all
15 // databases operate the same in that respect
18 // Copyright: (c) 1996 Remstar International, Inc.
19 // Licence: wxWindows licence, plus:
20 // Notice: This class library and its intellectual design are free of charge for use,
21 // modification, enhancement, debugging under the following conditions:
22 // 1) These classes may only be used as part of the implementation of a
23 // wxWindows-based application
24 // 2) All enhancements and bug fixes are to be submitted back to the wxWindows
25 // user groups free of all charges for use with the wxWindows library.
26 // 3) These classes may not be distributed as part of any other class library,
27 // DLL, text (written or electronic), other than a complete distribution of
28 // the wxWindows GUI development toolkit.
29 ///////////////////////////////////////////////////////////////////////////////
36 #include "wx/wxprec.h"
38 // Use this line for wxWindows v1.x
40 // Use this line for wxWindows v2.x
41 #include "wx/version.h"
43 #if wxMAJOR_VERSION == 2
45 #pragma implementation "db.h"
49 #ifdef DBDEBUG_CONSOLE
57 #if wxMAJOR_VERSION == 2
59 #include "wx/string.h"
60 #include "wx/object.h"
63 #include "wx/msgdlg.h"
65 #include "wx/filefn.h"
66 #include "wx/wxchar.h"
69 #if wxMAJOR_VERSION == 1
70 # if defined(wx_msw) || defined(wx_x)
88 #if wxMAJOR_VERSION == 1
90 #elif wxMAJOR_VERSION == 2
94 DbList WXDLLEXPORT
*PtrBegDbList
= 0;
96 char const *SQL_LOG_FILENAME
= "sqllog.txt";
97 char const *SQL_CATALOG_FILENAME
= "catalog.txt";
100 extern wxList TablesInUse
;
103 // SQL Log defaults to be used by GetDbConnection
104 enum sqlLog SQLLOGstate
= sqlLogOFF
;
106 //char SQLLOGfn[DB_PATH_MAX+1] = SQL_LOG_FILENAME;
107 char *SQLLOGfn
= (char*) SQL_LOG_FILENAME
;
109 // The wxDB::errorList is copied to this variable when the wxDB object
110 // is closed. This way, the error list is still available after the
111 // database object is closed. This is necessary if the database
112 // connection fails so the calling application can show the operator
113 // why the connection failed. Note: as each wxDB object is closed, it
114 // will overwrite the errors of the previously destroyed wxDB object in
116 char DBerrorList
[DB_MAX_ERROR_HISTORY
][DB_MAX_ERROR_MSG_LEN
];
119 /********** wxColFor Constructor **********/
122 i_Nation
= 0; // 0=EU, 1=UK, 2=International, 3=US
131 Format(1,DB_DATA_TYPE_VARCHAR
,0,0,0); // the Function that does the work
132 } // wxColFor::wxColFor()
135 wxColFor::~wxColFor()
137 } // wxColFor::~wxColFor()
140 int wxColFor::Format(int Nation
,int dbDataType
,SWORD sqlDataType
,short columnSize
,short decimalDigits
)
142 // ----------------------------------------------------------------------------------------
143 // -- 19991224 : mj10777@gmx.net : Create
144 // There is still a lot of work to do here, but it is a start
145 // It handles all the basic data-types that I have run into up to now
146 // The main work will have be with Dates and float Formatting (US 1,000.00 ; EU 1.000,00)
147 // There are wxWindow plans for locale support and the new wxDateTime.
148 // - if they define some constants (wxEUROPEAN) that can be gloably used,
149 // they should be used here.
150 // ----------------------------------------------------------------------------------------
151 // There should also be a Function to scan in a string to fill the variable
152 // ----------------------------------------------------------------------------------------
154 i_Nation
= Nation
; // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
155 i_dbDataType
= dbDataType
;
156 i_sqlDataType
= sqlDataType
;
157 s_Field
.Printf("%s%d",s_Menge
[1].c_str(),i_Menge
[1]); // OK for VARCHAR, INTEGER and FLOAT
158 if (i_dbDataType
== 0) // Filter unsupported dbDataTypes
160 if ((i_sqlDataType
== SQL_VARCHAR
) || (i_sqlDataType
== SQL_LONGVARCHAR
))
161 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
162 if (i_sqlDataType
== SQL_C_DATE
)
163 i_dbDataType
= DB_DATA_TYPE_DATE
;
164 if (i_sqlDataType
== SQL_C_BIT
)
165 i_dbDataType
= DB_DATA_TYPE_INTEGER
;
166 if (i_sqlDataType
== SQL_NUMERIC
)
167 i_dbDataType
= DB_DATA_TYPE_VARCHAR
;
168 if (i_sqlDataType
== SQL_REAL
)
169 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
171 if ((i_dbDataType
== DB_DATA_TYPE_INTEGER
) && (i_sqlDataType
== SQL_C_DOUBLE
))
173 i_dbDataType
= DB_DATA_TYPE_FLOAT
;
175 switch(i_dbDataType
) // -A-> Still a lot of proper formatting to do
177 case DB_DATA_TYPE_VARCHAR
:
180 case DB_DATA_TYPE_INTEGER
:
183 case DB_DATA_TYPE_FLOAT
:
184 if (decimalDigits
== 0)
187 Temp0
.Printf("%s%d.%d",Temp0
.c_str(),columnSize
,decimalDigits
);
188 s_Field
.Printf("%sf",Temp0
.c_str()); //
190 case DB_DATA_TYPE_DATE
:
191 if (i_Nation
== 0) // timestamp YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
193 s_Field
= "%04d-%02d-%02d %02d:%02d:%02d.%03d";
195 if (i_Nation
== 1) // European DD.MM.YYYY HH:MM:SS.SSS
197 s_Field
= "%02d.%02d.%04d %02d:%02d:%02d.%03d";
199 if (i_Nation
== 2) // UK DD/MM/YYYY HH:MM:SS.SSS
201 s_Field
= "%02d/%02d/%04d %02d:%02d:%02d.%03d";
203 if (i_Nation
== 3) // International YYYY-MM-DD HH:MM:SS.SSS
205 s_Field
= "%04d-%02d-%02d %02d:%02d:%02d.%03d";
207 if (i_Nation
== 4) // US MM/DD/YYYY HH:MM:SS.SSS
209 s_Field
= "%02d/%02d/%04d %02d:%02d:%02d.%03d";
213 s_Field
.Printf("-E-> unknown Format(%d)-sql(%d)",dbDataType
,sqlDataType
); //
217 } // wxColFor::Format()
220 /********** wxDB Constructor **********/
221 wxDB::wxDB(HENV
&aHenv
, bool FwdOnlyCursors
)
225 fpSqlLog
= 0; // Sql Log file pointer
226 sqlLogState
= sqlLogOFF
; // By default, logging is turned off
229 wxStrcpy(sqlState
,"");
230 wxStrcpy(errorMsg
,"");
231 nativeError
= cbErrorMsg
= 0;
232 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
233 wxStrcpy(errorList
[i
], "");
235 // Init typeInf structures
236 wxStrcpy(typeInfVarchar
.TypeName
,"");
237 typeInfVarchar
.FsqlType
= 0;
238 typeInfVarchar
.Precision
= 0;
239 typeInfVarchar
.CaseSensitive
= 0;
240 typeInfVarchar
.MaximumScale
= 0;
242 wxStrcpy(typeInfInteger
.TypeName
,"");
243 typeInfInteger
.FsqlType
= 0;
244 typeInfInteger
.Precision
= 0;
245 typeInfInteger
.CaseSensitive
= 0;
246 typeInfInteger
.MaximumScale
= 0;
248 wxStrcpy(typeInfFloat
.TypeName
,"");
249 typeInfFloat
.FsqlType
= 0;
250 typeInfFloat
.Precision
= 0;
251 typeInfFloat
.CaseSensitive
= 0;
252 typeInfFloat
.MaximumScale
= 0;
254 wxStrcpy(typeInfDate
.TypeName
,"");
255 typeInfDate
.FsqlType
= 0;
256 typeInfDate
.Precision
= 0;
257 typeInfDate
.CaseSensitive
= 0;
258 typeInfDate
.MaximumScale
= 0;
260 // Error reporting is turned OFF by default
263 // Copy the HENV into the db class
265 fwdOnlyCursors
= FwdOnlyCursors
;
267 // Allocate a data source connection handle
268 if (SQLAllocConnect(henv
, &hdbc
) != SQL_SUCCESS
)
271 // Initialize the db status flag
274 // Mark database as not open as of yet
280 /********** wxDB::Open() **********/
281 bool wxDB::Open(char *Dsn
, char *Uid
, char *AuthStr
)
283 assert(Dsn
&& wxStrlen(Dsn
));
290 if (!FwdOnlyCursors())
292 // Specify that the ODBC cursor library be used, if needed. This must be
293 // specified before the connection is made.
294 retcode
= SQLSetConnectOption(hdbc
, SQL_ODBC_CURSORS
, SQL_CUR_USE_IF_NEEDED
);
296 #ifdef DBDEBUG_CONSOLE
297 if (retcode
== SQL_SUCCESS
)
298 cout
<< "SQLSetConnectOption(CURSOR_LIB) successful" << endl
;
300 cout
<< "SQLSetConnectOption(CURSOR_LIB) failed" << endl
;
304 // Connect to the data source
305 retcode
= SQLConnect(hdbc
, (UCHAR FAR
*) Dsn
, SQL_NTS
,
306 (UCHAR FAR
*) Uid
, SQL_NTS
,
307 (UCHAR FAR
*) AuthStr
,SQL_NTS
);
308 if (retcode
== SQL_SUCCESS_WITH_INFO
)
309 DispAllErrors(henv
, hdbc
);
310 else if (retcode
!= SQL_SUCCESS
)
311 return(DispAllErrors(henv
, hdbc
));
314 If using Intersolv branded ODBC drivers, this is the place where you would substitute
315 your branded driver license information
317 SQLSetConnectOption(hdbc, 1041, (UDWORD) "");
318 SQLSetConnectOption(hdbc, 1042, (UDWORD) "");
321 // Mark database as open
324 // Allocate a statement handle for the database connection
325 if (SQLAllocStmt(hdbc
, &hstmt
) != SQL_SUCCESS
)
326 return(DispAllErrors(henv
, hdbc
));
328 // Set Connection Options
329 if (! setConnectionOptions())
332 // Query the data source for inf. about itself
336 // Query the data source regarding data type information
339 // The way I determined which SQL data types to use was by calling SQLGetInfo
340 // for all of the possible SQL data types to see which ones were supported. If
341 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
342 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
343 // types I've selected below will not alway's be what we want. These are just
344 // what happened to work against an Oracle 7/Intersolv combination. The following is
345 // a complete list of the results I got back against the Oracle 7 database:
347 // SQL_BIGINT SQL_NO_DATA_FOUND
348 // SQL_BINARY SQL_NO_DATA_FOUND
349 // SQL_BIT SQL_NO_DATA_FOUND
350 // SQL_CHAR type name = 'CHAR', Precision = 255
351 // SQL_DATE SQL_NO_DATA_FOUND
352 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
353 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
354 // SQL_FLOAT SQL_NO_DATA_FOUND
355 // SQL_INTEGER SQL_NO_DATA_FOUND
356 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
357 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
358 // SQL_NUMERIC SQL_NO_DATA_FOUND
359 // SQL_REAL SQL_NO_DATA_FOUND
360 // SQL_SMALLINT SQL_NO_DATA_FOUND
361 // SQL_TIME SQL_NO_DATA_FOUND
362 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
363 // SQL_VARBINARY type name = 'RAW', Precision = 255
364 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
365 // =====================================================================
366 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
368 // SQL_VARCHAR type name = 'TEXT', Precision = 255
369 // SQL_TIMESTAMP type name = 'DATETIME'
370 // SQL_DECIMAL SQL_NO_DATA_FOUND
371 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
372 // SQL_FLOAT SQL_NO_DATA_FOUND
373 // SQL_REAL type name = 'SINGLE', Precision = 7
374 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
375 // SQL_INTEGER type name = 'LONG', Precision = 10
377 // VARCHAR = Variable length character string
378 if (! getDataTypeInfo(SQL_VARCHAR
, typeInfVarchar
))
379 if (! getDataTypeInfo(SQL_CHAR
, typeInfVarchar
))
382 typeInfVarchar
.FsqlType
= SQL_CHAR
;
384 typeInfVarchar
.FsqlType
= SQL_VARCHAR
;
387 if (! getDataTypeInfo(SQL_DOUBLE
, typeInfFloat
))
388 if (! getDataTypeInfo(SQL_REAL
, typeInfFloat
))
389 if (! getDataTypeInfo(SQL_FLOAT
, typeInfFloat
))
390 if (! getDataTypeInfo(SQL_DECIMAL
, typeInfFloat
))
391 if (! getDataTypeInfo(SQL_NUMERIC
, typeInfFloat
))
394 typeInfFloat
.FsqlType
= SQL_NUMERIC
;
396 typeInfFloat
.FsqlType
= SQL_DECIMAL
;
398 typeInfFloat
.FsqlType
= SQL_FLOAT
;
400 typeInfFloat
.FsqlType
= SQL_REAL
;
402 typeInfFloat
.FsqlType
= SQL_DOUBLE
;
405 if (! getDataTypeInfo(SQL_INTEGER
, typeInfInteger
))
406 // If SQL_INTEGER is not supported, use the floating point
407 // data type to store integers as well as floats
408 if (! getDataTypeInfo(typeInfFloat
.FsqlType
, typeInfInteger
))
411 typeInfInteger
.FsqlType
= typeInfFloat
.FsqlType
;
413 typeInfInteger
.FsqlType
= SQL_INTEGER
;
416 if (Dbms() != dbmsDBASE
)
418 if (! getDataTypeInfo(SQL_TIMESTAMP
, typeInfDate
))
421 typeInfDate
.FsqlType
= SQL_TIMESTAMP
;
425 if (! getDataTypeInfo(SQL_DATE
, typeInfDate
))
428 typeInfDate
.FsqlType
= SQL_DATE
;
431 #ifdef DBDEBUG_CONSOLE
432 cout
<< "VARCHAR DATA TYPE: " << typeInfVarchar
.TypeName
<< endl
;
433 cout
<< "INTEGER DATA TYPE: " << typeInfInteger
.TypeName
<< endl
;
434 cout
<< "FLOAT DATA TYPE: " << typeInfFloat
.TypeName
<< endl
;
435 cout
<< "DATE DATA TYPE: " << typeInfDate
.TypeName
<< endl
;
439 // Completed Successfully
445 /********** wxDB::setConnectionOptions() **********/
446 bool wxDB::setConnectionOptions(void)
448 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
451 SQLSetConnectOption(hdbc
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
452 SQLSetConnectOption(hdbc
, SQL_OPT_TRACE
, SQL_OPT_TRACE_OFF
);
454 // Display the connection options to verify them
455 #ifdef DBDEBUG_CONSOLE
457 cout
<< "****** CONNECTION OPTIONS ******" << endl
;
459 if (SQLGetConnectOption(hdbc
, SQL_AUTOCOMMIT
, &l
) != SQL_SUCCESS
)
460 return(DispAllErrors(henv
, hdbc
));
461 cout
<< "AUTOCOMMIT: " << (l
== SQL_AUTOCOMMIT_OFF
? "OFF" : "ON") << endl
;
463 if (SQLGetConnectOption(hdbc
, SQL_ODBC_CURSORS
, &l
) != SQL_SUCCESS
)
464 return(DispAllErrors(henv
, hdbc
));
465 cout
<< "ODBC CURSORS: ";
468 case(SQL_CUR_USE_IF_NEEDED
):
469 cout
<< "SQL_CUR_USE_IF_NEEDED";
471 case(SQL_CUR_USE_ODBC
):
472 cout
<< "SQL_CUR_USE_ODBC";
474 case(SQL_CUR_USE_DRIVER
):
475 cout
<< "SQL_CUR_USE_DRIVER";
480 if (SQLGetConnectOption(hdbc
, SQL_OPT_TRACE
, &l
) != SQL_SUCCESS
)
481 return(DispAllErrors(henv
, hdbc
));
482 cout
<< "TRACING: " << (l
== SQL_OPT_TRACE_OFF
? "OFF" : "ON") << endl
;
487 // Completed Successfully
490 } // wxDB::setConnectionOptions()
493 /********** wxDB::getDbInfo() **********/
494 bool wxDB::getDbInfo(void)
499 if (SQLGetInfo(hdbc
, SQL_SERVER_NAME
, (UCHAR
*) dbInf
.serverName
, 80, &cb
) != SQL_SUCCESS
)
500 return(DispAllErrors(henv
, hdbc
));
502 if (SQLGetInfo(hdbc
, SQL_DATABASE_NAME
, (UCHAR
*) dbInf
.databaseName
, 128, &cb
) != SQL_SUCCESS
)
503 return(DispAllErrors(henv
, hdbc
));
505 if (SQLGetInfo(hdbc
, SQL_DBMS_NAME
, (UCHAR
*) dbInf
.dbmsName
, 40, &cb
) != SQL_SUCCESS
)
506 return(DispAllErrors(henv
, hdbc
));
509 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
510 // causing database connectivity to fail in some cases.
511 retcode
= SQLGetInfo(hdbc
, SQL_DBMS_VER
, (UCHAR
*) dbInf
.dbmsVer
, 64, &cb
);
513 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
514 return(DispAllErrors(henv
, hdbc
));
516 if (SQLGetInfo(hdbc
, SQL_ACTIVE_CONNECTIONS
, (UCHAR
*) &dbInf
.maxConnections
, sizeof(dbInf
.maxConnections
), &cb
) != SQL_SUCCESS
)
517 return(DispAllErrors(henv
, hdbc
));
519 if (SQLGetInfo(hdbc
, SQL_ACTIVE_STATEMENTS
, (UCHAR
*) &dbInf
.maxStmts
, sizeof(dbInf
.maxStmts
), &cb
) != SQL_SUCCESS
)
520 return(DispAllErrors(henv
, hdbc
));
522 if (SQLGetInfo(hdbc
, SQL_DRIVER_NAME
, (UCHAR
*) dbInf
.driverName
, 40, &cb
) != SQL_SUCCESS
)
523 return(DispAllErrors(henv
, hdbc
));
525 if (SQLGetInfo(hdbc
, SQL_DRIVER_ODBC_VER
, (UCHAR
*) dbInf
.odbcVer
, 60, &cb
) == SQL_ERROR
)
526 return(DispAllErrors(henv
, hdbc
));
528 retcode
= SQLGetInfo(hdbc
, SQL_ODBC_VER
, (UCHAR
*) dbInf
.drvMgrOdbcVer
, 60, &cb
);
529 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
530 return(DispAllErrors(henv
, hdbc
));
532 if (SQLGetInfo(hdbc
, SQL_DRIVER_VER
, (UCHAR
*) dbInf
.driverVer
, 60, &cb
) == SQL_ERROR
)
533 return(DispAllErrors(henv
, hdbc
));
535 if (SQLGetInfo(hdbc
, SQL_ODBC_API_CONFORMANCE
, (UCHAR
*) &dbInf
.apiConfLvl
, sizeof(dbInf
.apiConfLvl
), &cb
) != SQL_SUCCESS
)
536 return(DispAllErrors(henv
, hdbc
));
538 if (SQLGetInfo(hdbc
, SQL_ODBC_SAG_CLI_CONFORMANCE
, (UCHAR
*) &dbInf
.cliConfLvl
, sizeof(dbInf
.cliConfLvl
), &cb
) != SQL_SUCCESS
)
539 return(DispAllErrors(henv
, hdbc
));
541 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_CONFORMANCE
, (UCHAR
*) &dbInf
.sqlConfLvl
, sizeof(dbInf
.sqlConfLvl
), &cb
) != SQL_SUCCESS
)
542 return(DispAllErrors(henv
, hdbc
));
544 if (SQLGetInfo(hdbc
, SQL_OUTER_JOINS
, (UCHAR
*) dbInf
.outerJoins
, 2, &cb
) != SQL_SUCCESS
)
545 return(DispAllErrors(henv
, hdbc
));
547 if (SQLGetInfo(hdbc
, SQL_PROCEDURES
, (UCHAR
*) dbInf
.procedureSupport
, 2, &cb
) != SQL_SUCCESS
)
548 return(DispAllErrors(henv
, hdbc
));
550 if (SQLGetInfo(hdbc
, SQL_CURSOR_COMMIT_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorCommitBehavior
, sizeof(dbInf
.cursorCommitBehavior
), &cb
) != SQL_SUCCESS
)
551 return(DispAllErrors(henv
, hdbc
));
553 if (SQLGetInfo(hdbc
, SQL_CURSOR_ROLLBACK_BEHAVIOR
, (UCHAR
*) &dbInf
.cursorRollbackBehavior
, sizeof(dbInf
.cursorRollbackBehavior
), &cb
) != SQL_SUCCESS
)
554 return(DispAllErrors(henv
, hdbc
));
556 if (SQLGetInfo(hdbc
, SQL_NON_NULLABLE_COLUMNS
, (UCHAR
*) &dbInf
.supportNotNullClause
, sizeof(dbInf
.supportNotNullClause
), &cb
) != SQL_SUCCESS
)
557 return(DispAllErrors(henv
, hdbc
));
559 if (SQLGetInfo(hdbc
, SQL_ODBC_SQL_OPT_IEF
, (UCHAR
*) dbInf
.supportIEF
, 2, &cb
) != SQL_SUCCESS
)
560 return(DispAllErrors(henv
, hdbc
));
562 if (SQLGetInfo(hdbc
, SQL_DEFAULT_TXN_ISOLATION
, (UCHAR
*) &dbInf
.txnIsolation
, sizeof(dbInf
.txnIsolation
), &cb
) != SQL_SUCCESS
)
563 return(DispAllErrors(henv
, hdbc
));
565 if (SQLGetInfo(hdbc
, SQL_TXN_ISOLATION_OPTION
, (UCHAR
*) &dbInf
.txnIsolationOptions
, sizeof(dbInf
.txnIsolationOptions
), &cb
) != SQL_SUCCESS
)
566 return(DispAllErrors(henv
, hdbc
));
568 if (SQLGetInfo(hdbc
, SQL_FETCH_DIRECTION
, (UCHAR
*) &dbInf
.fetchDirections
, sizeof(dbInf
.fetchDirections
), &cb
) != SQL_SUCCESS
)
569 return(DispAllErrors(henv
, hdbc
));
571 if (SQLGetInfo(hdbc
, SQL_LOCK_TYPES
, (UCHAR
*) &dbInf
.lockTypes
, sizeof(dbInf
.lockTypes
), &cb
) != SQL_SUCCESS
)
572 return(DispAllErrors(henv
, hdbc
));
574 if (SQLGetInfo(hdbc
, SQL_POS_OPERATIONS
, (UCHAR
*) &dbInf
.posOperations
, sizeof(dbInf
.posOperations
), &cb
) != SQL_SUCCESS
)
575 return(DispAllErrors(henv
, hdbc
));
577 if (SQLGetInfo(hdbc
, SQL_POSITIONED_STATEMENTS
, (UCHAR
*) &dbInf
.posStmts
, sizeof(dbInf
.posStmts
), &cb
) != SQL_SUCCESS
)
578 return(DispAllErrors(henv
, hdbc
));
580 if (SQLGetInfo(hdbc
, SQL_SCROLL_CONCURRENCY
, (UCHAR
*) &dbInf
.scrollConcurrency
, sizeof(dbInf
.scrollConcurrency
), &cb
) != SQL_SUCCESS
)
581 return(DispAllErrors(henv
, hdbc
));
583 if (SQLGetInfo(hdbc
, SQL_SCROLL_OPTIONS
, (UCHAR
*) &dbInf
.scrollOptions
, sizeof(dbInf
.scrollOptions
), &cb
) != SQL_SUCCESS
)
584 return(DispAllErrors(henv
, hdbc
));
586 if (SQLGetInfo(hdbc
, SQL_STATIC_SENSITIVITY
, (UCHAR
*) &dbInf
.staticSensitivity
, sizeof(dbInf
.staticSensitivity
), &cb
) != SQL_SUCCESS
)
587 return(DispAllErrors(henv
, hdbc
));
589 if (SQLGetInfo(hdbc
, SQL_TXN_CAPABLE
, (UCHAR
*) &dbInf
.txnCapable
, sizeof(dbInf
.txnCapable
), &cb
) != SQL_SUCCESS
)
590 return(DispAllErrors(henv
, hdbc
));
592 if (SQLGetInfo(hdbc
, SQL_LOGIN_TIMEOUT
, (UCHAR
*) &dbInf
.loginTimeout
, sizeof(dbInf
.loginTimeout
), &cb
) != SQL_SUCCESS
)
593 return(DispAllErrors(henv
, hdbc
));
595 #ifdef DBDEBUG_CONSOLE
596 cout
<< "***** DATA SOURCE INFORMATION *****" << endl
;
597 cout
<< "SERVER Name: " << dbInf
.serverName
<< endl
;
598 cout
<< "DBMS Name: " << dbInf
.dbmsName
<< "; DBMS Version: " << dbInf
.dbmsVer
<< endl
;
599 cout
<< "ODBC Version: " << dbInf
.odbcVer
<< "; Driver Version: " << dbInf
.driverVer
<< endl
;
601 cout
<< "API Conf. Level: ";
602 switch(dbInf
.apiConfLvl
)
604 case SQL_OAC_NONE
: cout
<< "None"; break;
605 case SQL_OAC_LEVEL1
: cout
<< "Level 1"; break;
606 case SQL_OAC_LEVEL2
: cout
<< "Level 2"; break;
610 cout
<< "SAG CLI Conf. Level: ";
611 switch(dbInf
.cliConfLvl
)
613 case SQL_OSCC_NOT_COMPLIANT
: cout
<< "Not Compliant"; break;
614 case SQL_OSCC_COMPLIANT
: cout
<< "Compliant"; break;
618 cout
<< "SQL Conf. Level: ";
619 switch(dbInf
.sqlConfLvl
)
621 case SQL_OSC_MINIMUM
: cout
<< "Minimum Grammer"; break;
622 case SQL_OSC_CORE
: cout
<< "Core Grammer"; break;
623 case SQL_OSC_EXTENDED
: cout
<< "Extended Grammer"; break;
627 cout
<< "Max. Connections: " << dbInf
.maxConnections
<< endl
;
628 cout
<< "Outer Joins: " << dbInf
.outerJoins
<< endl
;
629 cout
<< "Support for Procedures: " << dbInf
.procedureSupport
<< endl
;
631 cout
<< "Cursor COMMIT Behavior: ";
632 switch(dbInf
.cursorCommitBehavior
)
634 case SQL_CB_DELETE
: cout
<< "Delete cursors"; break;
635 case SQL_CB_CLOSE
: cout
<< "Close cursors"; break;
636 case SQL_CB_PRESERVE
: cout
<< "Preserve cursors"; break;
640 cout
<< "Cursor ROLLBACK Behavior: ";
641 switch(dbInf
.cursorRollbackBehavior
)
643 case SQL_CB_DELETE
: cout
<< "Delete cursors"; break;
644 case SQL_CB_CLOSE
: cout
<< "Close cursors"; break;
645 case SQL_CB_PRESERVE
: cout
<< "Preserve cursors"; break;
649 cout
<< "Support NOT NULL clause: ";
650 switch(dbInf
.supportNotNullClause
)
652 case SQL_NNC_NULL
: cout
<< "No"; break;
653 case SQL_NNC_NON_NULL
: cout
<< "Yes"; break;
657 cout
<< "Support IEF (Ref. Integrity): " << dbInf
.supportIEF
<< endl
;
658 cout
<< "Login Timeout: " << dbInf
.loginTimeout
<< endl
;
660 cout
<< endl
<< endl
<< "more ..." << endl
;
663 cout
<< "Default Transaction Isolation: ";
664 switch(dbInf
.txnIsolation
)
666 case SQL_TXN_READ_UNCOMMITTED
: cout
<< "Read Uncommitted"; break;
667 case SQL_TXN_READ_COMMITTED
: cout
<< "Read Committed"; break;
668 case SQL_TXN_REPEATABLE_READ
: cout
<< "Repeatable Read"; break;
669 case SQL_TXN_SERIALIZABLE
: cout
<< "Serializable"; break;
671 case SQL_TXN_VERSIONING
: cout
<< "Versioning"; break;
676 cout
<< "Transaction Isolation Options: ";
677 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_UNCOMMITTED
)
678 cout
<< "Read Uncommitted, ";
679 if (dbInf
.txnIsolationOptions
& SQL_TXN_READ_COMMITTED
)
680 cout
<< "Read Committed, ";
681 if (dbInf
.txnIsolationOptions
& SQL_TXN_REPEATABLE_READ
)
682 cout
<< "Repeatable Read, ";
683 if (dbInf
.txnIsolationOptions
& SQL_TXN_SERIALIZABLE
)
684 cout
<< "Serializable, ";
686 if (dbInf
.txnIsolationOptions
& SQL_TXN_VERSIONING
)
687 cout
<< "Versioning";
691 cout
<< "Fetch Directions Supported:" << endl
<< " ";
692 if (dbInf
.fetchDirections
& SQL_FD_FETCH_NEXT
)
694 if (dbInf
.fetchDirections
& SQL_FD_FETCH_PRIOR
)
696 if (dbInf
.fetchDirections
& SQL_FD_FETCH_FIRST
)
698 if (dbInf
.fetchDirections
& SQL_FD_FETCH_LAST
)
700 if (dbInf
.fetchDirections
& SQL_FD_FETCH_ABSOLUTE
)
701 cout
<< "Absolute, ";
702 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RELATIVE
)
703 cout
<< "Relative, ";
705 if (dbInf
.fetchDirections
& SQL_FD_FETCH_RESUME
)
708 if (dbInf
.fetchDirections
& SQL_FD_FETCH_BOOKMARK
)
712 cout
<< "Lock Types Supported (SQLSetPos): ";
713 if (dbInf
.lockTypes
& SQL_LCK_NO_CHANGE
)
714 cout
<< "No Change, ";
715 if (dbInf
.lockTypes
& SQL_LCK_EXCLUSIVE
)
716 cout
<< "Exclusive, ";
717 if (dbInf
.lockTypes
& SQL_LCK_UNLOCK
)
721 cout
<< "Position Operations Supported (SQLSetPos): ";
722 if (dbInf
.posOperations
& SQL_POS_POSITION
)
723 cout
<< "Position, ";
724 if (dbInf
.posOperations
& SQL_POS_REFRESH
)
726 if (dbInf
.posOperations
& SQL_POS_UPDATE
)
728 if (dbInf
.posOperations
& SQL_POS_DELETE
)
730 if (dbInf
.posOperations
& SQL_POS_ADD
)
734 cout
<< "Positioned Statements Supported: ";
735 if (dbInf
.posStmts
& SQL_PS_POSITIONED_DELETE
)
736 cout
<< "Pos delete, ";
737 if (dbInf
.posStmts
& SQL_PS_POSITIONED_UPDATE
)
738 cout
<< "Pos update, ";
739 if (dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
740 cout
<< "Select for update";
743 cout
<< "Scroll Concurrency: ";
744 if (dbInf
.scrollConcurrency
& SQL_SCCO_READ_ONLY
)
745 cout
<< "Read Only, ";
746 if (dbInf
.scrollConcurrency
& SQL_SCCO_LOCK
)
748 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_ROWVER
)
749 cout
<< "Opt. Rowver, ";
750 if (dbInf
.scrollConcurrency
& SQL_SCCO_OPT_VALUES
)
751 cout
<< "Opt. Values";
754 cout
<< "Scroll Options: ";
755 if (dbInf
.scrollOptions
& SQL_SO_FORWARD_ONLY
)
756 cout
<< "Fwd Only, ";
757 if (dbInf
.scrollOptions
& SQL_SO_STATIC
)
759 if (dbInf
.scrollOptions
& SQL_SO_KEYSET_DRIVEN
)
760 cout
<< "Keyset Driven, ";
761 if (dbInf
.scrollOptions
& SQL_SO_DYNAMIC
)
763 if (dbInf
.scrollOptions
& SQL_SO_MIXED
)
767 cout
<< "Static Sensitivity: ";
768 if (dbInf
.staticSensitivity
& SQL_SS_ADDITIONS
)
769 cout
<< "Additions, ";
770 if (dbInf
.staticSensitivity
& SQL_SS_DELETIONS
)
771 cout
<< "Deletions, ";
772 if (dbInf
.staticSensitivity
& SQL_SS_UPDATES
)
776 cout
<< "Transaction Capable?: ";
777 switch(dbInf
.txnCapable
)
779 case SQL_TC_NONE
: cout
<< "No"; break;
780 case SQL_TC_DML
: cout
<< "DML Only"; break;
781 case SQL_TC_DDL_COMMIT
: cout
<< "DDL Commit"; break;
782 case SQL_TC_DDL_IGNORE
: cout
<< "DDL Ignore"; break;
783 case SQL_TC_ALL
: cout
<< "DDL & DML"; break;
790 // Completed Successfully
793 } // wxDB::getDbInfo()
796 /********** wxDB::getDataTypeInfo() **********/
797 bool wxDB::getDataTypeInfo(SWORD fSqlType
, SqlTypeInfo
&structSQLTypeInfo
)
800 * fSqlType will be something like SQL_VARCHAR. This parameter determines
801 * the data type inf. is gathered for.
803 * SqlTypeInfo is a structure that is filled in with data type information,
808 // Get information about the data type specified
809 if (SQLGetTypeInfo(hstmt
, fSqlType
) != SQL_SUCCESS
)
810 return(DispAllErrors(henv
, hdbc
, hstmt
));
812 if ((retcode
= SQLFetch(hstmt
)) != SQL_SUCCESS
)
814 #ifdef DBDEBUG_CONSOLE
815 if (retcode
== SQL_NO_DATA_FOUND
)
816 cout
<< "SQL_NO_DATA_FOUND fetching inf. about data type." << endl
;
818 DispAllErrors(henv
, hdbc
, hstmt
);
819 SQLFreeStmt(hstmt
, SQL_CLOSE
);
822 // Obtain columns from the record
823 if (SQLGetData(hstmt
, 1, SQL_C_CHAR
, (UCHAR
*) structSQLTypeInfo
.TypeName
, DB_TYPE_NAME_LEN
, &cbRet
) != SQL_SUCCESS
)
824 return(DispAllErrors(henv
, hdbc
, hstmt
));
827 if (Dbms() == dbmsMY_SQL
)
829 if (!strcmp(structSQLTypeInfo
.TypeName
, "middleint")) strcpy(structSQLTypeInfo
.TypeName
, "mediumint");
830 if (!strcmp(structSQLTypeInfo
.TypeName
, "middleint unsigned")) strcpy(structSQLTypeInfo
.TypeName
, "mediumint unsigned");
831 if (!strcmp(structSQLTypeInfo
.TypeName
, "integer")) strcpy(structSQLTypeInfo
.TypeName
, "int");
832 if (!strcmp(structSQLTypeInfo
.TypeName
, "integer unsigned")) strcpy(structSQLTypeInfo
.TypeName
, "int unsigned");
833 if (!strcmp(structSQLTypeInfo
.TypeName
, "middleint")) strcpy(structSQLTypeInfo
.TypeName
, "mediumint");
834 if (!strcmp(structSQLTypeInfo
.TypeName
, "varchar")) strcpy(structSQLTypeInfo
.TypeName
, "char");
837 if (SQLGetData(hstmt
, 3, SQL_C_LONG
, (UCHAR
*) &structSQLTypeInfo
.Precision
, 0, &cbRet
) != SQL_SUCCESS
)
838 return(DispAllErrors(henv
, hdbc
, hstmt
));
839 if (SQLGetData(hstmt
, 8, SQL_C_SHORT
, (UCHAR
*) &structSQLTypeInfo
.CaseSensitive
, 0, &cbRet
) != SQL_SUCCESS
)
840 return(DispAllErrors(henv
, hdbc
, hstmt
));
841 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
842 // return(DispAllErrors(henv, hdbc, hstmt));
844 if (SQLGetData(hstmt
, 15, SQL_C_SHORT
,(UCHAR
*) &structSQLTypeInfo
.MaximumScale
, 0, &cbRet
) != SQL_SUCCESS
)
845 return(DispAllErrors(henv
, hdbc
, hstmt
));
847 if (structSQLTypeInfo
.MaximumScale
< 0)
848 structSQLTypeInfo
.MaximumScale
= 0;
850 // Close the statement handle which closes open cursors
851 if (SQLFreeStmt(hstmt
, SQL_CLOSE
) != SQL_SUCCESS
)
852 return(DispAllErrors(henv
, hdbc
, hstmt
));
854 // Completed Successfully
857 } // wxDB::getDataTypeInfo()
860 /********** wxDB::Close() **********/
861 void wxDB::Close(void)
863 // Close the Sql Log file
870 // Free statement handle
873 if (SQLFreeStmt(hstmt
, SQL_DROP
) != SQL_SUCCESS
)
874 DispAllErrors(henv
, hdbc
);
877 // Disconnect from the datasource
878 if (SQLDisconnect(hdbc
) != SQL_SUCCESS
)
879 DispAllErrors(henv
, hdbc
);
881 // Free the connection to the datasource
882 if (SQLFreeConnect(hdbc
) != SQL_SUCCESS
)
883 DispAllErrors(henv
, hdbc
);
885 // There should be zero Ctable objects still connected to this db object
886 assert(nTables
== 0);
889 CstructTablesInUse
*tiu
;
891 pNode
= TablesInUse
.First();
895 tiu
= (CstructTablesInUse
*)pNode
->Data();
896 if (tiu
->pDb
== this)
898 s
.sprintf("(%-20s) tableID:[%6lu] pDb:[%p]", tiu
->tableName
,tiu
->tableID
,tiu
->pDb
);
899 s2
.sprintf("Orphaned found using pDb:[%p]",this);
902 pNode
= pNode
->Next();
906 // Copy the error messages to a global variable
908 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
909 wxStrcpy(DBerrorList
[i
],errorList
[i
]);
914 /********** wxDB::CommitTrans() **********/
915 bool wxDB::CommitTrans(void)
919 // Commit the transaction
920 if (SQLTransact(henv
, hdbc
, SQL_COMMIT
) != SQL_SUCCESS
)
921 return(DispAllErrors(henv
, hdbc
));
924 // Completed successfully
927 } // wxDB::CommitTrans()
930 /********** wxDB::RollbackTrans() **********/
931 bool wxDB::RollbackTrans(void)
933 // Rollback the transaction
934 if (SQLTransact(henv
, hdbc
, SQL_ROLLBACK
) != SQL_SUCCESS
)
935 return(DispAllErrors(henv
, hdbc
));
937 // Completed successfully
940 } // wxDB::RollbackTrans()
943 /********** wxDB::DispAllErrors() **********/
944 bool wxDB::DispAllErrors(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
946 // char odbcErrMsg[DB_MAX_ERROR_MSG_LEN];
949 while (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR
*) sqlState
, &nativeError
, (UCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
951 odbcErrMsg
.sprintf("SQL State = %s\nNative Error Code = %li\nError Message = %s\n", sqlState
, nativeError
, errorMsg
);
952 logError(odbcErrMsg
.GetData(), sqlState
);
955 #ifdef DBDEBUG_CONSOLE
956 // When run in console mode, use standard out to display errors.
957 cout
<< odbcErrMsg
.GetData() << endl
;
958 cout
<< "Press any key to continue..." << endl
;
964 wxMessageBox(odbcErrMsg
.GetData(),"DEBUG MESSAGE from DispAllErrors()");
968 return(FALSE
); // This function always returns false.
970 } // wxDB::DispAllErrors()
973 /********** wxDB::GetNextError() **********/
974 bool wxDB::GetNextError(HENV aHenv
, HDBC aHdbc
, HSTMT aHstmt
)
976 if (SQLError(aHenv
, aHdbc
, aHstmt
, (UCHAR FAR
*) sqlState
, &nativeError
, (UCHAR FAR
*) errorMsg
, SQL_MAX_MESSAGE_LENGTH
- 1, &cbErrorMsg
) == SQL_SUCCESS
)
981 } // wxDB::GetNextError()
984 /********** wxDB::DispNextError() **********/
985 void wxDB::DispNextError(void)
987 // char odbcErrMsg[DB_MAX_ERROR_MSG_LEN];
990 odbcErrMsg
.sprintf("SQL State = %s\nNative Error Code = %li\nError Message = %s\n", sqlState
, nativeError
, errorMsg
);
991 logError(odbcErrMsg
.GetData(), sqlState
);
996 #ifdef DBDEBUG_CONSOLE
997 // When run in console mode, use standard out to display errors.
998 cout
<< odbcErrMsg
.GetData() << endl
;
999 cout
<< "Press any key to continue..." << endl
;
1003 } // wxDB::DispNextError()
1006 /********** wxDB::logError() **********/
1007 void wxDB::logError(const char *errMsg
, const char *SQLState
)
1009 assert(errMsg
&& wxStrlen(errMsg
));
1011 static int pLast
= -1;
1014 if (++pLast
== DB_MAX_ERROR_HISTORY
)
1017 for (i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
1018 wxStrcpy(errorList
[i
], errorList
[i
+1]);
1022 wxStrcpy(errorList
[pLast
], errMsg
);
1024 if (SQLState
&& wxStrlen(SQLState
))
1025 if ((dbStatus
= TranslateSqlState(SQLState
)) != DB_ERR_FUNCTION_SEQUENCE_ERROR
)
1026 DB_STATUS
= dbStatus
;
1028 // Add the errmsg to the sql log
1029 WriteSqlLog(errMsg
);
1031 } // wxDB::logError()
1034 /**********wxDB::TranslateSqlState() **********/
1035 int wxDB::TranslateSqlState(const char *SQLState
)
1037 if (!wxStrcmp(SQLState
, "01000"))
1038 return(DB_ERR_GENERAL_WARNING
);
1039 if (!wxStrcmp(SQLState
, "01002"))
1040 return(DB_ERR_DISCONNECT_ERROR
);
1041 if (!wxStrcmp(SQLState
, "01004"))
1042 return(DB_ERR_DATA_TRUNCATED
);
1043 if (!wxStrcmp(SQLState
, "01006"))
1044 return(DB_ERR_PRIV_NOT_REVOKED
);
1045 if (!wxStrcmp(SQLState
, "01S00"))
1046 return(DB_ERR_INVALID_CONN_STR_ATTR
);
1047 if (!wxStrcmp(SQLState
, "01S01"))
1048 return(DB_ERR_ERROR_IN_ROW
);
1049 if (!wxStrcmp(SQLState
, "01S02"))
1050 return(DB_ERR_OPTION_VALUE_CHANGED
);
1051 if (!wxStrcmp(SQLState
, "01S03"))
1052 return(DB_ERR_NO_ROWS_UPD_OR_DEL
);
1053 if (!wxStrcmp(SQLState
, "01S04"))
1054 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL
);
1055 if (!wxStrcmp(SQLState
, "07001"))
1056 return(DB_ERR_WRONG_NO_OF_PARAMS
);
1057 if (!wxStrcmp(SQLState
, "07006"))
1058 return(DB_ERR_DATA_TYPE_ATTR_VIOL
);
1059 if (!wxStrcmp(SQLState
, "08001"))
1060 return(DB_ERR_UNABLE_TO_CONNECT
);
1061 if (!wxStrcmp(SQLState
, "08002"))
1062 return(DB_ERR_CONNECTION_IN_USE
);
1063 if (!wxStrcmp(SQLState
, "08003"))
1064 return(DB_ERR_CONNECTION_NOT_OPEN
);
1065 if (!wxStrcmp(SQLState
, "08004"))
1066 return(DB_ERR_REJECTED_CONNECTION
);
1067 if (!wxStrcmp(SQLState
, "08007"))
1068 return(DB_ERR_CONN_FAIL_IN_TRANS
);
1069 if (!wxStrcmp(SQLState
, "08S01"))
1070 return(DB_ERR_COMM_LINK_FAILURE
);
1071 if (!wxStrcmp(SQLState
, "21S01"))
1072 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH
);
1073 if (!wxStrcmp(SQLState
, "21S02"))
1074 return(DB_ERR_DERIVED_TABLE_MISMATCH
);
1075 if (!wxStrcmp(SQLState
, "22001"))
1076 return(DB_ERR_STRING_RIGHT_TRUNC
);
1077 if (!wxStrcmp(SQLState
, "22003"))
1078 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG
);
1079 if (!wxStrcmp(SQLState
, "22005"))
1080 return(DB_ERR_ERROR_IN_ASSIGNMENT
);
1081 if (!wxStrcmp(SQLState
, "22008"))
1082 return(DB_ERR_DATETIME_FLD_OVERFLOW
);
1083 if (!wxStrcmp(SQLState
, "22012"))
1084 return(DB_ERR_DIVIDE_BY_ZERO
);
1085 if (!wxStrcmp(SQLState
, "22026"))
1086 return(DB_ERR_STR_DATA_LENGTH_MISMATCH
);
1087 if (!wxStrcmp(SQLState
, "23000"))
1088 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1089 if (!wxStrcmp(SQLState
, "24000"))
1090 return(DB_ERR_INVALID_CURSOR_STATE
);
1091 if (!wxStrcmp(SQLState
, "25000"))
1092 return(DB_ERR_INVALID_TRANS_STATE
);
1093 if (!wxStrcmp(SQLState
, "28000"))
1094 return(DB_ERR_INVALID_AUTH_SPEC
);
1095 if (!wxStrcmp(SQLState
, "34000"))
1096 return(DB_ERR_INVALID_CURSOR_NAME
);
1097 if (!wxStrcmp(SQLState
, "37000"))
1098 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL
);
1099 if (!wxStrcmp(SQLState
, "3C000"))
1100 return(DB_ERR_DUPLICATE_CURSOR_NAME
);
1101 if (!wxStrcmp(SQLState
, "40001"))
1102 return(DB_ERR_SERIALIZATION_FAILURE
);
1103 if (!wxStrcmp(SQLState
, "42000"))
1104 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2
);
1105 if (!wxStrcmp(SQLState
, "70100"))
1106 return(DB_ERR_OPERATION_ABORTED
);
1107 if (!wxStrcmp(SQLState
, "IM001"))
1108 return(DB_ERR_UNSUPPORTED_FUNCTION
);
1109 if (!wxStrcmp(SQLState
, "IM002"))
1110 return(DB_ERR_NO_DATA_SOURCE
);
1111 if (!wxStrcmp(SQLState
, "IM003"))
1112 return(DB_ERR_DRIVER_LOAD_ERROR
);
1113 if (!wxStrcmp(SQLState
, "IM004"))
1114 return(DB_ERR_SQLALLOCENV_FAILED
);
1115 if (!wxStrcmp(SQLState
, "IM005"))
1116 return(DB_ERR_SQLALLOCCONNECT_FAILED
);
1117 if (!wxStrcmp(SQLState
, "IM006"))
1118 return(DB_ERR_SQLSETCONNECTOPTION_FAILED
);
1119 if (!wxStrcmp(SQLState
, "IM007"))
1120 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB
);
1121 if (!wxStrcmp(SQLState
, "IM008"))
1122 return(DB_ERR_DIALOG_FAILED
);
1123 if (!wxStrcmp(SQLState
, "IM009"))
1124 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL
);
1125 if (!wxStrcmp(SQLState
, "IM010"))
1126 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG
);
1127 if (!wxStrcmp(SQLState
, "IM011"))
1128 return(DB_ERR_DRIVER_NAME_TOO_LONG
);
1129 if (!wxStrcmp(SQLState
, "IM012"))
1130 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR
);
1131 if (!wxStrcmp(SQLState
, "IM013"))
1132 return(DB_ERR_TRACE_FILE_ERROR
);
1133 if (!wxStrcmp(SQLState
, "S0001"))
1134 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS
);
1135 if (!wxStrcmp(SQLState
, "S0002"))
1136 return(DB_ERR_TABLE_NOT_FOUND
);
1137 if (!wxStrcmp(SQLState
, "S0011"))
1138 return(DB_ERR_INDEX_ALREADY_EXISTS
);
1139 if (!wxStrcmp(SQLState
, "S0012"))
1140 return(DB_ERR_INDEX_NOT_FOUND
);
1141 if (!wxStrcmp(SQLState
, "S0021"))
1142 return(DB_ERR_COLUMN_ALREADY_EXISTS
);
1143 if (!wxStrcmp(SQLState
, "S0022"))
1144 return(DB_ERR_COLUMN_NOT_FOUND
);
1145 if (!wxStrcmp(SQLState
, "S0023"))
1146 return(DB_ERR_NO_DEFAULT_FOR_COLUMN
);
1147 if (!wxStrcmp(SQLState
, "S1000"))
1148 return(DB_ERR_GENERAL_ERROR
);
1149 if (!wxStrcmp(SQLState
, "S1001"))
1150 return(DB_ERR_MEMORY_ALLOCATION_FAILURE
);
1151 if (!wxStrcmp(SQLState
, "S1002"))
1152 return(DB_ERR_INVALID_COLUMN_NUMBER
);
1153 if (!wxStrcmp(SQLState
, "S1003"))
1154 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE
);
1155 if (!wxStrcmp(SQLState
, "S1004"))
1156 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE
);
1157 if (!wxStrcmp(SQLState
, "S1008"))
1158 return(DB_ERR_OPERATION_CANCELLED
);
1159 if (!wxStrcmp(SQLState
, "S1009"))
1160 return(DB_ERR_INVALID_ARGUMENT_VALUE
);
1161 if (!wxStrcmp(SQLState
, "S1010"))
1162 return(DB_ERR_FUNCTION_SEQUENCE_ERROR
);
1163 if (!wxStrcmp(SQLState
, "S1011"))
1164 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME
);
1165 if (!wxStrcmp(SQLState
, "S1012"))
1166 return(DB_ERR_INVALID_TRANS_OPERATION_CODE
);
1167 if (!wxStrcmp(SQLState
, "S1015"))
1168 return(DB_ERR_NO_CURSOR_NAME_AVAIL
);
1169 if (!wxStrcmp(SQLState
, "S1090"))
1170 return(DB_ERR_INVALID_STR_OR_BUF_LEN
);
1171 if (!wxStrcmp(SQLState
, "S1091"))
1172 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE
);
1173 if (!wxStrcmp(SQLState
, "S1092"))
1174 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE
);
1175 if (!wxStrcmp(SQLState
, "S1093"))
1176 return(DB_ERR_INVALID_PARAM_NO
);
1177 if (!wxStrcmp(SQLState
, "S1094"))
1178 return(DB_ERR_INVALID_SCALE_VALUE
);
1179 if (!wxStrcmp(SQLState
, "S1095"))
1180 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE
);
1181 if (!wxStrcmp(SQLState
, "S1096"))
1182 return(DB_ERR_INF_TYPE_OUT_OF_RANGE
);
1183 if (!wxStrcmp(SQLState
, "S1097"))
1184 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE
);
1185 if (!wxStrcmp(SQLState
, "S1098"))
1186 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE
);
1187 if (!wxStrcmp(SQLState
, "S1099"))
1188 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE
);
1189 if (!wxStrcmp(SQLState
, "S1100"))
1190 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE
);
1191 if (!wxStrcmp(SQLState
, "S1101"))
1192 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE
);
1193 if (!wxStrcmp(SQLState
, "S1103"))
1194 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE
);
1195 if (!wxStrcmp(SQLState
, "S1104"))
1196 return(DB_ERR_INVALID_PRECISION_VALUE
);
1197 if (!wxStrcmp(SQLState
, "S1105"))
1198 return(DB_ERR_INVALID_PARAM_TYPE
);
1199 if (!wxStrcmp(SQLState
, "S1106"))
1200 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE
);
1201 if (!wxStrcmp(SQLState
, "S1107"))
1202 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE
);
1203 if (!wxStrcmp(SQLState
, "S1108"))
1204 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE
);
1205 if (!wxStrcmp(SQLState
, "S1109"))
1206 return(DB_ERR_INVALID_CURSOR_POSITION
);
1207 if (!wxStrcmp(SQLState
, "S1110"))
1208 return(DB_ERR_INVALID_DRIVER_COMPLETION
);
1209 if (!wxStrcmp(SQLState
, "S1111"))
1210 return(DB_ERR_INVALID_BOOKMARK_VALUE
);
1211 if (!wxStrcmp(SQLState
, "S1C00"))
1212 return(DB_ERR_DRIVER_NOT_CAPABLE
);
1213 if (!wxStrcmp(SQLState
, "S1T00"))
1214 return(DB_ERR_TIMEOUT_EXPIRED
);
1219 } // wxDB::TranslateSqlState()
1222 /********** wxDB::Grant() **********/
1223 bool wxDB::Grant(int privileges
, const char *tableName
, const char *userList
)
1225 // char sqlStmt[DB_MAX_STATEMENT_LEN];
1228 // Build the grant statement
1230 if (privileges
== DB_GRANT_ALL
)
1235 if (privileges
& DB_GRANT_SELECT
)
1237 sqlStmt
+= "SELECT";
1240 if (privileges
& DB_GRANT_INSERT
)
1244 sqlStmt
+= "INSERT";
1246 if (privileges
& DB_GRANT_UPDATE
)
1250 sqlStmt
+= "UPDATE";
1252 if (privileges
& DB_GRANT_DELETE
)
1256 sqlStmt
+= "DELETE";
1261 sqlStmt
+= tableName
;
1263 sqlStmt
+= userList
;
1265 #ifdef DBDEBUG_CONSOLE
1266 cout
<< endl
<< sqlStmt
.GetData() << endl
;
1269 WriteSqlLog(sqlStmt
.GetData());
1271 return(ExecSql(sqlStmt
.GetData()));
1276 /********** wxDB::CreateView() **********/
1277 bool wxDB::CreateView(const char *viewName
, const char *colList
, const char *pSqlStmt
, bool attemptDrop
)
1279 // char sqlStmt[DB_MAX_STATEMENT_LEN];
1282 // Drop the view first
1283 if (attemptDrop
&& !DropView(viewName
))
1286 // Build the create view statement
1287 sqlStmt
= "CREATE VIEW ";
1288 sqlStmt
+= viewName
;
1290 if (wxStrlen(colList
))
1298 sqlStmt
+= pSqlStmt
;
1300 WriteSqlLog(sqlStmt
.GetData());
1302 #ifdef DBDEBUG_CONSOLE
1303 cout
<< sqlStmt
.GetData() << endl
;
1306 return(ExecSql(sqlStmt
.GetData()));
1308 } // wxDB::CreateView()
1311 /********** wxDB::DropView() **********/
1312 bool wxDB::DropView(const char *viewName
)
1315 * NOTE: This function returns TRUE if the View does not exist, but
1316 * only for identified databases. Code will need to be added
1317 * below for any other databases when those databases are defined
1318 * to handle this situation consistently
1320 // char sqlStmt[DB_MAX_STATEMENT_LEN];
1323 sqlStmt
.sprintf("DROP VIEW %s", viewName
);
1325 WriteSqlLog(sqlStmt
.GetData());
1327 #ifdef DBDEBUG_CONSOLE
1328 cout
<< endl
<< sqlStmt
.GetData() << endl
;
1331 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
.GetData(), SQL_NTS
) != SQL_SUCCESS
)
1333 // Check for "Base table not found" error and ignore
1334 GetNextError(henv
, hdbc
, hstmt
);
1335 if (wxStrcmp(sqlState
,"S0002")) // "Base table not found"
1337 // Check for product specific error codes
1338 if (!((Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(sqlState
,"42000")))) // 5.x (and lower?)
1341 DispAllErrors(henv
, hdbc
, hstmt
);
1348 // Commit the transaction
1349 if (! CommitTrans())
1354 } // wxDB::DropView()
1357 /********** wxDB::ExecSql() **********/
1358 bool wxDB::ExecSql(const char *pSqlStmt
)
1360 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1361 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) pSqlStmt
, SQL_NTS
) == SQL_SUCCESS
)
1365 DispAllErrors(henv
, hdbc
, hstmt
);
1369 } // wxDB::ExecSql()
1372 /********** wxDB::GetNext() **********/
1373 bool wxDB::GetNext(void)
1375 if (SQLFetch(hstmt
) == SQL_SUCCESS
)
1379 DispAllErrors(henv
, hdbc
, hstmt
);
1383 } // wxDB::GetNext()
1386 /********** wxDB::GetData() **********/
1387 bool wxDB::GetData(UWORD colNo
, SWORD cType
, PTR pData
, SDWORD maxLen
, SDWORD FAR
*cbReturned
)
1392 if (SQLGetData(hstmt
, colNo
, cType
, pData
, maxLen
, cbReturned
) == SQL_SUCCESS
)
1396 DispAllErrors(henv
, hdbc
, hstmt
);
1400 } // wxDB::GetData()
1403 /********** wxDB::GetKeyFields() **********/
1404 int wxDB::GetKeyFields(char *tableName
, wxColInf
* colInf
,int noCols
)
1406 char szPkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Primary key table name */
1407 char szFkTable
[DB_MAX_TABLE_NAME_LEN
+1]; /* Foreign key table name */
1409 // SQLSMALLINT iKeySeq;
1410 char szPkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Primary key column */
1411 char szFkCol
[DB_MAX_COLUMN_NAME_LEN
+1]; /* Foreign key column */
1417 * ---------------------------------------------------------------------
1418 * -- 19991224 : mj10777@gmx.net : Create ------
1419 * -- : Three things are done and stored here : ------
1420 * -- : 1) which Column(s) is/are Primary Key(s) ------
1421 * -- : 2) which tables use this Key as a Foreign Key ------
1422 * -- : 3) which columns are Foreign Key and the name ------
1423 * -- : of the Table where the Key is the Primary Key -----
1424 * -- : Called from GetColumns(char *tableName, ------
1425 * -- int *numCols,const char *userID ) ------
1426 * ---------------------------------------------------------------------
1429 /*---------------------------------------------------------------------*/
1430 /* Get the names of the columns in the primary key. */
1431 /*---------------------------------------------------------------------*/
1432 retcode
= SQLPrimaryKeys(hstmt
,
1433 NULL
, 0, /* Catalog name */
1434 NULL
, 0, /* Schema name */
1435 (UCHAR
*) tableName
, SQL_NTS
); /* Table name */
1437 /*---------------------------------------------------------------------*/
1438 /* Fetch and display the result set. This will be a list of the */
1439 /* columns in the primary key of the tableName table. */
1440 /*---------------------------------------------------------------------*/
1441 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
1443 retcode
= SQLFetch(hstmt
);
1444 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
1446 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1447 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
1449 for (i
=0;i
<noCols
;i
++) // Find the Column name
1450 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column
1451 colInf
[i
].PkCol
= iKeySeq
; // Which Primary Key is this (first, second usw.) ?
1452 } // if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
1453 } // while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
1454 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
1456 /*---------------------------------------------------------------------*/
1457 /* Get all the foreign keys that refer to tableName primary key. */
1458 /*---------------------------------------------------------------------*/
1459 retcode
= SQLForeignKeys(hstmt
,
1460 NULL
, 0, /* Primary catalog */
1461 NULL
, 0, /* Primary schema */
1462 (UCHAR
*)tableName
, SQL_NTS
, /* Primary table */
1463 NULL
, 0, /* Foreign catalog */
1464 NULL
, 0, /* Foreign schema */
1465 NULL
, 0); /* Foreign table */
1467 /*---------------------------------------------------------------------*/
1468 /* Fetch and display the result set. This will be all of the foreign */
1469 /* keys in other tables that refer to the tableName primary key. */
1470 /*---------------------------------------------------------------------*/
1473 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
1475 retcode
= SQLFetch(hstmt
);
1476 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
1478 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
1479 GetData( 4, SQL_C_CHAR
, szPkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1480 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
1481 GetData( 7, SQL_C_CHAR
, szFkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
1482 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1483 Temp0
.Printf("%s[%s] ",Temp0
.c_str(),szFkTable
); // [ ] in case there is a blank in the Table name
1484 } // if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
1485 } // while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
1486 Temp0
.Trim(); // Get rid of any unneeded blanks
1489 for (i
=0;i
<noCols
;i
++) // Find the Column name
1490 if (!wxStrcmp(colInf
[i
].colName
,szPkCol
)) // We have found the Column, store the Information
1491 strcpy(colInf
[i
].PkTableName
,Temp0
); // Name of the Tables where this Primary Key is used as a Foreign Key
1492 } // if (Temp0 != "")
1493 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
1495 /*---------------------------------------------------------------------*/
1496 /* Get all the foreign keys in the tablename table. */
1497 /*---------------------------------------------------------------------*/
1498 retcode
= SQLForeignKeys(hstmt
,
1499 NULL
, 0, /* Primary catalog */
1500 NULL
, 0, /* Primary schema */
1501 NULL
, 0, /* Primary table */
1502 NULL
, 0, /* Foreign catalog */
1503 NULL
, 0, /* Foreign schema */
1504 (UCHAR
*)tableName
, SQL_NTS
); /* Foreign table */
1506 /*---------------------------------------------------------------------*/
1507 /* Fetch and display the result set. This will be all of the */
1508 /* primary keys in other tables that are referred to by foreign */
1509 /* keys in the tableName table. */
1510 /*---------------------------------------------------------------------*/
1512 while ((retcode
== SQL_SUCCESS
) || (retcode
== SQL_SUCCESS_WITH_INFO
))
1514 retcode
= SQLFetch(hstmt
);
1515 if (retcode
== SQL_SUCCESS
|| retcode
== SQL_SUCCESS_WITH_INFO
)
1517 GetData( 3, SQL_C_CHAR
, szPkTable
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
1518 GetData( 5, SQL_C_SSHORT
, &iKeySeq
, 0, &cb
);
1519 GetData( 8, SQL_C_CHAR
, szFkCol
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1521 for (i
=0;i
<noCols
;i
++) // Find the Column name
1523 if (!wxStrcmp(colInf
[i
].colName
,szFkCol
)) // We have found the (Foreign Key) Column
1525 colInf
[i
].FkCol
= iKeySeq
; // Which Foreign Key is this (first, second usw.) ?
1526 strcpy(colInf
[i
].FkTableName
,szPkTable
); // Name of the Table where this Foriegn is the Primary Key
1527 } // if (!wxStrcmp(colInf[i].colName,szFkCol))
1528 } // for (i=0;i<noCols;i++)
1529 } // if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
1530 } // while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
1531 SQLFreeStmt(hstmt
, SQL_CLOSE
); /* Close the cursor (the hstmt is still allocated). */
1533 /*---------------------------------------------------------------------*/
1535 } // wxDB::GetKeyFields()
1538 /********** wxDB::GetColumns() **********/
1539 wxColInf
*wxDB::GetColumns(char *tableName
[], const char *userID
)
1541 * 1) The last array element of the tableName[] argument must be zero (null).
1542 * This is how the end of the array is detected.
1543 * 2) This function returns an array of wxColInf structures. If no columns
1544 * were found, or an error occured, this pointer will be zero (null). THE
1545 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
1546 * IS FINISHED WITH IT. i.e.
1548 * wxColInf *colInf = pDb->GetColumns(tableList, userID);
1551 * // Use the column inf
1553 * // Destroy the memory
1557 * userID is evaluated in the following manner:
1558 * userID == NULL ... UserID is ignored
1559 * userID == "" ... UserID set equal to 'this->uid'
1560 * userID != "" ... UserID set equal to 'userID'
1562 * NOTE: ALL column bindings associated with this wxDB instance are unbound
1563 * by this function. This function should use its own wxDB instance
1564 * to avoid undesired unbinding of columns.
1569 wxColInf
*colInf
= 0;
1579 if (!wxStrlen(userID
))
1587 // dBase does not use user names, and some drivers fail if you try to pass one
1588 if (Dbms() == dbmsDBASE
)
1591 // Oracle user names may only be in uppercase, so force
1592 // the name to uppercase
1593 if (Dbms() == dbmsORACLE
)
1594 UserID
= UserID
.Upper();
1596 // Pass 1 - Determine how many columns there are.
1597 // Pass 2 - Allocate the wxColInf array and fill in
1598 // the array with the column information.
1600 for (pass
= 1; pass
<= 2; pass
++)
1604 if (noCols
== 0) // Probably a bogus table name(s)
1606 // Allocate n wxColInf objects to hold the column information
1607 colInf
= new wxColInf
[noCols
+1];
1610 // Mark the end of the array
1611 wxStrcpy(colInf
[noCols
].tableName
, "");
1612 wxStrcpy(colInf
[noCols
].colName
, "");
1613 colInf
[noCols
].sqlDataType
= 0;
1615 // Loop through each table name
1617 for (tbl
= 0; tableName
[tbl
]; tbl
++)
1619 TableName
= tableName
[tbl
];
1620 // Oracle table names are uppercase only, so force
1621 // the name to uppercase just in case programmer forgot to do this
1622 if (Dbms() == dbmsORACLE
)
1623 TableName
= TableName
.Upper();
1625 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1627 // MySQL and Access cannot accept a user name when looking up column names, so we
1628 // use the call below that leaves out the user name
1629 if (wxStrcmp(UserID
.GetData(),"") &&
1630 Dbms() != dbmsMY_SQL
&&
1631 Dbms() != dbmsACCESS
)
1633 retcode
= SQLColumns(hstmt
,
1634 NULL
, 0, // All qualifiers
1635 (UCHAR
*) UserID
.GetData(), SQL_NTS
, // Owner
1636 (UCHAR
*) TableName
.GetData(), SQL_NTS
,
1637 NULL
, 0); // All columns
1641 retcode
= SQLColumns(hstmt
,
1642 NULL
, 0, // All qualifiers
1644 (UCHAR
*) TableName
.GetData(), SQL_NTS
,
1645 NULL
, 0); // All columns
1647 if (retcode
!= SQL_SUCCESS
)
1648 { // Error occured, abort
1649 DispAllErrors(henv
, hdbc
, hstmt
);
1652 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1656 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
1658 if (pass
== 1) // First pass, just add up the number of columns
1660 else // Pass 2; Fill in the array of structures
1662 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
1664 // NOTE: Only the ODBC 1.x fields are retrieved
1665 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
1666 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
1667 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
1668 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1669 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
1670 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
1671 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
1672 GetData( 8, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
1673 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
1674 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
1675 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
1676 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
1678 // Determine the wxDB data type that is used to represent the native data type of this data source
1679 colInf
[colNo
].dbDataType
= 0;
1680 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
1681 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
1682 else if (!wxStricmp(typeInfInteger
.TypeName
,colInf
[colNo
].typeName
))
1683 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
1684 else if (!wxStricmp(typeInfFloat
.TypeName
,colInf
[colNo
].typeName
))
1685 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
1686 else if (!wxStricmp(typeInfDate
.TypeName
,colInf
[colNo
].typeName
))
1687 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
1693 if (retcode
!= SQL_NO_DATA_FOUND
)
1694 { // Error occured, abort
1695 DispAllErrors(henv
, hdbc
, hstmt
);
1698 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1704 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1707 } // wxDB::GetColumns()
1710 /********** wxDB::GetColumns() **********/
1711 wxColInf
*wxDB::GetColumns(char *tableName
, int *numCols
, const char *userID
)
1713 * Same as the above GetColumns() function except this one gets columns
1714 * only for a single table, and if 'numCols' is not NULL, the number of
1715 * columns stored in the returned wxColInf is set in '*numCols'
1717 * userID is evaluated in the following manner:
1718 * userID == NULL ... UserID is ignored
1719 * userID == "" ... UserID set equal to 'this->uid'
1720 * userID != "" ... UserID set equal to 'userID'
1722 * NOTE: ALL column bindings associated with this wxDB instance are unbound
1723 * by this function. This function should use its own wxDB instance
1724 * to avoid undesired unbinding of columns.
1729 wxColInf
*colInf
= 0;
1739 if (!wxStrlen(userID
))
1747 // dBase does not use user names, and some drivers fail if you try to pass one
1748 if (Dbms() == dbmsDBASE
)
1751 // Oracle user names may only be in uppercase, so force
1752 // the name to uppercase
1753 if (Dbms() == dbmsORACLE
)
1754 UserID
= UserID
.Upper();
1756 // Pass 1 - Determine how many columns there are.
1757 // Pass 2 - Allocate the wxColInf array and fill in
1758 // the array with the column information.
1760 for (pass
= 1; pass
<= 2; pass
++)
1764 if (noCols
== 0) // Probably a bogus table name(s)
1766 // Allocate n wxColInf objects to hold the column information
1767 colInf
= new wxColInf
[noCols
+1];
1770 // Mark the end of the array
1771 wxStrcpy(colInf
[noCols
].tableName
, "");
1772 wxStrcpy(colInf
[noCols
].colName
, "");
1773 colInf
[noCols
].sqlDataType
= 0;
1776 TableName
= tableName
;
1777 // Oracle table names are uppercase only, so force
1778 // the name to uppercase just in case programmer forgot to do this
1779 if (Dbms() == dbmsORACLE
)
1780 TableName
= TableName
.Upper();
1782 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1784 // MySQL and Access cannot accept a user name when looking up column names, so we
1785 // use the call below that leaves out the user name
1786 if (wxStrcmp(UserID
.GetData(),"") &&
1787 Dbms() != dbmsMY_SQL
&&
1788 Dbms() != dbmsACCESS
)
1790 retcode
= SQLColumns(hstmt
,
1791 NULL
, 0, // All qualifiers
1792 (UCHAR
*) UserID
.GetData(), SQL_NTS
, // Owner
1793 (UCHAR
*) TableName
.GetData(), SQL_NTS
,
1794 NULL
, 0); // All columns
1798 retcode
= SQLColumns(hstmt
,
1799 NULL
, 0, // All qualifiers
1801 (UCHAR
*) TableName
.GetData(), SQL_NTS
,
1802 NULL
, 0); // All columns
1804 if (retcode
!= SQL_SUCCESS
)
1805 { // Error occured, abort
1806 DispAllErrors(henv
, hdbc
, hstmt
);
1809 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1815 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
1817 if (pass
== 1) // First pass, just add up the number of columns
1819 else // Pass 2; Fill in the array of structures
1821 if (colNo
< noCols
) // Some extra error checking to prevent memory overwrites
1823 // NOTE: Only the ODBC 1.x fields are retrieved
1824 GetData( 1, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].catalog
, 128+1, &cb
);
1825 GetData( 2, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].schema
, 128+1, &cb
);
1826 GetData( 3, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
1827 GetData( 4, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].colName
, DB_MAX_COLUMN_NAME_LEN
+1, &cb
);
1828 GetData( 5, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].sqlDataType
, 0, &cb
);
1829 GetData( 6, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].typeName
, 128+1, &cb
);
1830 GetData( 7, SQL_C_SLONG
, (UCHAR
*) &colInf
[colNo
].columnSize
, 0, &cb
);
1831 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
1832 GetData( 8, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].bufferLength
, 0, &cb
);
1833 GetData( 9, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].decimalDigits
,0, &cb
);
1834 GetData(10, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].numPrecRadix
, 0, &cb
);
1835 GetData(11, SQL_C_SSHORT
, (UCHAR
*) &colInf
[colNo
].nullable
, 0, &cb
);
1836 GetData(12, SQL_C_CHAR
, (UCHAR
*) colInf
[colNo
].remarks
, 254+1, &cb
);
1837 // Start Values for Primary/Foriegn Key (=No)
1838 colInf
[colNo
].PkCol
= 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
1839 colInf
[colNo
].PkTableName
[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
1840 colInf
[colNo
].FkCol
= 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
1841 colInf
[colNo
].FkTableName
[0] = 0; // Foreign key table name
1843 // Determine the wxDB data type that is used to represent the native data type of this data source
1844 colInf
[colNo
].dbDataType
= 0;
1845 if (!wxStricmp(typeInfVarchar
.TypeName
,colInf
[colNo
].typeName
))
1846 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_VARCHAR
;
1847 else if (!wxStricmp(typeInfInteger
.TypeName
,colInf
[colNo
].typeName
))
1848 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_INTEGER
;
1849 else if (!wxStricmp(typeInfFloat
.TypeName
,colInf
[colNo
].typeName
))
1850 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_FLOAT
;
1851 else if (!wxStricmp(typeInfDate
.TypeName
,colInf
[colNo
].typeName
))
1852 colInf
[colNo
].dbDataType
= DB_DATA_TYPE_DATE
;
1858 if (retcode
!= SQL_NO_DATA_FOUND
)
1859 { // Error occured, abort
1860 DispAllErrors(henv
, hdbc
, hstmt
);
1863 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1870 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1872 // Store Primary and Foriegn Keys
1873 GetKeyFields(tableName
,colInf
,noCols
);
1879 } // wxDB::GetColumns()
1882 /********** wxDB::GetColumnCount() **********/
1883 int wxDB::GetColumnCount(char *tableName
, const char *userID
)
1885 * Returns a count of how many columns are in a table.
1886 * If an error occurs in computing the number of columns
1887 * this function will return a -1 for the count
1889 * userID is evaluated in the following manner:
1890 * userID == NULL ... UserID is ignored
1891 * userID == "" ... UserID set equal to 'this->uid'
1892 * userID != "" ... UserID set equal to 'userID'
1894 * NOTE: ALL column bindings associated with this wxDB instance are unbound
1895 * by this function. This function should use its own wxDB instance
1896 * to avoid undesired unbinding of columns.
1908 if (!wxStrlen(userID
))
1916 // dBase does not use user names, and some drivers fail if you try to pass one
1917 if (Dbms() == dbmsDBASE
)
1920 // Oracle user names may only be in uppercase, so force
1921 // the name to uppercase
1922 if (Dbms() == dbmsORACLE
)
1923 UserID
= UserID
.Upper();
1926 // Loop through each table name
1928 TableName
= tableName
;
1929 // Oracle table names are uppercase only, so force
1930 // the name to uppercase just in case programmer forgot to do this
1931 if (Dbms() == dbmsORACLE
)
1932 TableName
= TableName
.Upper();
1934 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1936 // MySQL and Access cannot accept a user name when looking up column names, so we
1937 // use the call below that leaves out the user name
1938 if (wxStrcmp(UserID
.GetData(),"") &&
1939 Dbms() != dbmsMY_SQL
&&
1940 Dbms() != dbmsACCESS
)
1942 retcode
= SQLColumns(hstmt
,
1943 NULL
, 0, // All qualifiers
1944 (UCHAR
*) UserID
.GetData(), SQL_NTS
, // Owner
1945 (UCHAR
*) TableName
.GetData(), SQL_NTS
,
1946 NULL
, 0); // All columns
1950 retcode
= SQLColumns(hstmt
,
1951 NULL
, 0, // All qualifiers
1953 (UCHAR
*) TableName
.GetData(), SQL_NTS
,
1954 NULL
, 0); // All columns
1956 if (retcode
!= SQL_SUCCESS
)
1957 { // Error occured, abort
1958 DispAllErrors(henv
, hdbc
, hstmt
);
1959 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1963 // Count the columns
1964 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
1967 if (retcode
!= SQL_NO_DATA_FOUND
)
1968 { // Error occured, abort
1969 DispAllErrors(henv
, hdbc
, hstmt
);
1970 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1976 SQLFreeStmt(hstmt
, SQL_CLOSE
);
1979 } // wxDB::GetColumnCount()
1982 /********** wxDB::GetCatalog() *******/
1983 wxDbInf
*wxDB::GetCatalog(char *userID
)
1985 * ---------------------------------------------------------------------
1986 * -- 19991203 : mj10777@gmx.net : Create ------
1987 * -- : Creates a wxDbInf with Tables / Cols Array ------
1988 * -- : uses SQLTables and fills pTableInf; ------
1989 * -- : pColInf is set to NULL and numCols to 0; ------
1990 * -- : returns pDbInf (wxDbInf) ------
1991 * -- - if unsuccesfull (pDbInf == NULL) ------
1992 * -- : pColInf can be filled with GetColumns(..); ------
1993 * -- : numCols can be filled with GetColumnCount(..); ------
1994 * ---------------------------------------------------------------------
1996 * userID is evaluated in the following manner:
1997 * userID == NULL ... UserID is ignored
1998 * userID == "" ... UserID set equal to 'this->uid'
1999 * userID != "" ... UserID set equal to 'userID'
2001 * NOTE: ALL column bindings associated with this wxDB instance are unbound
2002 * by this function. This function should use its own wxDB instance
2003 * to avoid undesired unbinding of columns.
2006 wxDbInf
*pDbInf
= NULL
; // Array of catalog entries
2007 int noTab
= 0; // Counter while filling table entries
2011 // char tblNameSave[DB_MAX_TABLE_NAME_LEN+1];
2012 wxString tblNameSave
;
2018 if (!wxStrlen(userID
))
2026 // dBase does not use user names, and some drivers fail if you try to pass one
2027 if (Dbms() == dbmsDBASE
)
2030 // Oracle user names may only be in uppercase, so force
2031 // the name to uppercase
2032 if (Dbms() == dbmsORACLE
)
2033 UserID
= UserID
.Upper();
2035 //-------------------------------------------------------------
2036 pDbInf
= new wxDbInf
; // Create the Database Arrray
2037 pDbInf
->catalog
[0] = 0;
2038 pDbInf
->schema
[0] = 0;
2039 pDbInf
->numTables
= 0; // Counter for Tables
2040 pDbInf
->pTableInf
= NULL
; // Array of Tables
2041 //-------------------------------------------------------------
2042 // Table Information
2043 // Pass 1 - Determine how many Tables there are.
2044 // Pass 2 - Create the Table array and fill it
2045 // - Create the Cols array = NULL
2046 //-------------------------------------------------------------
2047 for (pass
= 1; pass
<= 2; pass
++)
2049 SQLFreeStmt(hstmt
, SQL_CLOSE
); // Close if Open
2052 if (wxStrcmp(UserID
.GetData(),"") &&
2053 Dbms() != dbmsMY_SQL
&&
2054 Dbms() != dbmsACCESS
)
2056 retcode
= SQLTables(hstmt
,
2057 NULL
, 0, // All qualifiers
2058 (UCHAR
*) UserID
.GetData(), SQL_NTS
, // User specified
2059 NULL
, 0, // All tables
2060 NULL
, 0); // All columns
2064 retcode
= SQLTables(hstmt
,
2065 NULL
, 0, // All qualifiers
2066 NULL
, 0, // User specified
2067 NULL
, 0, // All tables
2068 NULL
, 0); // All columns
2070 if (retcode
!= SQL_SUCCESS
)
2072 DispAllErrors(henv
, hdbc
, hstmt
);
2074 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2078 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
) // Table Information
2080 if (pass
== 1) // First pass, just count the Tables
2082 if (pDbInf
->numTables
== 0)
2084 GetData( 1, SQL_C_CHAR
, (UCHAR
*) pDbInf
->catalog
, 128+1, &cb
);
2085 GetData( 2, SQL_C_CHAR
, (UCHAR
*) pDbInf
->schema
, 128+1, &cb
);
2087 pDbInf
->numTables
++; // Counter for Tables
2089 if (pass
== 2) // Create and fill the Table entries
2091 if (pDbInf
->pTableInf
== NULL
) // Has the Table Array been created
2092 { // no, then create the Array
2093 pDbInf
->pTableInf
= new wxTableInf
[pDbInf
->numTables
];
2094 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
2096 (pDbInf
->pTableInf
+noTab
)->tableName
[0] = 0;
2097 (pDbInf
->pTableInf
+noTab
)->tableType
[0] = 0;
2098 (pDbInf
->pTableInf
+noTab
)->tableRemarks
[0] = 0;
2099 (pDbInf
->pTableInf
+noTab
)->numCols
= 0;
2100 (pDbInf
->pTableInf
+noTab
)->pColInf
= NULL
;
2103 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
2104 GetData( 3, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2105 GetData( 4, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableType
, 30+1, &cb
);
2106 GetData( 5, SQL_C_CHAR
, (UCHAR
*) (pDbInf
->pTableInf
+noTab
)->tableRemarks
, 254+1, &cb
);
2108 } // if (pass == 2) We now know the amount of Tables
2109 } // while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
2110 } // for (pass = 1; pass <= 2; pass++)
2111 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2113 // Query how many columns are in each table
2114 for (noTab
=0;noTab
<pDbInf
->numTables
;noTab
++)
2116 (pDbInf
->pTableInf
+noTab
)->numCols
= GetColumnCount((pDbInf
->pTableInf
+noTab
)->tableName
,UserID
);
2119 } // wxDB::GetCatalog()
2122 /********** wxDB::Catalog() **********/
2123 bool wxDB::Catalog(const char *userID
, const char *fileName
)
2125 * Creates the text file specified in 'filename' which will contain
2126 * a minimal data dictionary of all tables accessible by the user specified
2129 * userID is evaluated in the following manner:
2130 * userID == NULL ... UserID is ignored
2131 * userID == "" ... UserID set equal to 'this->uid'
2132 * userID != "" ... UserID set equal to 'userID'
2134 * NOTE: ALL column bindings associated with this wxDB instance are unbound
2135 * by this function. This function should use its own wxDB instance
2136 * to avoid undesired unbinding of columns.
2139 assert(fileName
&& wxStrlen(fileName
));
2143 char tblName
[DB_MAX_TABLE_NAME_LEN
+1];
2144 wxString tblNameSave
;
2145 char colName
[DB_MAX_COLUMN_NAME_LEN
+1];
2147 char typeName
[30+1];
2148 SWORD precision
, length
;
2152 FILE *fp
= fopen(fileName
,"wt");
2156 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2160 if (!wxStrlen(userID
))
2168 // dBase does not use user names, and some drivers fail if you try to pass one
2169 if (Dbms() == dbmsDBASE
)
2172 // Oracle user names may only be in uppercase, so force
2173 // the name to uppercase
2174 if (Dbms() == dbmsORACLE
)
2175 UserID
= UserID
.Upper();
2177 if (wxStrcmp(UserID
.GetData(),"") &&
2178 Dbms() != dbmsMY_SQL
&&
2179 Dbms() != dbmsACCESS
)
2181 retcode
= SQLColumns(hstmt
,
2182 NULL
, 0, // All qualifiers
2183 (UCHAR
*) UserID
.GetData(), SQL_NTS
, // User specified
2184 NULL
, 0, // All tables
2185 NULL
, 0); // All columns
2189 retcode
= SQLColumns(hstmt
,
2190 NULL
, 0, // All qualifiers
2191 NULL
, 0, // User specified
2192 NULL
, 0, // All tables
2193 NULL
, 0); // All columns
2195 if (retcode
!= SQL_SUCCESS
)
2197 DispAllErrors(henv
, hdbc
, hstmt
);
2206 while ((retcode
= SQLFetch(hstmt
)) == SQL_SUCCESS
)
2208 if (wxStrcmp(tblName
,tblNameSave
.GetData()))
2212 fputs("================================ ", fp
);
2213 fputs("================================ ", fp
);
2214 fputs("===================== ", fp
);
2215 fputs("========= ", fp
);
2216 fputs("=========\n", fp
);
2217 outStr
.sprintf("%-32s %-32s %-21s %9s %9s\n",
2218 "TABLE NAME", "COLUMN NAME", "DATA TYPE", "PRECISION", "LENGTH");
2219 fputs(outStr
.GetData(), fp
);
2220 fputs("================================ ", fp
);
2221 fputs("================================ ", fp
);
2222 fputs("===================== ", fp
);
2223 fputs("========= ", fp
);
2224 fputs("=========\n", fp
);
2225 tblNameSave
= tblName
;
2228 GetData(3,SQL_C_CHAR
, (UCHAR
*)tblName
, DB_MAX_TABLE_NAME_LEN
+1, &cb
);
2229 GetData(4,SQL_C_CHAR
, (UCHAR
*)colName
, DB_MAX_COLUMN_NAME_LEN
+1,&cb
);
2230 GetData(5,SQL_C_SSHORT
,(UCHAR
*)&sqlDataType
,0, &cb
);
2231 GetData(6,SQL_C_CHAR
, (UCHAR
*)typeName
, sizeof(typeName
), &cb
);
2232 GetData(7,SQL_C_SSHORT
,(UCHAR
*)&precision
, 0, &cb
);
2233 GetData(8,SQL_C_SSHORT
,(UCHAR
*)&length
, 0, &cb
);
2235 outStr
.sprintf("%-32s %-32s (%04d)%-15s %9d %9d\n",
2236 tblName
, colName
, sqlDataType
, typeName
, precision
, length
);
2237 if (fputs(outStr
.GetData(), fp
) == EOF
)
2239 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2246 if (retcode
!= SQL_NO_DATA_FOUND
)
2247 DispAllErrors(henv
, hdbc
, hstmt
);
2249 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2252 return(retcode
== SQL_NO_DATA_FOUND
);
2254 } // wxDB::Catalog()
2257 bool wxDB::TableExists(const char *tableName
, const char *userID
, const char *tablePath
)
2259 * Table name can refer to a table, view, alias or synonym. Returns true
2260 * if the object exists in the database. This function does not indicate
2261 * whether or not the user has privleges to query or perform other functions
2264 * userID is evaluated in the following manner:
2265 * userID == NULL ... UserID is ignored
2266 * userID == "" ... UserID set equal to 'this->uid'
2267 * userID != "" ... UserID set equal to 'userID'
2273 assert(tableName
&& wxStrlen(tableName
));
2275 if (Dbms() == dbmsDBASE
)
2278 if (tablePath
&& wxStrlen(tablePath
))
2279 dbName
.sprintf("%s/%s.dbf",tablePath
,tableName
);
2281 dbName
.sprintf("%s.dbf",tableName
);
2284 exists
= wxFileExists(dbName
.GetData());
2290 if (!wxStrlen(userID
))
2298 // Oracle user names may only be in uppercase, so force
2299 // the name to uppercase
2300 if (Dbms() == dbmsORACLE
)
2301 UserID
= UserID
.Upper();
2303 TableName
= tableName
;
2304 // Oracle table names are uppercase only, so force
2305 // the name to uppercase just in case programmer forgot to do this
2306 if (Dbms() == dbmsORACLE
)
2307 TableName
= TableName
.Upper();
2309 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2312 // MySQL and Access cannot accept a user name when looking up table names, so we
2313 // use the call below that leaves out the user name
2314 if (wxStrcmp(UserID
,"") &&
2315 Dbms() != dbmsMY_SQL
&&
2316 Dbms() != dbmsACCESS
)
2318 retcode
= SQLTables(hstmt
,
2319 NULL
, 0, // All qualifiers
2320 (UCHAR
*) UserID
.GetData(), SQL_NTS
, // All owners
2321 (UCHAR FAR
*)TableName
.GetData(), SQL_NTS
,
2322 NULL
, 0); // All table types
2326 retcode
= SQLTables(hstmt
,
2327 NULL
, 0, // All qualifiers
2328 NULL
, 0, // All owners
2329 (UCHAR FAR
*)TableName
.GetData(), SQL_NTS
,
2330 NULL
, 0); // All table types
2332 if (retcode
!= SQL_SUCCESS
)
2333 return(DispAllErrors(henv
, hdbc
, hstmt
));
2335 retcode
= SQLFetch(hstmt
);
2336 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
2338 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2339 return(DispAllErrors(henv
, hdbc
, hstmt
));
2342 SQLFreeStmt(hstmt
, SQL_CLOSE
);
2345 } // wxDB::TableExists()
2348 /********** wxDB::SqlLog() **********/
2349 bool wxDB::SqlLog(enum sqlLog state
, const char *filename
, bool append
)
2351 assert(state
== sqlLogON
|| state
== sqlLogOFF
);
2352 assert(state
== sqlLogOFF
|| filename
);
2354 if (state
== sqlLogON
)
2358 fpSqlLog
= fopen(filename
, (append
? "at" : "wt"));
2359 if (fpSqlLog
== NULL
)
2367 if (fclose(fpSqlLog
))
2373 sqlLogState
= state
;
2379 /********** wxDB::WriteSqlLog() **********/
2380 bool wxDB::WriteSqlLog(const char *logMsg
)
2384 if (fpSqlLog
== 0 || sqlLogState
== sqlLogOFF
)
2387 if (fputs("\n", fpSqlLog
) == EOF
) return(FALSE
);
2388 if (fputs(logMsg
, fpSqlLog
) == EOF
) return(FALSE
);
2389 if (fputs("\n", fpSqlLog
) == EOF
) return(FALSE
);
2393 } // wxDB::WriteSqlLog()
2396 /********** wxDB::Dbms() **********/
2397 DBMS
wxDB::Dbms(void)
2399 * Be aware that not all database engines use the exact same syntax, and not
2400 * every ODBC compliant database is compliant to the same level of compliancy.
2401 * Some manufacturers support the minimum Level 1 compliancy, and others up
2402 * through Level 3. Others support subsets of features for levels above 1.
2404 * If you find an inconsistency between the wxDB class and a specific database
2405 * engine, and an identifier to this section, and special handle the database in
2406 * the area where behavior is non-conforming with the other databases.
2409 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
2410 * ---------------------------------------------------
2413 * - Currently the only database supported by the class to support VIEWS
2416 * - Does not support the SQL_TIMESTAMP structure
2417 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
2418 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
2419 * is TRUE. The user must create ALL indexes from their program.
2420 * - Table names can only be 8 characters long
2421 * - Column names can only be 10 characters long
2424 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
2425 * after every table name involved in the query/join if that tables matching record(s)
2427 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
2429 * SYBASE (Enterprise)
2430 * - If a column is part of the Primary Key, the column cannot be NULL
2433 * - If a column is part of the Primary Key, the column cannot be NULL
2434 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
2437 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
2442 wxChar baseName
[25+1];
2444 wxStrncpy(baseName
,dbInf
.dbmsName
,25);
2445 if (!wxStricmp(dbInf
.dbmsName
,"Adaptive Server Anywhere"))
2446 return(dbmsSYBASE_ASA
);
2447 if (!wxStricmp(dbInf
.dbmsName
,"SQL Server")) // Sybase Adaptive Server
2448 return(dbmsSYBASE_ASE
);
2449 if (!wxStricmp(dbInf
.dbmsName
,"Microsoft SQL Server"))
2450 return(dbmsMS_SQL_SERVER
);
2451 if (!wxStricmp(dbInf
.dbmsName
,"MySQL"))
2453 if (!wxStricmp(dbInf
.dbmsName
,"PostgreSQL")) // v6.5.0
2454 return(dbmsPOSTGRES
);
2457 if (!wxStricmp(baseName
,"Informix"))
2458 return(dbmsINFORMIX
);
2461 if (!wxStricmp(baseName
,"Oracle"))
2463 if (!wxStricmp(dbInf
.dbmsName
,"ACCESS"))
2465 if (!wxStricmp(dbInf
.dbmsName
,"MySQL"))
2469 if (!wxStricmp(baseName
,"DBASE"))
2472 return(dbmsUNIDENTIFIED
);
2476 /********** GetDbConnection() **********/
2477 wxDB WXDLLEXPORT
*GetDbConnection(DbStuff
*pDbStuff
, bool FwdOnlyCursors
)
2481 // Scan the linked list searching for an available database connection
2482 // that's already been opened but is currently not in use.
2483 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
2485 // The database connection must be for the same datasource
2486 // name and must currently not be in use.
2487 if (pList
->Free
&& (! wxStrcmp(pDbStuff
->Dsn
, pList
->Dsn
))) // Found a free connection
2489 pList
->Free
= FALSE
;
2490 return(pList
->PtrDb
);
2494 // No available connections. A new connection must be made and
2495 // appended to the end of the linked list.
2498 // Find the end of the list
2499 for (pList
= PtrBegDbList
; pList
->PtrNext
; pList
= pList
->PtrNext
);
2500 // Append a new list item
2501 pList
->PtrNext
= new DbList
;
2502 pList
->PtrNext
->PtrPrev
= pList
;
2503 pList
= pList
->PtrNext
;
2507 // Create the first node on the list
2508 pList
= PtrBegDbList
= new DbList
;
2512 // Initialize new node in the linked list
2514 pList
->Free
= FALSE
;
2515 wxStrcpy(pList
->Dsn
, pDbStuff
->Dsn
);
2516 pList
->PtrDb
= new wxDB(pDbStuff
->Henv
,FwdOnlyCursors
);
2518 // Connect to the datasource
2519 if (pList
->PtrDb
->Open(pDbStuff
->Dsn
, pDbStuff
->Uid
, pDbStuff
->AuthStr
))
2521 pList
->PtrDb
->SqlLog(SQLLOGstate
,SQLLOGfn
,TRUE
);
2522 return(pList
->PtrDb
);
2524 else // Unable to connect, destroy list item
2527 pList
->PtrPrev
->PtrNext
= 0;
2529 PtrBegDbList
= 0; // Empty list again
2530 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDB object
2531 pList
->PtrDb
->Close(); // Close the wxDB object
2532 delete pList
->PtrDb
; // Deletes the wxDB object
2533 delete pList
; // Deletes the linked list object
2537 } // GetDbConnection()
2540 /********** FreeDbConnection() **********/
2541 bool WXDLLEXPORT
FreeDbConnection(wxDB
*pDb
)
2545 // Scan the linked list searching for the database connection
2546 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
2548 if (pList
->PtrDb
== pDb
) // Found it!!!
2549 return(pList
->Free
= TRUE
);
2552 // Never found the database object, return failure
2555 } // FreeDbConnection()
2558 /********** CloseDbConnections() **********/
2559 void WXDLLEXPORT
CloseDbConnections(void)
2561 DbList
*pList
, *pNext
;
2563 // Traverse the linked list closing database connections and freeing memory as I go.
2564 for (pList
= PtrBegDbList
; pList
; pList
= pNext
)
2566 pNext
= pList
->PtrNext
; // Save the pointer to next
2567 pList
->PtrDb
->CommitTrans(); // Commit any open transactions on wxDB object
2568 pList
->PtrDb
->Close(); // Close the wxDB object
2569 delete pList
->PtrDb
; // Deletes the wxDB object
2570 delete pList
; // Deletes the linked list object
2573 // Mark the list as empty
2576 } // CloseDbConnections()
2579 /********** NumberDbConnectionsInUse() **********/
2580 int WXDLLEXPORT
NumberDbConnectionsInUse(void)
2585 // Scan the linked list counting db connections that are currently in use
2586 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
2588 if (pList
->Free
== FALSE
)
2594 } // NumberDbConnectionsInUse()
2597 /********** SqlLog() **********/
2598 bool SqlLog(enum sqlLog state
, char *filename
)
2600 bool append
= FALSE
;
2603 for (pList
= PtrBegDbList
; pList
; pList
= pList
->PtrNext
)
2605 if (!pList
->PtrDb
->SqlLog(state
,filename
,append
))
2610 SQLLOGstate
= state
;
2611 wxStrcpy(SQLLOGfn
,filename
);
2618 /********** GetDataSource() **********/
2619 bool GetDataSource(HENV henv
, char *Dsn
, SWORD DsnMax
, char *DsDesc
, SWORD DsDescMax
,
2622 * Dsn and DsDesc will contain the data source name and data source
2623 * description upon return
2628 if (SQLDataSources(henv
, direction
, (UCHAR FAR
*) Dsn
, DsnMax
, &cb1
,
2629 (UCHAR FAR
*) DsDesc
, DsDescMax
, &cb2
) == SQL_SUCCESS
)
2634 } // GetDataSource()