]> git.saurik.com Git - wxWidgets.git/blob - src/common/db.cpp
Added another hack to the collection of hacks in
[wxWidgets.git] / src / common / db.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: db.cpp
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.
6 // Author: Doug Card
7 // Modified by: George Tasker
8 // Mods: Dec, 1998:
9 // -Added support for SQL statement logging and database cataloging
10 // Mods: April, 1999
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
16 // Created: 9.96
17 // RCS-ID: $Id$
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 ///////////////////////////////////////////////////////////////////////////////
30
31 /*
32 // SYNOPSIS START
33 // SYNOPSIS STOP
34 */
35
36 #include "wx/wxprec.h"
37
38
39 // Use this line for wxWindows v1.x
40 //#include "wx_ver.h"
41 // Use this line for wxWindows v2.x
42 #include "wx/version.h"
43
44 #if wxMAJOR_VERSION == 2
45 #ifdef __GNUG__
46 #pragma implementation "db.h"
47 #endif
48 #endif
49
50 #ifdef DBDEBUG_CONSOLE
51 #include "wx/ioswrap.h"
52 #endif
53
54 #ifdef __BORLANDC__
55 #pragma hdrstop
56 #endif //__BORLANDC__
57
58 #if wxMAJOR_VERSION == 2
59 #ifndef WX_PRECOMP
60 #include "wx/string.h"
61 #include "wx/object.h"
62 #include "wx/list.h"
63 #include "wx/utils.h"
64 #include "wx/msgdlg.h"
65 #include "wx/log.h"
66 #endif
67 #include "wx/filefn.h"
68 #include "wx/wxchar.h"
69 #endif
70
71
72 #if wxMAJOR_VERSION == 1
73 # if defined(wx_msw) || defined(wx_x)
74 # ifdef WX_PRECOMP
75 # include "wx_prec.h"
76 # else
77 # include "wx.h"
78 # endif
79 # endif
80 # define wxUSE_ODBC 1
81 #endif
82
83 #if wxUSE_ODBC
84
85 #include <stdio.h>
86 #include <string.h>
87 #include <assert.h>
88 #include <stdlib.h>
89 #include <ctype.h>
90
91 #if wxMAJOR_VERSION == 1
92 #include "db.h"
93 #elif wxMAJOR_VERSION == 2
94 #include "wx/db.h"
95 #endif
96
97 WXDLLEXPORT_DATA(wxDbList*) PtrBegDbList = 0;
98
99
100 char const *SQL_LOG_FILENAME = "sqllog.txt";
101 char const *SQL_CATALOG_FILENAME = "catalog.txt";
102
103 #ifdef __WXDEBUG__
104 extern wxList TablesInUse;
105 #endif
106
107 // SQL Log defaults to be used by GetDbConnection
108 wxDbSqlLogState SQLLOGstate = sqlLogOFF;
109
110 //char SQLLOGfn[wxDB_PATH_MAX+1] = SQL_LOG_FILENAME;
111 wxChar *SQLLOGfn = (wxChar*) SQL_LOG_FILENAME;
112
113 // The wxDb::errorList is copied to this variable when the wxDb object
114 // is closed. This way, the error list is still available after the
115 // database object is closed. This is necessary if the database
116 // connection fails so the calling application can show the operator
117 // why the connection failed. Note: as each wxDb object is closed, it
118 // will overwrite the errors of the previously destroyed wxDb object in
119 // this variable.
120 char DBerrorList[DB_MAX_ERROR_HISTORY][DB_MAX_ERROR_MSG_LEN];
121
122
123 /********** wxDbColFor Constructor **********/
124 wxDbColFor::wxDbColFor()
125 {
126 i_Nation = 0; // 0=EU, 1=UK, 2=International, 3=US
127 s_Field = "";
128 int i;
129 for (i=0;i<7;i++)
130 {
131 s_Format[i] = "";
132 s_Menge[i] = "";
133 i_Menge[i] = 0;
134 }
135 Format(1,DB_DATA_TYPE_VARCHAR,0,0,0); // the Function that does the work
136 } // wxDbColFor::wxDbColFor()
137
138
139 wxDbColFor::~wxDbColFor()
140 {
141 } // wxDbColFor::~wxDbColFor()
142
143
144 int wxDbColFor::Format(int Nation,int dbDataType,SWORD sqlDataType,short columnSize,short decimalDigits)
145 {
146 // ----------------------------------------------------------------------------------------
147 // -- 19991224 : mj10777@gmx.net : Create
148 // There is still a lot of work to do here, but it is a start
149 // It handles all the basic data-types that I have run into up to now
150 // The main work will have be with Dates and float Formatting (US 1,000.00 ; EU 1.000,00)
151 // There are wxWindow plans for locale support and the new wxDateTime.
152 // - if they define some constants (wxEUROPEAN) that can be gloably used,
153 // they should be used here.
154 // ----------------------------------------------------------------------------------------
155 // There should also be a Function to scan in a string to fill the variable
156 // ----------------------------------------------------------------------------------------
157 wxString Temp0;
158 i_Nation = Nation; // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
159 i_dbDataType = dbDataType;
160 i_sqlDataType = sqlDataType;
161 s_Field.Printf(wxT("%s%d"),s_Menge[1].c_str(),i_Menge[1]); // OK for VARCHAR, INTEGER and FLOAT
162 if (i_dbDataType == 0) // Filter unsupported dbDataTypes
163 {
164 if ((i_sqlDataType == SQL_VARCHAR) || (i_sqlDataType == SQL_LONGVARCHAR))
165 i_dbDataType = DB_DATA_TYPE_VARCHAR;
166 if (i_sqlDataType == SQL_C_DATE)
167 i_dbDataType = DB_DATA_TYPE_DATE;
168 if (i_sqlDataType == SQL_C_BIT)
169 i_dbDataType = DB_DATA_TYPE_INTEGER;
170 if (i_sqlDataType == SQL_NUMERIC)
171 i_dbDataType = DB_DATA_TYPE_VARCHAR;
172 if (i_sqlDataType == SQL_REAL)
173 i_dbDataType = DB_DATA_TYPE_FLOAT;
174 }
175 if ((i_dbDataType == DB_DATA_TYPE_INTEGER) && (i_sqlDataType == SQL_C_DOUBLE))
176 { // DBASE Numeric
177 i_dbDataType = DB_DATA_TYPE_FLOAT;
178 }
179 switch(i_dbDataType) // -A-> Still a lot of proper formatting to do
180 {
181 case DB_DATA_TYPE_VARCHAR:
182 s_Field = "%s"; //
183 break;
184 case DB_DATA_TYPE_INTEGER:
185 s_Field = "%d"; //
186 break;
187 case DB_DATA_TYPE_FLOAT:
188 if (decimalDigits == 0)
189 decimalDigits = 2;
190 Temp0 = "%";
191 Temp0.Printf(wxT("%s%d.%d"),Temp0.c_str(),columnSize,decimalDigits);
192 s_Field.Printf(wxT("%sf"),Temp0.c_str()); //
193 break;
194 case DB_DATA_TYPE_DATE:
195 if (i_Nation == 0) // timestamp YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
196 {
197 s_Field = "%04d-%02d-%02d %02d:%02d:%02d.%03d";
198 }
199 if (i_Nation == 1) // European DD.MM.YYYY HH:MM:SS.SSS
200 {
201 s_Field = "%02d.%02d.%04d %02d:%02d:%02d.%03d";
202 }
203 if (i_Nation == 2) // UK DD/MM/YYYY HH:MM:SS.SSS
204 {
205 s_Field = "%02d/%02d/%04d %02d:%02d:%02d.%03d";
206 }
207 if (i_Nation == 3) // International YYYY-MM-DD HH:MM:SS.SSS
208 {
209 s_Field = "%04d-%02d-%02d %02d:%02d:%02d.%03d";
210 }
211 if (i_Nation == 4) // US MM/DD/YYYY HH:MM:SS.SSS
212 {
213 s_Field = "%02d/%02d/%04d %02d:%02d:%02d.%03d";
214 }
215 break;
216 default:
217 s_Field.Printf(wxT("-E-> unknown Format(%d)-sql(%d)"),dbDataType,sqlDataType); //
218 break;
219 };
220 return TRUE;
221 } // wxDbColFor::Format()
222
223
224 /********** wxDb Constructor **********/
225 wxDb::wxDb(HENV &aHenv, bool FwdOnlyCursors)
226 {
227 int i;
228
229 fpSqlLog = 0; // Sql Log file pointer
230 sqlLogState = sqlLogOFF; // By default, logging is turned off
231 nTables = 0;
232
233 wxStrcpy(sqlState,wxT(""));
234 wxStrcpy(errorMsg,wxT(""));
235 nativeError = cbErrorMsg = 0;
236 for (i = 0; i < DB_MAX_ERROR_HISTORY; i++)
237 wxStrcpy(errorList[i], wxT(""));
238
239 // Init typeInf structures
240 wxStrcpy(typeInfVarchar.TypeName,wxT(""));
241 typeInfVarchar.FsqlType = 0;
242 typeInfVarchar.Precision = 0;
243 typeInfVarchar.CaseSensitive = 0;
244 typeInfVarchar.MaximumScale = 0;
245
246 wxStrcpy(typeInfInteger.TypeName,wxT(""));
247 typeInfInteger.FsqlType = 0;
248 typeInfInteger.Precision = 0;
249 typeInfInteger.CaseSensitive = 0;
250 typeInfInteger.MaximumScale = 0;
251
252 wxStrcpy(typeInfFloat.TypeName,wxT(""));
253 typeInfFloat.FsqlType = 0;
254 typeInfFloat.Precision = 0;
255 typeInfFloat.CaseSensitive = 0;
256 typeInfFloat.MaximumScale = 0;
257
258 wxStrcpy(typeInfDate.TypeName,wxT(""));
259 typeInfDate.FsqlType = 0;
260 typeInfDate.Precision = 0;
261 typeInfDate.CaseSensitive = 0;
262 typeInfDate.MaximumScale = 0;
263
264 // Error reporting is turned OFF by default
265 silent = TRUE;
266
267 // Copy the HENV into the db class
268 henv = aHenv;
269 fwdOnlyCursors = FwdOnlyCursors;
270
271 // Allocate a data source connection handle
272 if (SQLAllocConnect(henv, &hdbc) != SQL_SUCCESS)
273 DispAllErrors(henv);
274
275 // Initialize the db status flag
276 DB_STATUS = 0;
277
278 // Mark database as not open as of yet
279 dbIsOpen = FALSE;
280
281 } // wxDb::wxDb()
282
283
284 /********** wxDb::Open() **********/
285 bool wxDb::Open(char *Dsn, char *Uid, char *AuthStr)
286 {
287 assert(Dsn && wxStrlen(Dsn));
288 dsn = Dsn;
289 uid = Uid;
290 authStr = AuthStr;
291
292 RETCODE retcode;
293
294 if (!FwdOnlyCursors())
295 {
296 // Specify that the ODBC cursor library be used, if needed. This must be
297 // specified before the connection is made.
298 retcode = SQLSetConnectOption(hdbc, SQL_ODBC_CURSORS, SQL_CUR_USE_IF_NEEDED);
299
300 #ifdef DBDEBUG_CONSOLE
301 if (retcode == SQL_SUCCESS)
302 cout << "SQLSetConnectOption(CURSOR_LIB) successful" << endl;
303 else
304 cout << "SQLSetConnectOption(CURSOR_LIB) failed" << endl;
305 #endif
306 }
307
308 // Connect to the data source
309 retcode = SQLConnect(hdbc, (UCHAR FAR *) Dsn, SQL_NTS,
310 (UCHAR FAR *) Uid, SQL_NTS,
311 (UCHAR FAR *) AuthStr,SQL_NTS);
312
313 if (retcode == SQL_SUCCESS_WITH_INFO)
314 DispAllErrors(henv, hdbc);
315 else if (retcode != SQL_SUCCESS)
316 return(DispAllErrors(henv, hdbc));
317
318 /*
319 If using Intersolv branded ODBC drivers, this is the place where you would substitute
320 your branded driver license information
321
322 SQLSetConnectOption(hdbc, 1041, (UDWORD) "");
323 SQLSetConnectOption(hdbc, 1042, (UDWORD) "");
324 */
325
326 // Mark database as open
327 dbIsOpen = TRUE;
328
329 // Allocate a statement handle for the database connection
330 if (SQLAllocStmt(hdbc, &hstmt) != SQL_SUCCESS)
331 return(DispAllErrors(henv, hdbc));
332
333 // Set Connection Options
334 if (! setConnectionOptions())
335 return(FALSE);
336
337 // Query the data source for inf. about itself
338 if (! getDbInfo())
339 return(FALSE);
340
341 // Query the data source regarding data type information
342
343 //
344 // The way I determined which SQL data types to use was by calling SQLGetInfo
345 // for all of the possible SQL data types to see which ones were supported. If
346 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
347 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
348 // types I've selected below will not alway's be what we want. These are just
349 // what happened to work against an Oracle 7/Intersolv combination. The following is
350 // a complete list of the results I got back against the Oracle 7 database:
351 //
352 // SQL_BIGINT SQL_NO_DATA_FOUND
353 // SQL_BINARY SQL_NO_DATA_FOUND
354 // SQL_BIT SQL_NO_DATA_FOUND
355 // SQL_CHAR type name = 'CHAR', Precision = 255
356 // SQL_DATE SQL_NO_DATA_FOUND
357 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
358 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
359 // SQL_FLOAT SQL_NO_DATA_FOUND
360 // SQL_INTEGER SQL_NO_DATA_FOUND
361 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
362 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
363 // SQL_NUMERIC SQL_NO_DATA_FOUND
364 // SQL_REAL SQL_NO_DATA_FOUND
365 // SQL_SMALLINT SQL_NO_DATA_FOUND
366 // SQL_TIME SQL_NO_DATA_FOUND
367 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
368 // SQL_VARBINARY type name = 'RAW', Precision = 255
369 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
370 // =====================================================================
371 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
372 //
373 // SQL_VARCHAR type name = 'TEXT', Precision = 255
374 // SQL_TIMESTAMP type name = 'DATETIME'
375 // SQL_DECIMAL SQL_NO_DATA_FOUND
376 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
377 // SQL_FLOAT SQL_NO_DATA_FOUND
378 // SQL_REAL type name = 'SINGLE', Precision = 7
379 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
380 // SQL_INTEGER type name = 'LONG', Precision = 10
381
382 // VARCHAR = Variable length character string
383 if (! getDataTypeInfo(SQL_VARCHAR, typeInfVarchar))
384 if (! getDataTypeInfo(SQL_CHAR, typeInfVarchar))
385 return(FALSE);
386 else
387 typeInfVarchar.FsqlType = SQL_CHAR;
388 else
389 typeInfVarchar.FsqlType = SQL_VARCHAR;
390
391 // Float
392 if (! getDataTypeInfo(SQL_DOUBLE, typeInfFloat))
393
394 if (! getDataTypeInfo(SQL_REAL, typeInfFloat))
395 if (! getDataTypeInfo(SQL_FLOAT, typeInfFloat))
396 if (! getDataTypeInfo(SQL_DECIMAL, typeInfFloat))
397 if (! getDataTypeInfo(SQL_NUMERIC, typeInfFloat))
398 return(FALSE);
399 else
400 typeInfFloat.FsqlType = SQL_NUMERIC;
401 else
402 typeInfFloat.FsqlType = SQL_DECIMAL;
403 else
404 typeInfFloat.FsqlType = SQL_FLOAT;
405 else
406 typeInfFloat.FsqlType = SQL_REAL;
407 else
408 typeInfFloat.FsqlType = SQL_DOUBLE;
409
410
411 // Integer
412 if (! getDataTypeInfo(SQL_INTEGER, typeInfInteger))
413 // If SQL_INTEGER is not supported, use the floating point
414 // data type to store integers as well as floats
415 if (! getDataTypeInfo(typeInfFloat.FsqlType, typeInfInteger))
416 return(FALSE);
417 else
418 typeInfInteger.FsqlType = typeInfFloat.FsqlType;
419 else
420 typeInfInteger.FsqlType = SQL_INTEGER;
421
422 // Date/Time
423 if (Dbms() != dbmsDBASE)
424 {
425 if (! getDataTypeInfo(SQL_TIMESTAMP, typeInfDate))
426 return(FALSE);
427 else
428 typeInfDate.FsqlType = SQL_TIMESTAMP;
429 }
430 else
431 {
432 if (! getDataTypeInfo(SQL_DATE, typeInfDate))
433 return(FALSE);
434 else
435 typeInfDate.FsqlType = SQL_DATE;
436 }
437
438 #ifdef DBDEBUG_CONSOLE
439 cout << "VARCHAR DATA TYPE: " << typeInfVarchar.TypeName << endl;
440 cout << "INTEGER DATA TYPE: " << typeInfInteger.TypeName << endl;
441 cout << "FLOAT DATA TYPE: " << typeInfFloat.TypeName << endl;
442 cout << "DATE DATA TYPE: " << typeInfDate.TypeName << endl;
443 cout << endl;
444 #endif
445
446 // Completed Successfully
447 return(TRUE);
448
449 } // wxDb::Open()
450
451
452 /********** wxDb::setConnectionOptions() **********/
453 bool wxDb::setConnectionOptions(void)
454 /*
455 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
456 */
457 {
458 SQLSetConnectOption(hdbc, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF);
459 SQLSetConnectOption(hdbc, SQL_OPT_TRACE, SQL_OPT_TRACE_OFF);
460
461 // Display the connection options to verify them
462 #ifdef DBDEBUG_CONSOLE
463 long l;
464 cout << "****** CONNECTION OPTIONS ******" << endl;
465
466 if (SQLGetConnectOption(hdbc, SQL_AUTOCOMMIT, &l) != SQL_SUCCESS)
467 return(DispAllErrors(henv, hdbc));
468 cout << "AUTOCOMMIT: " << (l == SQL_AUTOCOMMIT_OFF ? "OFF" : "ON") << endl;
469
470 if (SQLGetConnectOption(hdbc, SQL_ODBC_CURSORS, &l) != SQL_SUCCESS)
471 return(DispAllErrors(henv, hdbc));
472 cout << "ODBC CURSORS: ";
473 switch(l)
474 {
475 case(SQL_CUR_USE_IF_NEEDED):
476 cout << "SQL_CUR_USE_IF_NEEDED";
477 break;
478 case(SQL_CUR_USE_ODBC):
479 cout << "SQL_CUR_USE_ODBC";
480 break;
481 case(SQL_CUR_USE_DRIVER):
482 cout << "SQL_CUR_USE_DRIVER";
483 break;
484 }
485 cout << endl;
486
487 if (SQLGetConnectOption(hdbc, SQL_OPT_TRACE, &l) != SQL_SUCCESS)
488 return(DispAllErrors(henv, hdbc));
489 cout << "TRACING: " << (l == SQL_OPT_TRACE_OFF ? "OFF" : "ON") << endl;
490
491 cout << endl;
492 #endif
493
494 // Completed Successfully
495 return(TRUE);
496
497 } // wxDb::setConnectionOptions()
498
499
500 /********** wxDb::getDbInfo() **********/
501 bool wxDb::getDbInfo(void)
502 {
503 SWORD cb;
504 RETCODE retcode;
505
506 if (SQLGetInfo(hdbc, SQL_SERVER_NAME, (UCHAR*) dbInf.serverName, 80, &cb) != SQL_SUCCESS)
507 return(DispAllErrors(henv, hdbc));
508
509 if (SQLGetInfo(hdbc, SQL_DATABASE_NAME, (UCHAR*) dbInf.databaseName, 128, &cb) != SQL_SUCCESS)
510 return(DispAllErrors(henv, hdbc));
511
512 if (SQLGetInfo(hdbc, SQL_DBMS_NAME, (UCHAR*) dbInf.dbmsName, 40, &cb) != SQL_SUCCESS)
513 return(DispAllErrors(henv, hdbc));
514
515 // 16-Mar-1999
516 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
517 // causing database connectivity to fail in some cases.
518 retcode = SQLGetInfo(hdbc, SQL_DBMS_VER, (UCHAR*) dbInf.dbmsVer, 64, &cb);
519
520 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
521 return(DispAllErrors(henv, hdbc));
522
523 if (SQLGetInfo(hdbc, SQL_ACTIVE_CONNECTIONS, (UCHAR*) &dbInf.maxConnections, sizeof(dbInf.maxConnections), &cb) != SQL_SUCCESS)
524 return(DispAllErrors(henv, hdbc));
525
526 if (SQLGetInfo(hdbc, SQL_ACTIVE_STATEMENTS, (UCHAR*) &dbInf.maxStmts, sizeof(dbInf.maxStmts), &cb) != SQL_SUCCESS)
527 return(DispAllErrors(henv, hdbc));
528
529 if (SQLGetInfo(hdbc, SQL_DRIVER_NAME, (UCHAR*) dbInf.driverName, 40, &cb) != SQL_SUCCESS)
530 return(DispAllErrors(henv, hdbc));
531
532 if (SQLGetInfo(hdbc, SQL_DRIVER_ODBC_VER, (UCHAR*) dbInf.odbcVer, 60, &cb) == SQL_ERROR)
533 return(DispAllErrors(henv, hdbc));
534
535 retcode = SQLGetInfo(hdbc, SQL_ODBC_VER, (UCHAR*) dbInf.drvMgrOdbcVer, 60, &cb);
536 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
537 return(DispAllErrors(henv, hdbc));
538
539 if (SQLGetInfo(hdbc, SQL_DRIVER_VER, (UCHAR*) dbInf.driverVer, 60, &cb) == SQL_ERROR)
540 return(DispAllErrors(henv, hdbc));
541
542 if (SQLGetInfo(hdbc, SQL_ODBC_API_CONFORMANCE, (UCHAR*) &dbInf.apiConfLvl, sizeof(dbInf.apiConfLvl), &cb) != SQL_SUCCESS)
543 return(DispAllErrors(henv, hdbc));
544
545 if (SQLGetInfo(hdbc, SQL_ODBC_SAG_CLI_CONFORMANCE, (UCHAR*) &dbInf.cliConfLvl, sizeof(dbInf.cliConfLvl), &cb) != SQL_SUCCESS)
546 return(DispAllErrors(henv, hdbc));
547
548 if (SQLGetInfo(hdbc, SQL_ODBC_SQL_CONFORMANCE, (UCHAR*) &dbInf.sqlConfLvl, sizeof(dbInf.sqlConfLvl), &cb) != SQL_SUCCESS)
549 return(DispAllErrors(henv, hdbc));
550
551 if (SQLGetInfo(hdbc, SQL_OUTER_JOINS, (UCHAR*) dbInf.outerJoins, 2, &cb) != SQL_SUCCESS)
552 return(DispAllErrors(henv, hdbc));
553
554 if (SQLGetInfo(hdbc, SQL_PROCEDURES, (UCHAR*) dbInf.procedureSupport, 2, &cb) != SQL_SUCCESS)
555 return(DispAllErrors(henv, hdbc));
556
557 if (SQLGetInfo(hdbc, SQL_CURSOR_COMMIT_BEHAVIOR, (UCHAR*) &dbInf.cursorCommitBehavior, sizeof(dbInf.cursorCommitBehavior), &cb) != SQL_SUCCESS)
558 return(DispAllErrors(henv, hdbc));
559
560 if (SQLGetInfo(hdbc, SQL_CURSOR_ROLLBACK_BEHAVIOR, (UCHAR*) &dbInf.cursorRollbackBehavior, sizeof(dbInf.cursorRollbackBehavior), &cb) != SQL_SUCCESS)
561 return(DispAllErrors(henv, hdbc));
562
563 if (SQLGetInfo(hdbc, SQL_NON_NULLABLE_COLUMNS, (UCHAR*) &dbInf.supportNotNullClause, sizeof(dbInf.supportNotNullClause), &cb) != SQL_SUCCESS)
564 return(DispAllErrors(henv, hdbc));
565
566 if (SQLGetInfo(hdbc, SQL_ODBC_SQL_OPT_IEF, (UCHAR*) dbInf.supportIEF, 2, &cb) != SQL_SUCCESS)
567 return(DispAllErrors(henv, hdbc));
568
569 if (SQLGetInfo(hdbc, SQL_DEFAULT_TXN_ISOLATION, (UCHAR*) &dbInf.txnIsolation, sizeof(dbInf.txnIsolation), &cb) != SQL_SUCCESS)
570 return(DispAllErrors(henv, hdbc));
571
572 if (SQLGetInfo(hdbc, SQL_TXN_ISOLATION_OPTION, (UCHAR*) &dbInf.txnIsolationOptions, sizeof(dbInf.txnIsolationOptions), &cb) != SQL_SUCCESS)
573 return(DispAllErrors(henv, hdbc));
574
575 if (SQLGetInfo(hdbc, SQL_FETCH_DIRECTION, (UCHAR*) &dbInf.fetchDirections, sizeof(dbInf.fetchDirections), &cb) != SQL_SUCCESS)
576 return(DispAllErrors(henv, hdbc));
577
578 if (SQLGetInfo(hdbc, SQL_LOCK_TYPES, (UCHAR*) &dbInf.lockTypes, sizeof(dbInf.lockTypes), &cb) != SQL_SUCCESS)
579 return(DispAllErrors(henv, hdbc));
580
581 if (SQLGetInfo(hdbc, SQL_POS_OPERATIONS, (UCHAR*) &dbInf.posOperations, sizeof(dbInf.posOperations), &cb) != SQL_SUCCESS)
582 return(DispAllErrors(henv, hdbc));
583
584 if (SQLGetInfo(hdbc, SQL_POSITIONED_STATEMENTS, (UCHAR*) &dbInf.posStmts, sizeof(dbInf.posStmts), &cb) != SQL_SUCCESS)
585 return(DispAllErrors(henv, hdbc));
586
587 if (SQLGetInfo(hdbc, SQL_SCROLL_CONCURRENCY, (UCHAR*) &dbInf.scrollConcurrency, sizeof(dbInf.scrollConcurrency), &cb) != SQL_SUCCESS)
588 return(DispAllErrors(henv, hdbc));
589
590 if (SQLGetInfo(hdbc, SQL_SCROLL_OPTIONS, (UCHAR*) &dbInf.scrollOptions, sizeof(dbInf.scrollOptions), &cb) != SQL_SUCCESS)
591 return(DispAllErrors(henv, hdbc));
592
593 if (SQLGetInfo(hdbc, SQL_STATIC_SENSITIVITY, (UCHAR*) &dbInf.staticSensitivity, sizeof(dbInf.staticSensitivity), &cb) != SQL_SUCCESS)
594 return(DispAllErrors(henv, hdbc));
595
596 if (SQLGetInfo(hdbc, SQL_TXN_CAPABLE, (UCHAR*) &dbInf.txnCapable, sizeof(dbInf.txnCapable), &cb) != SQL_SUCCESS)
597 return(DispAllErrors(henv, hdbc));
598
599 if (SQLGetInfo(hdbc, SQL_LOGIN_TIMEOUT, (UCHAR*) &dbInf.loginTimeout, sizeof(dbInf.loginTimeout), &cb) != SQL_SUCCESS)
600 return(DispAllErrors(henv, hdbc));
601
602 #ifdef DBDEBUG_CONSOLE
603 cout << "***** DATA SOURCE INFORMATION *****" << endl;
604 cout << "SERVER Name: " << dbInf.serverName << endl;
605 cout << "DBMS Name: " << dbInf.dbmsName << "; DBMS Version: " << dbInf.dbmsVer << endl;
606 cout << "ODBC Version: " << dbInf.odbcVer << "; Driver Version: " << dbInf.driverVer << endl;
607
608 cout << "API Conf. Level: ";
609 switch(dbInf.apiConfLvl)
610 {
611 case SQL_OAC_NONE: cout << "None"; break;
612 case SQL_OAC_LEVEL1: cout << "Level 1"; break;
613 case SQL_OAC_LEVEL2: cout << "Level 2"; break;
614 }
615 cout << endl;
616
617 cout << "SAG CLI Conf. Level: ";
618 switch(dbInf.cliConfLvl)
619 {
620 case SQL_OSCC_NOT_COMPLIANT: cout << "Not Compliant"; break;
621 case SQL_OSCC_COMPLIANT: cout << "Compliant"; break;
622 }
623 cout << endl;
624
625 cout << "SQL Conf. Level: ";
626 switch(dbInf.sqlConfLvl)
627 {
628 case SQL_OSC_MINIMUM: cout << "Minimum Grammer"; break;
629 case SQL_OSC_CORE: cout << "Core Grammer"; break;
630 case SQL_OSC_EXTENDED: cout << "Extended Grammer"; break;
631 }
632 cout << endl;
633
634 cout << "Max. Connections: " << dbInf.maxConnections << endl;
635 cout << "Outer Joins: " << dbInf.outerJoins << endl;
636 cout << "Support for Procedures: " << dbInf.procedureSupport << endl;
637
638 cout << "Cursor COMMIT Behavior: ";
639 switch(dbInf.cursorCommitBehavior)
640 {
641 case SQL_CB_DELETE: cout << "Delete cursors"; break;
642 case SQL_CB_CLOSE: cout << "Close cursors"; break;
643 case SQL_CB_PRESERVE: cout << "Preserve cursors"; break;
644 }
645 cout << endl;
646
647 cout << "Cursor ROLLBACK Behavior: ";
648 switch(dbInf.cursorRollbackBehavior)
649 {
650 case SQL_CB_DELETE: cout << "Delete cursors"; break;
651 case SQL_CB_CLOSE: cout << "Close cursors"; break;
652 case SQL_CB_PRESERVE: cout << "Preserve cursors"; break;
653 }
654 cout << endl;
655
656 cout << "Support NOT NULL clause: ";
657 switch(dbInf.supportNotNullClause)
658 {
659 case SQL_NNC_NULL: cout << "No"; break;
660 case SQL_NNC_NON_NULL: cout << "Yes"; break;
661 }
662 cout << endl;
663
664 cout << "Support IEF (Ref. Integrity): " << dbInf.supportIEF << endl;
665 cout << "Login Timeout: " << dbInf.loginTimeout << endl;
666
667 cout << endl << endl << "more ..." << endl;
668 getchar();
669
670 cout << "Default Transaction Isolation: ";
671 switch(dbInf.txnIsolation)
672 {
673 case SQL_TXN_READ_UNCOMMITTED: cout << "Read Uncommitted"; break;
674 case SQL_TXN_READ_COMMITTED: cout << "Read Committed"; break;
675 case SQL_TXN_REPEATABLE_READ: cout << "Repeatable Read"; break;
676 case SQL_TXN_SERIALIZABLE: cout << "Serializable"; break;
677 #ifdef ODBC_V20
678 case SQL_TXN_VERSIONING: cout << "Versioning"; break;
679 #endif
680 }
681 cout << endl;
682
683 cout << "Transaction Isolation Options: ";
684 if (dbInf.txnIsolationOptions & SQL_TXN_READ_UNCOMMITTED)
685 cout << "Read Uncommitted, ";
686 if (dbInf.txnIsolationOptions & SQL_TXN_READ_COMMITTED)
687 cout << "Read Committed, ";
688 if (dbInf.txnIsolationOptions & SQL_TXN_REPEATABLE_READ)
689 cout << "Repeatable Read, ";
690 if (dbInf.txnIsolationOptions & SQL_TXN_SERIALIZABLE)
691 cout << "Serializable, ";
692 #ifdef ODBC_V20
693 if (dbInf.txnIsolationOptions & SQL_TXN_VERSIONING)
694 cout << "Versioning";
695 #endif
696 cout << endl;
697
698 cout << "Fetch Directions Supported:" << endl << " ";
699 if (dbInf.fetchDirections & SQL_FD_FETCH_NEXT)
700 cout << "Next, ";
701 if (dbInf.fetchDirections & SQL_FD_FETCH_PRIOR)
702 cout << "Prev, ";
703 if (dbInf.fetchDirections & SQL_FD_FETCH_FIRST)
704 cout << "First, ";
705 if (dbInf.fetchDirections & SQL_FD_FETCH_LAST)
706 cout << "Last, ";
707 if (dbInf.fetchDirections & SQL_FD_FETCH_ABSOLUTE)
708 cout << "Absolute, ";
709 if (dbInf.fetchDirections & SQL_FD_FETCH_RELATIVE)
710 cout << "Relative, ";
711 #ifdef ODBC_V20
712 if (dbInf.fetchDirections & SQL_FD_FETCH_RESUME)
713 cout << "Resume, ";
714 #endif
715 if (dbInf.fetchDirections & SQL_FD_FETCH_BOOKMARK)
716 cout << "Bookmark";
717 cout << endl;
718
719 cout << "Lock Types Supported (SQLSetPos): ";
720 if (dbInf.lockTypes & SQL_LCK_NO_CHANGE)
721 cout << "No Change, ";
722 if (dbInf.lockTypes & SQL_LCK_EXCLUSIVE)
723 cout << "Exclusive, ";
724 if (dbInf.lockTypes & SQL_LCK_UNLOCK)
725 cout << "UnLock";
726 cout << endl;
727
728 cout << "Position Operations Supported (SQLSetPos): ";
729 if (dbInf.posOperations & SQL_POS_POSITION)
730 cout << "Position, ";
731 if (dbInf.posOperations & SQL_POS_REFRESH)
732 cout << "Refresh, ";
733 if (dbInf.posOperations & SQL_POS_UPDATE)
734 cout << "Upd, ";
735 if (dbInf.posOperations & SQL_POS_DELETE)
736 cout << "Del, ";
737 if (dbInf.posOperations & SQL_POS_ADD)
738 cout << "Add";
739 cout << endl;
740
741 cout << "Positioned Statements Supported: ";
742 if (dbInf.posStmts & SQL_PS_POSITIONED_DELETE)
743 cout << "Pos delete, ";
744 if (dbInf.posStmts & SQL_PS_POSITIONED_UPDATE)
745 cout << "Pos update, ";
746 if (dbInf.posStmts & SQL_PS_SELECT_FOR_UPDATE)
747 cout << "Select for update";
748 cout << endl;
749
750 cout << "Scroll Concurrency: ";
751 if (dbInf.scrollConcurrency & SQL_SCCO_READ_ONLY)
752 cout << "Read Only, ";
753 if (dbInf.scrollConcurrency & SQL_SCCO_LOCK)
754 cout << "Lock, ";
755 if (dbInf.scrollConcurrency & SQL_SCCO_OPT_ROWVER)
756 cout << "Opt. Rowver, ";
757 if (dbInf.scrollConcurrency & SQL_SCCO_OPT_VALUES)
758 cout << "Opt. Values";
759 cout << endl;
760
761 cout << "Scroll Options: ";
762 if (dbInf.scrollOptions & SQL_SO_FORWARD_ONLY)
763 cout << "Fwd Only, ";
764 if (dbInf.scrollOptions & SQL_SO_STATIC)
765 cout << "Static, ";
766 if (dbInf.scrollOptions & SQL_SO_KEYSET_DRIVEN)
767 cout << "Keyset Driven, ";
768 if (dbInf.scrollOptions & SQL_SO_DYNAMIC)
769 cout << "Dynamic, ";
770 if (dbInf.scrollOptions & SQL_SO_MIXED)
771 cout << "Mixed";
772 cout << endl;
773
774 cout << "Static Sensitivity: ";
775 if (dbInf.staticSensitivity & SQL_SS_ADDITIONS)
776 cout << "Additions, ";
777 if (dbInf.staticSensitivity & SQL_SS_DELETIONS)
778 cout << "Deletions, ";
779 if (dbInf.staticSensitivity & SQL_SS_UPDATES)
780 cout << "Updates";
781 cout << endl;
782
783 cout << "Transaction Capable?: ";
784 switch(dbInf.txnCapable)
785 {
786 case SQL_TC_NONE: cout << "No"; break;
787 case SQL_TC_DML: cout << "DML Only"; break;
788 case SQL_TC_DDL_COMMIT: cout << "DDL Commit"; break;
789 case SQL_TC_DDL_IGNORE: cout << "DDL Ignore"; break;
790 case SQL_TC_ALL: cout << "DDL & DML"; break;
791 }
792 cout << endl;
793
794 cout << endl;
795 #endif
796
797 // Completed Successfully
798 return(TRUE);
799
800 } // wxDb::getDbInfo()
801
802
803 /********** wxDb::getDataTypeInfo() **********/
804 bool wxDb::getDataTypeInfo(SWORD fSqlType, wxDbSqlTypeInfo &structSQLTypeInfo)
805 {
806 /*
807 * fSqlType will be something like SQL_VARCHAR. This parameter determines
808 * the data type inf. is gathered for.
809 *
810 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
811 */
812 RETCODE retcode;
813 SDWORD cbRet;
814
815 // Get information about the data type specified
816 if (SQLGetTypeInfo(hstmt, fSqlType) != SQL_SUCCESS)
817 return(DispAllErrors(henv, hdbc, hstmt));
818 // Fetch the record
819 if ((retcode = SQLFetch(hstmt)) != SQL_SUCCESS)
820 {
821 #ifdef DBDEBUG_CONSOLE
822 if (retcode == SQL_NO_DATA_FOUND)
823 cout << "SQL_NO_DATA_FOUND fetching inf. about data type." << endl;
824 #endif
825 DispAllErrors(henv, hdbc, hstmt);
826 SQLFreeStmt(hstmt, SQL_CLOSE);
827 return(FALSE);
828 }
829 // Obtain columns from the record
830 if (SQLGetData(hstmt, 1, SQL_C_CHAR, (UCHAR*) structSQLTypeInfo.TypeName, DB_TYPE_NAME_LEN, &cbRet) != SQL_SUCCESS)
831 return(DispAllErrors(henv, hdbc, hstmt));
832
833
834 // BJO 20000503: no more needed with new GetColumns...
835 #if OLD_GETCOLUMNS
836 // BJO 991209
837 if (Dbms() == dbmsMY_SQL)
838 {
839 if (!wxStrcmp(structSQLTypeInfo.TypeName, "middleint")) wxStrcpy(structSQLTypeInfo.TypeName, "mediumint");
840 if (!wxStrcmp(structSQLTypeInfo.TypeName, "middleint unsigned")) wxStrcpy(structSQLTypeInfo.TypeName, "mediumint unsigned");
841 if (!wxStrcmp(structSQLTypeInfo.TypeName, "integer")) wxStrcpy(structSQLTypeInfo.TypeName, "int");
842 if (!wxStrcmp(structSQLTypeInfo.TypeName, "integer unsigned")) wxStrcpy(structSQLTypeInfo.TypeName, "int unsigned");
843 if (!wxStrcmp(structSQLTypeInfo.TypeName, "middleint")) wxStrcpy(structSQLTypeInfo.TypeName, "mediumint");
844 if (!wxStrcmp(structSQLTypeInfo.TypeName, "varchar")) wxStrcpy(structSQLTypeInfo.TypeName, "char");
845 }
846
847 // BJO 20000427 : OpenLink driver
848 if (!wxStrncmp(dbInf.driverName, "oplodbc", 7) ||
849 !wxStrncmp(dbInf.driverName, "OLOD", 4))
850 if (!wxStrcmp(structSQLTypeInfo.TypeName, "double precision")) wxStrcpy(structSQLTypeInfo.TypeName, "real");
851 #endif
852
853 if (SQLGetData(hstmt, 3, SQL_C_LONG, (UCHAR*) &structSQLTypeInfo.Precision, 0, &cbRet) != SQL_SUCCESS)
854 return(DispAllErrors(henv, hdbc, hstmt));
855 if (SQLGetData(hstmt, 8, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.CaseSensitive, 0, &cbRet) != SQL_SUCCESS)
856 return(DispAllErrors(henv, hdbc, hstmt));
857 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
858 // return(DispAllErrors(henv, hdbc, hstmt));
859
860 if (SQLGetData(hstmt, 15, SQL_C_SHORT,(UCHAR*) &structSQLTypeInfo.MaximumScale, 0, &cbRet) != SQL_SUCCESS)
861 return(DispAllErrors(henv, hdbc, hstmt));
862
863 if (structSQLTypeInfo.MaximumScale < 0)
864 structSQLTypeInfo.MaximumScale = 0;
865
866 // Close the statement handle which closes open cursors
867 if (SQLFreeStmt(hstmt, SQL_CLOSE) != SQL_SUCCESS)
868 return(DispAllErrors(henv, hdbc, hstmt));
869
870 // Completed Successfully
871 return(TRUE);
872
873 } // wxDb::getDataTypeInfo()
874
875
876 /********** wxDb::Close() **********/
877 void wxDb::Close(void)
878 {
879 // Close the Sql Log file
880 if (fpSqlLog)
881 {
882 fclose(fpSqlLog);
883 fpSqlLog = 0;
884 }
885
886 // Free statement handle
887 if (dbIsOpen)
888 {
889 if (SQLFreeStmt(hstmt, SQL_DROP) != SQL_SUCCESS)
890 DispAllErrors(henv, hdbc);
891 }
892
893 // Disconnect from the datasource
894 if (SQLDisconnect(hdbc) != SQL_SUCCESS)
895 DispAllErrors(henv, hdbc);
896
897 // Free the connection to the datasource
898 if (SQLFreeConnect(hdbc) != SQL_SUCCESS)
899 DispAllErrors(henv, hdbc);
900
901 // There should be zero Ctable objects still connected to this db object
902 assert(nTables == 0);
903
904 #ifdef __WXDEBUG__
905 wxTablesInUse *tiu;
906 wxNode *pNode;
907 pNode = TablesInUse.First();
908 wxString s,s2;
909 while (pNode)
910 {
911 tiu = (wxTablesInUse *)pNode->Data();
912 if (tiu->pDb == this)
913 {
914 s.sprintf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu->tableName,tiu->tableID,tiu->pDb);
915 s2.sprintf(wxT("Orphaned found using pDb:[%p]"),this);
916 wxLogDebug (s.c_str(),s2.c_str());
917 }
918 pNode = pNode->Next();
919 }
920 #endif
921
922 // Copy the error messages to a global variable
923 int i;
924 for (i = 0; i < DB_MAX_ERROR_HISTORY; i++)
925 wxStrcpy(DBerrorList[i],errorList[i]);
926
927 } // wxDb::Close()
928
929
930 /********** wxDb::CommitTrans() **********/
931 bool wxDb::CommitTrans(void)
932 {
933 if (this)
934 {
935 // Commit the transaction
936 if (SQLTransact(henv, hdbc, SQL_COMMIT) != SQL_SUCCESS)
937 return(DispAllErrors(henv, hdbc));
938 }
939
940 // Completed successfully
941 return(TRUE);
942
943 } // wxDb::CommitTrans()
944
945
946 /********** wxDb::RollbackTrans() **********/
947 bool wxDb::RollbackTrans(void)
948 {
949 // Rollback the transaction
950 if (SQLTransact(henv, hdbc, SQL_ROLLBACK) != SQL_SUCCESS)
951 return(DispAllErrors(henv, hdbc));
952
953 // Completed successfully
954 return(TRUE);
955
956 } // wxDb::RollbackTrans()
957
958
959 /********** wxDb::DispAllErrors() **********/
960 bool wxDb::DispAllErrors(HENV aHenv, HDBC aHdbc, HSTMT aHstmt)
961 {
962 // char odbcErrMsg[DB_MAX_ERROR_MSG_LEN];
963 wxString odbcErrMsg;
964
965 while (SQLError(aHenv, aHdbc, aHstmt, (UCHAR FAR *) sqlState, &nativeError, (UCHAR FAR *) errorMsg, SQL_MAX_MESSAGE_LENGTH - 1, &cbErrorMsg) == SQL_SUCCESS)
966 {
967 odbcErrMsg.sprintf("SQL State = %s\nNative Error Code = %li\nError Message = %s\n", sqlState, nativeError, errorMsg);
968 logError(odbcErrMsg.c_str(), sqlState);
969 if (!silent)
970 {
971 #ifdef DBDEBUG_CONSOLE
972 // When run in console mode, use standard out to display errors.
973 cout << odbcErrMsg.c_str() << endl;
974 cout << "Press any key to continue..." << endl;
975 getchar();
976 #endif
977
978 #ifdef __WXDEBUG__
979 wxLogDebug(odbcErrMsg.c_str(),wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
980 #endif
981 }
982 }
983
984 return(FALSE); // This function always returns false.
985
986 } // wxDb::DispAllErrors()
987
988
989 /********** wxDb::GetNextError() **********/
990 bool wxDb::GetNextError(HENV aHenv, HDBC aHdbc, HSTMT aHstmt)
991 {
992 if (SQLError(aHenv, aHdbc, aHstmt, (UCHAR FAR *) sqlState, &nativeError, (UCHAR FAR *) errorMsg, SQL_MAX_MESSAGE_LENGTH - 1, &cbErrorMsg) == SQL_SUCCESS)
993 return(TRUE);
994 else
995 return(FALSE);
996
997 } // wxDb::GetNextError()
998
999
1000 /********** wxDb::DispNextError() **********/
1001 void wxDb::DispNextError(void)
1002 {
1003 // char odbcErrMsg[DB_MAX_ERROR_MSG_LEN];
1004 wxString odbcErrMsg;
1005
1006 odbcErrMsg.sprintf("SQL State = %s\nNative Error Code = %li\nError Message = %s\n", sqlState, nativeError, errorMsg);
1007 logError(odbcErrMsg.c_str(), sqlState);
1008
1009 if (silent)
1010 return;
1011
1012 #ifdef DBDEBUG_CONSOLE
1013 // When run in console mode, use standard out to display errors.
1014 cout << odbcErrMsg.c_str() << endl;
1015 cout << "Press any key to continue..." << endl;
1016 getchar();
1017 #endif
1018
1019 #ifdef __WXDEBUG__
1020 wxLogDebug(odbcErrMsg,wxT("ODBC DEBUG MESSAGE"));
1021 #endif // __WXDEBUG__
1022
1023
1024 } // wxDb::DispNextError()
1025
1026
1027 /********** wxDb::logError() **********/
1028 void wxDb::logError(const char *errMsg, const char *SQLState)
1029 {
1030 assert(errMsg && wxStrlen(errMsg));
1031
1032 static int pLast = -1;
1033 int dbStatus;
1034
1035 if (++pLast == DB_MAX_ERROR_HISTORY)
1036 {
1037 int i;
1038 for (i = 0; i < DB_MAX_ERROR_HISTORY; i++)
1039 wxStrcpy(errorList[i], errorList[i+1]);
1040 pLast--;
1041 }
1042
1043 wxStrcpy(errorList[pLast], errMsg);
1044
1045 if (SQLState && wxStrlen(SQLState))
1046 if ((dbStatus = TranslateSqlState(SQLState)) != DB_ERR_FUNCTION_SEQUENCE_ERROR)
1047 DB_STATUS = dbStatus;
1048
1049 // Add the errmsg to the sql log
1050 WriteSqlLog(errMsg);
1051
1052 } // wxDb::logError()
1053
1054
1055 /**********wxDb::TranslateSqlState() **********/
1056 int wxDb::TranslateSqlState(const wxChar *SQLState)
1057 {
1058 if (!wxStrcmp(SQLState, wxT("01000")))
1059 return(DB_ERR_GENERAL_WARNING);
1060 if (!wxStrcmp(SQLState, wxT("01002")))
1061 return(DB_ERR_DISCONNECT_ERROR);
1062 if (!wxStrcmp(SQLState, wxT("01004")))
1063 return(DB_ERR_DATA_TRUNCATED);
1064 if (!wxStrcmp(SQLState, wxT("01006")))
1065 return(DB_ERR_PRIV_NOT_REVOKED);
1066 if (!wxStrcmp(SQLState, wxT("01S00")))
1067 return(DB_ERR_INVALID_CONN_STR_ATTR);
1068 if (!wxStrcmp(SQLState, wxT("01S01")))
1069 return(DB_ERR_ERROR_IN_ROW);
1070 if (!wxStrcmp(SQLState, wxT("01S02")))
1071 return(DB_ERR_OPTION_VALUE_CHANGED);
1072 if (!wxStrcmp(SQLState, wxT("01S03")))
1073 return(DB_ERR_NO_ROWS_UPD_OR_DEL);
1074 if (!wxStrcmp(SQLState, wxT("01S04")))
1075 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL);
1076 if (!wxStrcmp(SQLState, wxT("07001")))
1077 return(DB_ERR_WRONG_NO_OF_PARAMS);
1078 if (!wxStrcmp(SQLState, wxT("07006")))
1079 return(DB_ERR_DATA_TYPE_ATTR_VIOL);
1080 if (!wxStrcmp(SQLState, wxT("08001")))
1081 return(DB_ERR_UNABLE_TO_CONNECT);
1082 if (!wxStrcmp(SQLState, wxT("08002")))
1083 return(DB_ERR_CONNECTION_IN_USE);
1084 if (!wxStrcmp(SQLState, wxT("08003")))
1085 return(DB_ERR_CONNECTION_NOT_OPEN);
1086 if (!wxStrcmp(SQLState, wxT("08004")))
1087 return(DB_ERR_REJECTED_CONNECTION);
1088 if (!wxStrcmp(SQLState, wxT("08007")))
1089 return(DB_ERR_CONN_FAIL_IN_TRANS);
1090 if (!wxStrcmp(SQLState, wxT("08S01")))
1091 return(DB_ERR_COMM_LINK_FAILURE);
1092 if (!wxStrcmp(SQLState, wxT("21S01")))
1093 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH);
1094 if (!wxStrcmp(SQLState, wxT("21S02")))
1095 return(DB_ERR_DERIVED_TABLE_MISMATCH);
1096 if (!wxStrcmp(SQLState, wxT("22001")))
1097 return(DB_ERR_STRING_RIGHT_TRUNC);
1098 if (!wxStrcmp(SQLState, wxT("22003")))
1099 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG);
1100 if (!wxStrcmp(SQLState, wxT("22005")))
1101 return(DB_ERR_ERROR_IN_ASSIGNMENT);
1102 if (!wxStrcmp(SQLState, wxT("22008")))
1103 return(DB_ERR_DATETIME_FLD_OVERFLOW);
1104 if (!wxStrcmp(SQLState, wxT("22012")))
1105 return(DB_ERR_DIVIDE_BY_ZERO);
1106 if (!wxStrcmp(SQLState, wxT("22026")))
1107 return(DB_ERR_STR_DATA_LENGTH_MISMATCH);
1108 if (!wxStrcmp(SQLState, wxT("23000")))
1109 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL);
1110 if (!wxStrcmp(SQLState, wxT("24000")))
1111 return(DB_ERR_INVALID_CURSOR_STATE);
1112 if (!wxStrcmp(SQLState, wxT("25000")))
1113 return(DB_ERR_INVALID_TRANS_STATE);
1114 if (!wxStrcmp(SQLState, wxT("28000")))
1115 return(DB_ERR_INVALID_AUTH_SPEC);
1116 if (!wxStrcmp(SQLState, wxT("34000")))
1117 return(DB_ERR_INVALID_CURSOR_NAME);
1118 if (!wxStrcmp(SQLState, wxT("37000")))
1119 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL);
1120 if (!wxStrcmp(SQLState, wxT("3C000")))
1121 return(DB_ERR_DUPLICATE_CURSOR_NAME);
1122 if (!wxStrcmp(SQLState, wxT("40001")))
1123 return(DB_ERR_SERIALIZATION_FAILURE);
1124 if (!wxStrcmp(SQLState, wxT("42000")))
1125 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2);
1126 if (!wxStrcmp(SQLState, wxT("70100")))
1127 return(DB_ERR_OPERATION_ABORTED);
1128 if (!wxStrcmp(SQLState, wxT("IM001")))
1129 return(DB_ERR_UNSUPPORTED_FUNCTION);
1130 if (!wxStrcmp(SQLState, wxT("IM002")))
1131 return(DB_ERR_NO_DATA_SOURCE);
1132 if (!wxStrcmp(SQLState, wxT("IM003")))
1133 return(DB_ERR_DRIVER_LOAD_ERROR);
1134 if (!wxStrcmp(SQLState, wxT("IM004")))
1135 return(DB_ERR_SQLALLOCENV_FAILED);
1136 if (!wxStrcmp(SQLState, wxT("IM005")))
1137 return(DB_ERR_SQLALLOCCONNECT_FAILED);
1138 if (!wxStrcmp(SQLState, wxT("IM006")))
1139 return(DB_ERR_SQLSETCONNECTOPTION_FAILED);
1140 if (!wxStrcmp(SQLState, wxT("IM007")))
1141 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB);
1142 if (!wxStrcmp(SQLState, wxT("IM008")))
1143 return(DB_ERR_DIALOG_FAILED);
1144 if (!wxStrcmp(SQLState, wxT("IM009")))
1145 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL);
1146 if (!wxStrcmp(SQLState, wxT("IM010")))
1147 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG);
1148 if (!wxStrcmp(SQLState, wxT("IM011")))
1149 return(DB_ERR_DRIVER_NAME_TOO_LONG);
1150 if (!wxStrcmp(SQLState, wxT("IM012")))
1151 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR);
1152 if (!wxStrcmp(SQLState, wxT("IM013")))
1153 return(DB_ERR_TRACE_FILE_ERROR);
1154 if (!wxStrcmp(SQLState, wxT("S0001")))
1155 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS);
1156 if (!wxStrcmp(SQLState, wxT("S0002")))
1157 return(DB_ERR_TABLE_NOT_FOUND);
1158 if (!wxStrcmp(SQLState, wxT("S0011")))
1159 return(DB_ERR_INDEX_ALREADY_EXISTS);
1160 if (!wxStrcmp(SQLState, wxT("S0012")))
1161 return(DB_ERR_INDEX_NOT_FOUND);
1162 if (!wxStrcmp(SQLState, wxT("S0021")))
1163 return(DB_ERR_COLUMN_ALREADY_EXISTS);
1164 if (!wxStrcmp(SQLState, wxT("S0022")))
1165 return(DB_ERR_COLUMN_NOT_FOUND);
1166 if (!wxStrcmp(SQLState, wxT("S0023")))
1167 return(DB_ERR_NO_DEFAULT_FOR_COLUMN);
1168 if (!wxStrcmp(SQLState, wxT("S1000")))
1169 return(DB_ERR_GENERAL_ERROR);
1170 if (!wxStrcmp(SQLState, wxT("S1001")))
1171 return(DB_ERR_MEMORY_ALLOCATION_FAILURE);
1172 if (!wxStrcmp(SQLState, wxT("S1002")))
1173 return(DB_ERR_INVALID_COLUMN_NUMBER);
1174 if (!wxStrcmp(SQLState, wxT("S1003")))
1175 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE);
1176 if (!wxStrcmp(SQLState, wxT("S1004")))
1177 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE);
1178 if (!wxStrcmp(SQLState, wxT("S1008")))
1179 return(DB_ERR_OPERATION_CANCELLED);
1180 if (!wxStrcmp(SQLState, wxT("S1009")))
1181 return(DB_ERR_INVALID_ARGUMENT_VALUE);
1182 if (!wxStrcmp(SQLState, wxT("S1010")))
1183 return(DB_ERR_FUNCTION_SEQUENCE_ERROR);
1184 if (!wxStrcmp(SQLState, wxT("S1011")))
1185 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME);
1186 if (!wxStrcmp(SQLState, wxT("S1012")))
1187 return(DB_ERR_INVALID_TRANS_OPERATION_CODE);
1188 if (!wxStrcmp(SQLState, wxT("S1015")))
1189 return(DB_ERR_NO_CURSOR_NAME_AVAIL);
1190 if (!wxStrcmp(SQLState, wxT("S1090")))
1191 return(DB_ERR_INVALID_STR_OR_BUF_LEN);
1192 if (!wxStrcmp(SQLState, wxT("S1091")))
1193 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE);
1194 if (!wxStrcmp(SQLState, wxT("S1092")))
1195 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE);
1196 if (!wxStrcmp(SQLState, wxT("S1093")))
1197 return(DB_ERR_INVALID_PARAM_NO);
1198 if (!wxStrcmp(SQLState, wxT("S1094")))
1199 return(DB_ERR_INVALID_SCALE_VALUE);
1200 if (!wxStrcmp(SQLState, wxT("S1095")))
1201 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE);
1202 if (!wxStrcmp(SQLState, wxT("S1096")))
1203 return(DB_ERR_INF_TYPE_OUT_OF_RANGE);
1204 if (!wxStrcmp(SQLState, wxT("S1097")))
1205 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE);
1206 if (!wxStrcmp(SQLState, wxT("S1098")))
1207 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE);
1208 if (!wxStrcmp(SQLState, wxT("S1099")))
1209 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE);
1210 if (!wxStrcmp(SQLState, wxT("S1100")))
1211 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE);
1212 if (!wxStrcmp(SQLState, wxT("S1101")))
1213 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE);
1214 if (!wxStrcmp(SQLState, wxT("S1103")))
1215 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE);
1216 if (!wxStrcmp(SQLState, wxT("S1104")))
1217 return(DB_ERR_INVALID_PRECISION_VALUE);
1218 if (!wxStrcmp(SQLState, wxT("S1105")))
1219 return(DB_ERR_INVALID_PARAM_TYPE);
1220 if (!wxStrcmp(SQLState, wxT("S1106")))
1221 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE);
1222 if (!wxStrcmp(SQLState, wxT("S1107")))
1223 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE);
1224 if (!wxStrcmp(SQLState, wxT("S1108")))
1225 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE);
1226 if (!wxStrcmp(SQLState, wxT("S1109")))
1227 return(DB_ERR_INVALID_CURSOR_POSITION);
1228 if (!wxStrcmp(SQLState, wxT("S1110")))
1229 return(DB_ERR_INVALID_DRIVER_COMPLETION);
1230 if (!wxStrcmp(SQLState, wxT("S1111")))
1231 return(DB_ERR_INVALID_BOOKMARK_VALUE);
1232 if (!wxStrcmp(SQLState, wxT("S1C00")))
1233 return(DB_ERR_DRIVER_NOT_CAPABLE);
1234 if (!wxStrcmp(SQLState, wxT("S1T00")))
1235 return(DB_ERR_TIMEOUT_EXPIRED);
1236
1237 // No match
1238 return(0);
1239
1240 } // wxDb::TranslateSqlState()
1241
1242
1243 /********** wxDb::Grant() **********/
1244 bool wxDb::Grant(int privileges, const char *tableName, const char *userList)
1245 {
1246 // char sqlStmt[DB_MAX_STATEMENT_LEN];
1247 wxString sqlStmt;
1248
1249 // Build the grant statement
1250 sqlStmt = "GRANT ";
1251 if (privileges == DB_GRANT_ALL)
1252 sqlStmt += "ALL";
1253 else
1254 {
1255 int c = 0;
1256 if (privileges & DB_GRANT_SELECT)
1257 {
1258 sqlStmt += "SELECT";
1259 c++;
1260 }
1261 if (privileges & DB_GRANT_INSERT)
1262 {
1263 if (c++)
1264 sqlStmt += ", ";
1265 sqlStmt += "INSERT";
1266 }
1267 if (privileges & DB_GRANT_UPDATE)
1268 {
1269 if (c++)
1270 sqlStmt += ", ";
1271 sqlStmt += "UPDATE";
1272 }
1273 if (privileges & DB_GRANT_DELETE)
1274 {
1275 if (c++)
1276 sqlStmt += ", ";
1277 sqlStmt += "DELETE";
1278 }
1279 }
1280
1281 sqlStmt += " ON ";
1282 sqlStmt += tableName;
1283 sqlStmt += " TO ";
1284 sqlStmt += userList;
1285
1286 #ifdef DBDEBUG_CONSOLE
1287 cout << endl << sqlStmt.c_str() << endl;
1288 #endif
1289
1290 WriteSqlLog(sqlStmt.c_str());
1291
1292 return(ExecSql(sqlStmt.c_str()));
1293
1294 } // wxDb::Grant()
1295
1296
1297 /********** wxDb::CreateView() **********/
1298 bool wxDb::CreateView(const char *viewName, const char *colList, const char *pSqlStmt, bool attemptDrop)
1299 {
1300 // char sqlStmt[DB_MAX_STATEMENT_LEN];
1301 wxString sqlStmt;
1302
1303 // Drop the view first
1304 if (attemptDrop && !DropView(viewName))
1305 return FALSE;
1306
1307 // Build the create view statement
1308 sqlStmt = "CREATE VIEW ";
1309 sqlStmt += viewName;
1310
1311 if (wxStrlen(colList))
1312 {
1313 sqlStmt += " (";
1314 sqlStmt += colList;
1315 sqlStmt += ")";
1316 }
1317
1318 sqlStmt += " AS ";
1319 sqlStmt += pSqlStmt;
1320
1321 WriteSqlLog(sqlStmt.c_str());
1322
1323 #ifdef DBDEBUG_CONSOLE
1324 cout << sqlStmt.c_str() << endl;
1325 #endif
1326
1327 return(ExecSql(sqlStmt.c_str()));
1328
1329 } // wxDb::CreateView()
1330
1331
1332 /********** wxDb::DropView() **********/
1333 bool wxDb::DropView(const char *viewName)
1334 {
1335 /*
1336 * NOTE: This function returns TRUE if the View does not exist, but
1337 * only for identified databases. Code will need to be added
1338 * below for any other databases when those databases are defined
1339 * to handle this situation consistently
1340 */
1341 // char sqlStmt[DB_MAX_STATEMENT_LEN];
1342 wxString sqlStmt;
1343
1344 sqlStmt.sprintf("DROP VIEW %s", viewName);
1345
1346 WriteSqlLog(sqlStmt.c_str());
1347
1348 #ifdef DBDEBUG_CONSOLE
1349 cout << endl << sqlStmt.c_str() << endl;
1350 #endif
1351
1352 if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
1353 {
1354 // Check for "Base table not found" error and ignore
1355 GetNextError(henv, hdbc, hstmt);
1356 if (wxStrcmp(sqlState,wxT("S0002"))) // "Base table not found"
1357 {
1358 // Check for product specific error codes
1359 if (!((Dbms() == dbmsSYBASE_ASA && !wxStrcmp(sqlState,wxT("42000"))))) // 5.x (and lower?)
1360 {
1361 DispNextError();
1362 DispAllErrors(henv, hdbc, hstmt);
1363 RollbackTrans();
1364 return(FALSE);
1365 }
1366 }
1367 }
1368
1369 // Commit the transaction
1370 if (! CommitTrans())
1371 return(FALSE);
1372
1373 return TRUE;
1374
1375 } // wxDb::DropView()
1376
1377
1378 /********** wxDb::ExecSql() **********/
1379 bool wxDb::ExecSql(const char *pSqlStmt)
1380 {
1381 SQLFreeStmt(hstmt, SQL_CLOSE);
1382 if (SQLExecDirect(hstmt, (UCHAR FAR *) pSqlStmt, SQL_NTS) == SQL_SUCCESS)
1383 return(TRUE);
1384 else
1385 {
1386 DispAllErrors(henv, hdbc, hstmt);
1387 return(FALSE);
1388 }
1389
1390 } // wxDb::ExecSql()
1391
1392
1393 /********** wxDb::GetNext() **********/
1394 bool wxDb::GetNext(void)
1395 {
1396 if (SQLFetch(hstmt) == SQL_SUCCESS)
1397 return(TRUE);
1398 else
1399 {
1400 DispAllErrors(henv, hdbc, hstmt);
1401 return(FALSE);
1402 }
1403
1404 } // wxDb::GetNext()
1405
1406
1407 /********** wxDb::GetData() **********/
1408 bool wxDb::GetData(UWORD colNo, SWORD cType, PTR pData, SDWORD maxLen, SDWORD FAR *cbReturned)
1409 {
1410 assert(pData);
1411 assert(cbReturned);
1412
1413 if (SQLGetData(hstmt, colNo, cType, pData, maxLen, cbReturned) == SQL_SUCCESS)
1414 return(TRUE);
1415 else
1416 {
1417 DispAllErrors(henv, hdbc, hstmt);
1418 return(FALSE);
1419 }
1420
1421 } // wxDb::GetData()
1422
1423
1424 /********** wxDb::GetKeyFields() **********/
1425 int wxDb::GetKeyFields(char *tableName, wxDbColInf* colInf, int noCols)
1426 {
1427 char szPkTable[DB_MAX_TABLE_NAME_LEN+1]; /* Primary key table name */
1428 char szFkTable[DB_MAX_TABLE_NAME_LEN+1]; /* Foreign key table name */
1429 short iKeySeq;
1430 // SQLSMALLINT iKeySeq;
1431 char szPkCol[DB_MAX_COLUMN_NAME_LEN+1]; /* Primary key column */
1432 char szFkCol[DB_MAX_COLUMN_NAME_LEN+1]; /* Foreign key column */
1433 SQLRETURN retcode;
1434 SDWORD cb;
1435 int i;
1436 wxString Temp0;
1437 /*
1438 * ---------------------------------------------------------------------
1439 * -- 19991224 : mj10777@gmx.net : Create ------
1440 * -- : Three things are done and stored here : ------
1441 * -- : 1) which Column(s) is/are Primary Key(s) ------
1442 * -- : 2) which tables use this Key as a Foreign Key ------
1443 * -- : 3) which columns are Foreign Key and the name ------
1444 * -- : of the Table where the Key is the Primary Key -----
1445 * -- : Called from GetColumns(char *tableName, ------
1446 * -- int *numCols,const char *userID ) ------
1447 * ---------------------------------------------------------------------
1448 */
1449
1450 /*---------------------------------------------------------------------*/
1451 /* Get the names of the columns in the primary key. */
1452 /*---------------------------------------------------------------------*/
1453 retcode = SQLPrimaryKeys(hstmt,
1454 NULL, 0, /* Catalog name */
1455 NULL, 0, /* Schema name */
1456 (UCHAR *) tableName, SQL_NTS); /* Table name */
1457
1458 /*---------------------------------------------------------------------*/
1459 /* Fetch and display the result set. This will be a list of the */
1460 /* columns in the primary key of the tableName table. */
1461 /*---------------------------------------------------------------------*/
1462 while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
1463 {
1464 retcode = SQLFetch(hstmt);
1465 if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
1466 {
1467 GetData( 4, SQL_C_CHAR, szPkCol, DB_MAX_COLUMN_NAME_LEN+1, &cb);
1468 GetData( 5, SQL_C_SSHORT, &iKeySeq, 0, &cb);
1469 //-------
1470 for (i=0;i<noCols;i++) // Find the Column name
1471 if (!wxStrcmp(colInf[i].colName,szPkCol)) // We have found the Column
1472 colInf[i].PkCol = iKeySeq; // Which Primary Key is this (first, second usw.) ?
1473 } // if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
1474 } // while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
1475 SQLFreeStmt(hstmt, SQL_CLOSE); /* Close the cursor (the hstmt is still allocated). */
1476
1477 /*---------------------------------------------------------------------*/
1478 /* Get all the foreign keys that refer to tableName primary key. */
1479 /*---------------------------------------------------------------------*/
1480 retcode = SQLForeignKeys(hstmt,
1481 NULL, 0, /* Primary catalog */
1482 NULL, 0, /* Primary schema */
1483 (UCHAR *)tableName, SQL_NTS, /* Primary table */
1484 NULL, 0, /* Foreign catalog */
1485 NULL, 0, /* Foreign schema */
1486 NULL, 0); /* Foreign table */
1487
1488 /*---------------------------------------------------------------------*/
1489 /* Fetch and display the result set. This will be all of the foreign */
1490 /* keys in other tables that refer to the tableName primary key. */
1491 /*---------------------------------------------------------------------*/
1492 Temp0.Empty();
1493 szPkCol[0] = 0;
1494 while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
1495 {
1496 retcode = SQLFetch(hstmt);
1497 if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
1498 {
1499 GetData( 3, SQL_C_CHAR, szPkTable, DB_MAX_TABLE_NAME_LEN+1, &cb);
1500 GetData( 4, SQL_C_CHAR, szPkCol, DB_MAX_COLUMN_NAME_LEN+1, &cb);
1501 GetData( 5, SQL_C_SSHORT, &iKeySeq, 0, &cb);
1502 GetData( 7, SQL_C_CHAR, szFkTable, DB_MAX_TABLE_NAME_LEN+1, &cb);
1503 GetData( 8, SQL_C_CHAR, szFkCol, DB_MAX_COLUMN_NAME_LEN+1, &cb);
1504 Temp0.Printf(wxT("%s[%s] "),Temp0.c_str(),szFkTable); // [ ] in case there is a blank in the Table name
1505 } // if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
1506 } // while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
1507 Temp0.Trim(); // Get rid of any unneeded blanks
1508 if (Temp0 != wxT(""))
1509 {
1510 for (i=0;i<noCols;i++) // Find the Column name
1511 if (!wxStrcmp(colInf[i].colName,szPkCol)) // We have found the Column, store the Information
1512 wxStrcpy(colInf[i].PkTableName,Temp0.c_str()); // Name of the Tables where this Primary Key is used as a Foreign Key
1513 } // if (Temp0 != "")
1514 SQLFreeStmt(hstmt, SQL_CLOSE); /* Close the cursor (the hstmt is still allocated). */
1515
1516 /*---------------------------------------------------------------------*/
1517 /* Get all the foreign keys in the tablename table. */
1518 /*---------------------------------------------------------------------*/
1519 retcode = SQLForeignKeys(hstmt,
1520 NULL, 0, /* Primary catalog */
1521 NULL, 0, /* Primary schema */
1522 NULL, 0, /* Primary table */
1523 NULL, 0, /* Foreign catalog */
1524 NULL, 0, /* Foreign schema */
1525 (UCHAR *)tableName, SQL_NTS); /* Foreign table */
1526
1527 /*---------------------------------------------------------------------*/
1528 /* Fetch and display the result set. This will be all of the */
1529 /* primary keys in other tables that are referred to by foreign */
1530 /* keys in the tableName table. */
1531 /*---------------------------------------------------------------------*/
1532 i = 0;
1533 while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
1534 {
1535 retcode = SQLFetch(hstmt);
1536 if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
1537 {
1538 GetData( 3, SQL_C_CHAR, szPkTable, DB_MAX_TABLE_NAME_LEN+1, &cb);
1539 GetData( 5, SQL_C_SSHORT, &iKeySeq, 0, &cb);
1540 GetData( 8, SQL_C_CHAR, szFkCol, DB_MAX_COLUMN_NAME_LEN+1, &cb);
1541 //-------
1542 for (i=0;i<noCols;i++) // Find the Column name
1543 {
1544 if (!wxStrcmp(colInf[i].colName,szFkCol)) // We have found the (Foreign Key) Column
1545 {
1546 colInf[i].FkCol = iKeySeq; // Which Foreign Key is this (first, second usw.) ?
1547 wxStrcpy(colInf[i].FkTableName,szPkTable); // Name of the Table where this Foriegn is the Primary Key
1548 } // if (!wxStrcmp(colInf[i].colName,szFkCol))
1549 } // for (i=0;i<noCols;i++)
1550 } // if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
1551 } // while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
1552 SQLFreeStmt(hstmt, SQL_CLOSE); /* Close the cursor (the hstmt is still allocated). */
1553
1554 /*---------------------------------------------------------------------*/
1555 return TRUE;
1556 } // wxDb::GetKeyFields()
1557
1558
1559 #if OLD_GETCOLUMNS
1560 /********** wxDb::GetColumns() **********/
1561 wxDbColInf *wxDb::GetColumns(char *tableName[], const char *userID)
1562 /*
1563 * 1) The last array element of the tableName[] argument must be zero (null).
1564 * This is how the end of the array is detected.
1565 * 2) This function returns an array of wxDbColInf structures. If no columns
1566 * were found, or an error occured, this pointer will be zero (null). THE
1567 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
1568 * IS FINISHED WITH IT. i.e.
1569 *
1570 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
1571 * if (colInf)
1572 * {
1573 * // Use the column inf
1574 * .......
1575 * // Destroy the memory
1576 * delete [] colInf;
1577 * }
1578 *
1579 * userID is evaluated in the following manner:
1580 * userID == NULL ... UserID is ignored
1581 * userID == "" ... UserID set equal to 'this->uid'
1582 * userID != "" ... UserID set equal to 'userID'
1583 *
1584 * NOTE: ALL column bindings associated with this wxDb instance are unbound
1585 * by this function. This function should use its own wxDb instance
1586 * to avoid undesired unbinding of columns.
1587 */
1588 {
1589 int noCols = 0;
1590 int colNo = 0;
1591 wxDbColInf *colInf = 0;
1592
1593 RETCODE retcode;
1594 SDWORD cb;
1595
1596 wxString UserID;
1597 wxString TableName;
1598
1599 if (userID)
1600 {
1601 if (!wxStrlen(userID))
1602 UserID = uid;
1603 else
1604 UserID = userID;
1605 }
1606 else
1607 UserID = "";
1608
1609 // dBase does not use user names, and some drivers fail if you try to pass one
1610 if (Dbms() == dbmsDBASE)
1611 UserID = "";
1612
1613 // Oracle user names may only be in uppercase, so force
1614 // the name to uppercase
1615 if (Dbms() == dbmsORACLE)
1616 UserID = UserID.Upper();
1617
1618 // Pass 1 - Determine how many columns there are.
1619 // Pass 2 - Allocate the wxDbColInf array and fill in
1620 // the array with the column information.
1621 int pass;
1622 for (pass = 1; pass <= 2; pass++)
1623 {
1624 if (pass == 2)
1625 {
1626 if (noCols == 0) // Probably a bogus table name(s)
1627 break;
1628 // Allocate n wxDbColInf objects to hold the column information
1629 colInf = new wxDbColInf[noCols+1];
1630 if (!colInf)
1631 break;
1632 // Mark the end of the array
1633 wxStrcpy(colInf[noCols].tableName, wxT(""));
1634 wxStrcpy(colInf[noCols].colName, wxT(""));
1635 colInf[noCols].sqlDataType = 0;
1636 }
1637 // Loop through each table name
1638 int tbl;
1639 for (tbl = 0; tableName[tbl]; tbl++)
1640 {
1641 TableName = tableName[tbl];
1642 // Oracle table names are uppercase only, so force
1643 // the name to uppercase just in case programmer forgot to do this
1644 if (Dbms() == dbmsORACLE)
1645 TableName = TableName.Upper();
1646
1647 SQLFreeStmt(hstmt, SQL_CLOSE);
1648
1649 // MySQL and Access cannot accept a user name when looking up column names, so we
1650 // use the call below that leaves out the user name
1651 if (wxStrcmp(UserID.c_str(),wxT("")) &&
1652 Dbms() != dbmsMY_SQL &&
1653 Dbms() != dbmsACCESS)
1654 {
1655 retcode = SQLColumns(hstmt,
1656 NULL, 0, // All qualifiers
1657 (UCHAR *) UserID.c_str(), SQL_NTS, // Owner
1658 (UCHAR *) TableName.c_str(), SQL_NTS,
1659 NULL, 0); // All columns
1660 }
1661 else
1662 {
1663 retcode = SQLColumns(hstmt,
1664 NULL, 0, // All qualifiers
1665 NULL, 0, // Owner
1666 (UCHAR *) TableName.c_str(), SQL_NTS,
1667 NULL, 0); // All columns
1668 }
1669 if (retcode != SQL_SUCCESS)
1670 { // Error occured, abort
1671 DispAllErrors(henv, hdbc, hstmt);
1672 if (colInf)
1673 delete [] colInf;
1674 SQLFreeStmt(hstmt, SQL_CLOSE);
1675 return(0);
1676 }
1677
1678 while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
1679 {
1680 if (pass == 1) // First pass, just add up the number of columns
1681 noCols++;
1682 else // Pass 2; Fill in the array of structures
1683 {
1684 if (colNo < noCols) // Some extra error checking to prevent memory overwrites
1685 {
1686 // NOTE: Only the ODBC 1.x fields are retrieved
1687 GetData( 1, SQL_C_CHAR, (UCHAR*) colInf[colNo].catalog, 128+1, &cb);
1688 GetData( 2, SQL_C_CHAR, (UCHAR*) colInf[colNo].schema, 128+1, &cb);
1689 GetData( 3, SQL_C_CHAR, (UCHAR*) colInf[colNo].tableName, DB_MAX_TABLE_NAME_LEN+1, &cb);
1690 GetData( 4, SQL_C_CHAR, (UCHAR*) colInf[colNo].colName, DB_MAX_COLUMN_NAME_LEN+1, &cb);
1691 GetData( 5, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].sqlDataType, 0, &cb);
1692 GetData( 6, SQL_C_CHAR, (UCHAR*) colInf[colNo].typeName, 128+1, &cb);
1693 GetData( 7, SQL_C_SLONG, (UCHAR*) &colInf[colNo].columnSize, 0, &cb);
1694 GetData( 8, SQL_C_SLONG, (UCHAR*) &colInf[colNo].bufferLength, 0, &cb);
1695 GetData( 9, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].decimalDigits,0, &cb);
1696 GetData(10, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].numPrecRadix, 0, &cb);
1697 GetData(11, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].nullable, 0, &cb);
1698 GetData(12, SQL_C_CHAR, (UCHAR*) colInf[colNo].remarks, 254+1, &cb);
1699
1700 // Determine the wxDb data type that is used to represent the native data type of this data source
1701 colInf[colNo].dbDataType = 0;
1702 if (!wxStricmp(typeInfVarchar.TypeName,colInf[colNo].typeName))
1703 {
1704 if (colInf[colNo].columnSize < 1)
1705 {
1706 // IODBC does not return a correct columnSize, so we set
1707 // columnSize = bufferLength if no column size was returned
1708 colInf[colNo].columnSize = colInf[colNo].bufferLength;
1709 }
1710 colInf[colNo].dbDataType = DB_DATA_TYPE_VARCHAR;
1711 }
1712 else if (!wxStricmp(typeInfInteger.TypeName,colInf[colNo].typeName))
1713 colInf[colNo].dbDataType = DB_DATA_TYPE_INTEGER;
1714 else if (!wxStricmp(typeInfFloat.TypeName,colInf[colNo].typeName))
1715 colInf[colNo].dbDataType = DB_DATA_TYPE_FLOAT;
1716 else if (!wxStricmp(typeInfDate.TypeName,colInf[colNo].typeName))
1717 colInf[colNo].dbDataType = DB_DATA_TYPE_DATE;
1718
1719 colNo++;
1720 }
1721 }
1722 }
1723 if (retcode != SQL_NO_DATA_FOUND)
1724 { // Error occured, abort
1725 DispAllErrors(henv, hdbc, hstmt);
1726 if (colInf)
1727 delete [] colInf;
1728 SQLFreeStmt(hstmt, SQL_CLOSE);
1729 return(0);
1730 }
1731 }
1732 }
1733
1734 SQLFreeStmt(hstmt, SQL_CLOSE);
1735 return colInf;
1736
1737 } // wxDb::GetColumns()
1738
1739
1740 /********** wxDb::GetColumns() **********/
1741
1742 wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID)
1743 //
1744 // Same as the above GetColumns() function except this one gets columns
1745 // only for a single table, and if 'numCols' is not NULL, the number of
1746 // columns stored in the returned wxDbColInf is set in '*numCols'
1747 //
1748 // userID is evaluated in the following manner:
1749 // userID == NULL ... UserID is ignored
1750 // userID == "" ... UserID set equal to 'this->uid'
1751 // userID != "" ... UserID set equal to 'userID'
1752 //
1753 // NOTE: ALL column bindings associated with this wxDb instance are unbound
1754 // by this function. This function should use its own wxDb instance
1755 // to avoid undesired unbinding of columns.
1756
1757 {
1758 int noCols = 0;
1759 int colNo = 0;
1760 wxDbColInf *colInf = 0;
1761
1762 RETCODE retcode;
1763 SDWORD cb;
1764
1765 wxString UserID;
1766 wxString TableName;
1767
1768 if (userID)
1769 {
1770 if (!wxStrlen(userID))
1771 UserID = uid;
1772 else
1773 UserID = userID;
1774 }
1775 else
1776 UserID = "";
1777
1778 // dBase does not use user names, and some drivers fail if you try to pass one
1779 if (Dbms() == dbmsDBASE)
1780 UserID = "";
1781
1782 // Oracle user names may only be in uppercase, so force
1783 // the name to uppercase
1784 if (Dbms() == dbmsORACLE)
1785 UserID = UserID.Upper();
1786
1787 // Pass 1 - Determine how many columns there are.
1788 // Pass 2 - Allocate the wxDbColInf array and fill in
1789 // the array with the column information.
1790 int pass;
1791 for (pass = 1; pass <= 2; pass++)
1792 {
1793 if (pass == 2)
1794 {
1795 if (noCols == 0) // Probably a bogus table name(s)
1796 break;
1797 // Allocate n wxDbColInf objects to hold the column information
1798 colInf = new wxDbColInf[noCols+1];
1799 if (!colInf)
1800 break;
1801 // Mark the end of the array
1802 wxStrcpy(colInf[noCols].tableName, wxT(""));
1803 wxStrcpy(colInf[noCols].colName, wxT(""));
1804 colInf[noCols].sqlDataType = 0;
1805 }
1806
1807 TableName = tableName;
1808 // Oracle table names are uppercase only, so force
1809 // the name to uppercase just in case programmer forgot to do this
1810 if (Dbms() == dbmsORACLE)
1811 TableName = TableName.Upper();
1812
1813 SQLFreeStmt(hstmt, SQL_CLOSE);
1814
1815 // MySQL and Access cannot accept a user name when looking up column names, so we
1816 // use the call below that leaves out the user name
1817 if (wxStrcmp(UserID.c_str(),wxT("")) &&
1818 Dbms() != dbmsMY_SQL &&
1819 Dbms() != dbmsACCESS)
1820 {
1821 retcode = SQLColumns(hstmt,
1822 NULL, 0, // All qualifiers
1823 (UCHAR *) UserID.c_str(), SQL_NTS, // Owner
1824 (UCHAR *) TableName.c_str(), SQL_NTS,
1825 NULL, 0); // All columns
1826 }
1827 else
1828 {
1829 retcode = SQLColumns(hstmt,
1830 NULL, 0, // All qualifiers
1831 NULL, 0, // Owner
1832 (UCHAR *) TableName.c_str(), SQL_NTS,
1833 NULL, 0); // All columns
1834 }
1835 if (retcode != SQL_SUCCESS)
1836 { // Error occured, abort
1837 DispAllErrors(henv, hdbc, hstmt);
1838 if (colInf)
1839 delete [] colInf;
1840 SQLFreeStmt(hstmt, SQL_CLOSE);
1841 if (numCols)
1842 *numCols = 0;
1843 return(0);
1844 }
1845
1846 while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
1847 {
1848 if (pass == 1) // First pass, just add up the number of columns
1849 noCols++;
1850 else // Pass 2; Fill in the array of structures
1851 {
1852 if (colNo < noCols) // Some extra error checking to prevent memory overwrites
1853 {
1854 // NOTE: Only the ODBC 1.x fields are retrieved
1855 GetData( 1, SQL_C_CHAR, (UCHAR*) colInf[colNo].catalog, 128+1, &cb);
1856 GetData( 2, SQL_C_CHAR, (UCHAR*) colInf[colNo].schema, 128+1, &cb);
1857 GetData( 3, SQL_C_CHAR, (UCHAR*) colInf[colNo].tableName, DB_MAX_TABLE_NAME_LEN+1, &cb);
1858 GetData( 4, SQL_C_CHAR, (UCHAR*) colInf[colNo].colName, DB_MAX_COLUMN_NAME_LEN+1, &cb);
1859 GetData( 5, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].sqlDataType, 0, &cb);
1860 GetData( 6, SQL_C_CHAR, (UCHAR*) colInf[colNo].typeName, 128+1, &cb);
1861 GetData( 7, SQL_C_SLONG, (UCHAR*) &colInf[colNo].columnSize, 0, &cb);
1862 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
1863 GetData( 8, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].bufferLength, 0, &cb);
1864 GetData( 9, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].decimalDigits,0, &cb);
1865 GetData(10, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].numPrecRadix, 0, &cb);
1866 GetData(11, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].nullable, 0, &cb);
1867 GetData(12, SQL_C_CHAR, (UCHAR*) colInf[colNo].remarks, 254+1, &cb);
1868 // Start Values for Primary/Foriegn Key (=No)
1869 colInf[colNo].PkCol = 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
1870 colInf[colNo].PkTableName[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
1871 colInf[colNo].FkCol = 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
1872 colInf[colNo].FkTableName[0] = 0; // Foreign key table name
1873
1874 // BJO 20000428 : Virtuoso returns type names with upper cases!
1875 if (Dbms() == dbmsVIRTUOSO)
1876 {
1877 wxString s = colInf[colNo].typeName;
1878 s = s.MakeLower();
1879 wxStrcmp(colInf[colNo].typeName, s.c_str());
1880 }
1881
1882 // Determine the wxDb data type that is used to represent the native data type of this data source
1883 colInf[colNo].dbDataType = 0;
1884 if (!wxStricmp(typeInfVarchar.TypeName,colInf[colNo].typeName))
1885 {
1886 if (colInf[colNo].columnSize < 1)
1887 {
1888 // IODBC does not return a correct columnSize, so we set
1889 // columnSize = bufferLength if no column size was returned
1890 colInf[colNo].columnSize = colInf[colNo].bufferLength;
1891 }
1892 colInf[colNo].dbDataType = DB_DATA_TYPE_VARCHAR;
1893 }
1894 else if (!wxStricmp(typeInfInteger.TypeName,colInf[colNo].typeName))
1895 colInf[colNo].dbDataType = DB_DATA_TYPE_INTEGER;
1896 else if (!wxStricmp(typeInfFloat.TypeName,colInf[colNo].typeName))
1897 colInf[colNo].dbDataType = DB_DATA_TYPE_FLOAT;
1898 else if (!wxStricmp(typeInfDate.TypeName,colInf[colNo].typeName))
1899 colInf[colNo].dbDataType = DB_DATA_TYPE_DATE;
1900
1901 colNo++;
1902 }
1903 }
1904 }
1905 if (retcode != SQL_NO_DATA_FOUND)
1906 { // Error occured, abort
1907 DispAllErrors(henv, hdbc, hstmt);
1908 if (colInf)
1909 delete [] colInf;
1910 SQLFreeStmt(hstmt, SQL_CLOSE);
1911 if (numCols)
1912 *numCols = 0;
1913 return(0);
1914 }
1915 }
1916
1917 SQLFreeStmt(hstmt, SQL_CLOSE);
1918
1919 // Store Primary and Foriegn Keys
1920 GetKeyFields(tableName,colInf,noCols);
1921
1922 if (numCols)
1923 *numCols = noCols;
1924 return colInf;
1925
1926 } // wxDb::GetColumns()
1927
1928 #else // New GetColumns
1929
1930
1931
1932 /*
1933
1934 BJO 20000503
1935
1936 These are tentative new GetColumns members:
1937
1938 - The first one (wxDbColInf *wxDb::GetColumns(char *tableName[], const char* userID)) calls
1939 the second implementation for each separate table before merging the results. This makes the
1940 code easier to maintain as only one member (the second) makes the real work
1941 - wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID) is a little bit improved
1942 - It doesn't anymore rely on the type-name to find out which database-type each column has
1943 - It ends by sorting the columns, so that they are returned in the same order they were created
1944
1945 */
1946
1947 typedef struct
1948 {
1949 int noCols;
1950 wxDbColInf *colInf;
1951 } _TableColumns;
1952
1953 wxDbColInf *wxDb::GetColumns(char *tableName[], const char* userID)
1954 {
1955 int i, j;
1956 // The last array element of the tableName[] argument must be zero (null).
1957 // This is how the end of the array is detected.
1958
1959 int noCols = 0;
1960
1961 // How many tables ?
1962 int tbl;
1963 for (tbl = 0 ; tableName[tbl]; tbl++);
1964
1965 // Create a table to maintain the columns for each separate table
1966 _TableColumns *TableColumns = new _TableColumns[tbl];
1967
1968 // Fill the table
1969 for (i = 0 ; i < tbl ; i++)
1970
1971 {
1972 TableColumns[i].colInf = GetColumns(tableName[i], &TableColumns[i].noCols, userID);
1973 if (TableColumns[i].colInf == NULL) return NULL;
1974 noCols += TableColumns[i].noCols;
1975 }
1976
1977 // Now merge all the separate table infos
1978 wxDbColInf *colInf = new wxDbColInf[noCols+1];
1979
1980 // Mark the end of the array
1981 wxStrcpy(colInf[noCols].tableName, wxT(""));
1982 wxStrcpy(colInf[noCols].colName, wxT(""));
1983 colInf[noCols].sqlDataType = 0;
1984
1985 // Merge ...
1986 int offset = 0;
1987
1988 for (i = 0 ; i < tbl ; i++)
1989 {
1990 for (j = 0 ; j < TableColumns[i].noCols ; j++)
1991 {
1992 colInf[offset++] = TableColumns[i].colInf[j];
1993 }
1994 }
1995
1996 delete [] TableColumns;
1997
1998 return colInf;
1999 }
2000
2001
2002 wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID)
2003 //
2004 // Same as the above GetColumns() function except this one gets columns
2005 // only for a single table, and if 'numCols' is not NULL, the number of
2006 // columns stored in the returned wxDbColInf is set in '*numCols'
2007 //
2008 // userID is evaluated in the following manner:
2009 // userID == NULL ... UserID is ignored
2010 // userID == "" ... UserID set equal to 'this->uid'
2011 // userID != "" ... UserID set equal to 'userID'
2012 //
2013 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2014 // by this function. This function should use its own wxDb instance
2015 // to avoid undesired unbinding of columns.
2016
2017 {
2018 SWORD noCols = 0;
2019 int colNo = 0;
2020 wxDbColInf *colInf = 0;
2021
2022 RETCODE retcode;
2023 SDWORD cb;
2024
2025 wxString UserID;
2026 wxString TableName;
2027
2028 if (userID)
2029 {
2030 if (!wxStrlen(userID))
2031 UserID = uid;
2032 else
2033 UserID = userID;
2034 }
2035 else
2036 UserID = "";
2037
2038 // dBase does not use user names, and some drivers fail if you try to pass one
2039 if (Dbms() == dbmsDBASE)
2040 UserID = "";
2041
2042 // Oracle user names may only be in uppercase, so force
2043 // the name to uppercase
2044 if (Dbms() == dbmsORACLE)
2045 UserID = UserID.Upper();
2046
2047 // Pass 1 - Determine how many columns there are.
2048 // Pass 2 - Allocate the wxDbColInf array and fill in
2049 // the array with the column information.
2050 int pass;
2051 for (pass = 1; pass <= 2; pass++)
2052 {
2053 if (pass == 2)
2054 {
2055 if (noCols == 0) // Probably a bogus table name(s)
2056 break;
2057 // Allocate n wxDbColInf objects to hold the column information
2058 colInf = new wxDbColInf[noCols+1];
2059 if (!colInf)
2060 break;
2061 // Mark the end of the array
2062 wxStrcpy(colInf[noCols].tableName, wxT(""));
2063 wxStrcpy(colInf[noCols].colName, wxT(""));
2064 colInf[noCols].sqlDataType = 0;
2065 }
2066
2067 TableName = tableName;
2068 // Oracle table names are uppercase only, so force
2069 // the name to uppercase just in case programmer forgot to do this
2070 if (Dbms() == dbmsORACLE)
2071 TableName = TableName.Upper();
2072
2073 SQLFreeStmt(hstmt, SQL_CLOSE);
2074
2075 // MySQL and Access cannot accept a user name when looking up column names, so we
2076 // use the call below that leaves out the user name
2077 if (wxStrcmp(UserID.c_str(),wxT("")) &&
2078 Dbms() != dbmsMY_SQL &&
2079 Dbms() != dbmsACCESS)
2080 {
2081 retcode = SQLColumns(hstmt,
2082 NULL, 0, // All qualifiers
2083 (UCHAR *) UserID.c_str(), SQL_NTS, // Owner
2084 (UCHAR *) TableName.c_str(), SQL_NTS,
2085 NULL, 0); // All columns
2086 }
2087 else
2088 {
2089 retcode = SQLColumns(hstmt,
2090 NULL, 0, // All qualifiers
2091 NULL, 0, // Owner
2092 (UCHAR *) TableName.c_str(), SQL_NTS,
2093 NULL, 0); // All columns
2094 }
2095 if (retcode != SQL_SUCCESS)
2096 { // Error occured, abort
2097 DispAllErrors(henv, hdbc, hstmt);
2098 if (colInf)
2099 delete [] colInf;
2100 SQLFreeStmt(hstmt, SQL_CLOSE);
2101 if (numCols)
2102 *numCols = 0;
2103 return(0);
2104 }
2105
2106 while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
2107 {
2108 if (pass == 1) // First pass, just add up the number of columns
2109 noCols++;
2110 else // Pass 2; Fill in the array of structures
2111 {
2112 if (colNo < noCols) // Some extra error checking to prevent memory overwrites
2113 {
2114 // NOTE: Only the ODBC 1.x fields are retrieved
2115 GetData( 1, SQL_C_CHAR, (UCHAR*) colInf[colNo].catalog, 128+1, &cb);
2116 GetData( 2, SQL_C_CHAR, (UCHAR*) colInf[colNo].schema, 128+1, &cb);
2117 GetData( 3, SQL_C_CHAR, (UCHAR*) colInf[colNo].tableName, DB_MAX_TABLE_NAME_LEN+1, &cb);
2118 GetData( 4, SQL_C_CHAR, (UCHAR*) colInf[colNo].colName, DB_MAX_COLUMN_NAME_LEN+1, &cb);
2119 GetData( 5, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].sqlDataType, 0, &cb);
2120 GetData( 6, SQL_C_CHAR, (UCHAR*) colInf[colNo].typeName, 128+1, &cb);
2121 GetData( 7, SQL_C_SLONG, (UCHAR*) &colInf[colNo].columnSize, 0, &cb);
2122 GetData( 8, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].bufferLength, 0, &cb);
2123 GetData( 9, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].decimalDigits,0, &cb);
2124 GetData(10, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].numPrecRadix, 0, &cb);
2125 GetData(11, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].nullable, 0, &cb);
2126 GetData(12, SQL_C_CHAR, (UCHAR*) colInf[colNo].remarks, 254+1, &cb);
2127 // Start Values for Primary/Foriegn Key (=No)
2128 colInf[colNo].PkCol = 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2129 colInf[colNo].PkTableName[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2130 colInf[colNo].FkCol = 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2131 colInf[colNo].FkTableName[0] = 0; // Foreign key table name
2132
2133 #ifdef _IODBC_
2134 // IODBC returns the columnSize in bufferLength.. (bug)
2135 colInf[colNo].columnSize = colInf[colNo].bufferLength;
2136 #endif
2137
2138 // Determine the wxDb data type that is used to represent the native data type of this data source
2139 colInf[colNo].dbDataType = 0;
2140 // Get the intern datatype
2141 switch (colInf[colNo].sqlDataType)
2142 {
2143 case SQL_VARCHAR:
2144 case SQL_CHAR:
2145 colInf[colNo].dbDataType = DB_DATA_TYPE_VARCHAR;
2146 break;
2147
2148 case SQL_TINYINT:
2149 case SQL_SMALLINT:
2150 case SQL_INTEGER:
2151 colInf[colNo].dbDataType = DB_DATA_TYPE_INTEGER;
2152 break;
2153 case SQL_DOUBLE:
2154 case SQL_DECIMAL:
2155 case SQL_NUMERIC:
2156 case SQL_FLOAT:
2157 case SQL_REAL:
2158 colInf[colNo].dbDataType = DB_DATA_TYPE_FLOAT;
2159 break;
2160 case SQL_DATE:
2161 colInf[colNo].dbDataType = DB_DATA_TYPE_DATE;
2162 break;
2163
2164
2165 #ifdef __WXDEBUG__
2166 default:
2167 wxString errMsg;
2168 errMsg.sprintf("SQL Data type %d currently not supported by wxWindows", colInf[colNo].sqlDataType);
2169 wxLogDebug(errMsg,wxT("ODBC DEBUG MESSAGE"));
2170 #endif
2171 }
2172
2173
2174
2175 colNo++;
2176 }
2177 }
2178 }
2179 if (retcode != SQL_NO_DATA_FOUND)
2180 { // Error occured, abort
2181 DispAllErrors(henv, hdbc, hstmt);
2182 if (colInf)
2183 delete [] colInf;
2184 SQLFreeStmt(hstmt, SQL_CLOSE);
2185 if (numCols)
2186 *numCols = 0;
2187 return(0);
2188 }
2189 }
2190
2191 SQLFreeStmt(hstmt, SQL_CLOSE);
2192
2193 // Store Primary and Foreign Keys
2194 GetKeyFields(tableName,colInf,noCols);
2195
2196
2197
2198 ///////////////////////////////////////////////////////////////////////////
2199 // Now sort the the columns in order to make them appear in the right order
2200 ///////////////////////////////////////////////////////////////////////////
2201
2202 // Build a generic SELECT statement which returns 0 rows
2203 wxString Stmt;
2204
2205 Stmt.sprintf("select * from %s where 0=1", tableName);
2206
2207 // Execute query
2208 if (SQLExecDirect(hstmt, (UCHAR FAR *) Stmt.c_str(), SQL_NTS) != SQL_SUCCESS)
2209 {
2210 DispAllErrors(henv, hdbc, hstmt);
2211 return NULL;
2212 }
2213
2214 // Get the number of result columns
2215 if (SQLNumResultCols (hstmt, &noCols) != SQL_SUCCESS)
2216 {
2217 DispAllErrors(henv, hdbc, hstmt);
2218 return NULL;
2219 }
2220
2221 if (noCols == 0) // Probably a bogus table name
2222 return NULL;
2223
2224 // Get the name
2225 int i;
2226 short colNum;
2227 UCHAR name[100];
2228 SWORD Sword;
2229 SDWORD Sdword;
2230 for (colNum = 0; colNum < noCols; colNum++)
2231 {
2232 if (SQLColAttributes(hstmt,colNum+1, SQL_COLUMN_NAME,
2233 name, sizeof(name),
2234 &Sword, &Sdword) != SQL_SUCCESS)
2235 {
2236 DispAllErrors(henv, hdbc, hstmt);
2237 return NULL;
2238 }
2239
2240 wxString Name1 = name;
2241 Name1 = Name1.Upper();
2242
2243 // Where is this name in the array ?
2244 for (i = colNum ; i < noCols ; i++)
2245 {
2246 wxString Name2 = colInf[i].colName;
2247 Name2 = Name2.Upper();
2248 if (Name2 == Name1)
2249 {
2250 if (colNum != i) // swap to sort
2251 {
2252 wxDbColInf tmpColInf = colInf[colNum];
2253 colInf[colNum] = colInf[i];
2254 colInf[i] = tmpColInf;
2255 }
2256 break;
2257 }
2258 }
2259 }
2260 SQLFreeStmt(hstmt, SQL_CLOSE);
2261
2262 ///////////////////////////////////////////////////////////////////////////
2263 // End sorting
2264 ///////////////////////////////////////////////////////////////////////////
2265
2266
2267
2268
2269 if (numCols)
2270 *numCols = noCols;
2271 return colInf;
2272
2273 } // wxDb::GetColumns()
2274
2275
2276 #endif
2277
2278
2279 /********** wxDb::GetColumnCount() **********/
2280 int wxDb::GetColumnCount(char *tableName, const char *userID)
2281 /*
2282 * Returns a count of how many columns are in a table.
2283 * If an error occurs in computing the number of columns
2284 * this function will return a -1 for the count
2285 *
2286 * userID is evaluated in the following manner:
2287 * userID == NULL ... UserID is ignored
2288 * userID == "" ... UserID set equal to 'this->uid'
2289 * userID != "" ... UserID set equal to 'userID'
2290 *
2291 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2292 * by this function. This function should use its own wxDb instance
2293 * to avoid undesired unbinding of columns.
2294 */
2295 {
2296 int noCols = 0;
2297
2298 RETCODE retcode;
2299
2300 wxString UserID;
2301 wxString TableName;
2302
2303 if (userID)
2304 {
2305 if (!wxStrlen(userID))
2306 UserID = uid;
2307 else
2308 UserID = userID;
2309 }
2310 else
2311 UserID = wxT("");
2312
2313 // dBase does not use user names, and some drivers fail if you try to pass one
2314 if (Dbms() == dbmsDBASE)
2315 UserID = wxT("");
2316
2317 // Oracle user names may only be in uppercase, so force
2318 // the name to uppercase
2319 if (Dbms() == dbmsORACLE)
2320 UserID = UserID.Upper();
2321
2322 {
2323 // Loop through each table name
2324 {
2325 TableName = tableName;
2326 // Oracle table names are uppercase only, so force
2327 // the name to uppercase just in case programmer forgot to do this
2328 if (Dbms() == dbmsORACLE)
2329 TableName = TableName.Upper();
2330
2331 SQLFreeStmt(hstmt, SQL_CLOSE);
2332
2333 // MySQL and Access cannot accept a user name when looking up column names, so we
2334 // use the call below that leaves out the user name
2335 if (wxStrcmp(UserID.c_str(),wxT("")) &&
2336 Dbms() != dbmsMY_SQL &&
2337 Dbms() != dbmsACCESS)
2338 {
2339 retcode = SQLColumns(hstmt,
2340 NULL, 0, // All qualifiers
2341 (UCHAR *) UserID.c_str(), SQL_NTS, // Owner
2342 (UCHAR *) TableName.c_str(), SQL_NTS,
2343 NULL, 0); // All columns
2344 }
2345 else
2346 {
2347 retcode = SQLColumns(hstmt,
2348 NULL, 0, // All qualifiers
2349 NULL, 0, // Owner
2350 (UCHAR *) TableName.c_str(), SQL_NTS,
2351 NULL, 0); // All columns
2352 }
2353 if (retcode != SQL_SUCCESS)
2354 { // Error occured, abort
2355 DispAllErrors(henv, hdbc, hstmt);
2356 SQLFreeStmt(hstmt, SQL_CLOSE);
2357 return(-1);
2358 }
2359
2360 // Count the columns
2361 while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
2362 noCols++;
2363
2364 if (retcode != SQL_NO_DATA_FOUND)
2365 { // Error occured, abort
2366 DispAllErrors(henv, hdbc, hstmt);
2367 SQLFreeStmt(hstmt, SQL_CLOSE);
2368 return(-1);
2369 }
2370 }
2371 }
2372
2373 SQLFreeStmt(hstmt, SQL_CLOSE);
2374 return noCols;
2375
2376 } // wxDb::GetColumnCount()
2377
2378
2379 /********** wxDb::GetCatalog() *******/
2380 wxDbInf *wxDb::GetCatalog(char *userID)
2381 /*
2382 * ---------------------------------------------------------------------
2383 * -- 19991203 : mj10777@gmx.net : Create ------
2384 * -- : Creates a wxDbInf with Tables / Cols Array ------
2385 * -- : uses SQLTables and fills pTableInf; ------
2386 * -- : pColInf is set to NULL and numCols to 0; ------
2387 * -- : returns pDbInf (wxDbInf) ------
2388 * -- - if unsuccesfull (pDbInf == NULL) ------
2389 * -- : pColInf can be filled with GetColumns(..); ------
2390 * -- : numCols can be filled with GetColumnCount(..); ------
2391 * ---------------------------------------------------------------------
2392 *
2393 * userID is evaluated in the following manner:
2394 * userID == NULL ... UserID is ignored
2395 * userID == "" ... UserID set equal to 'this->uid'
2396 * userID != "" ... UserID set equal to 'userID'
2397 *
2398 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2399 * by this function. This function should use its own wxDb instance
2400 * to avoid undesired unbinding of columns.
2401 */
2402 {
2403 wxDbInf *pDbInf = NULL; // Array of catalog entries
2404 int noTab = 0; // Counter while filling table entries
2405 int pass;
2406 RETCODE retcode;
2407 SDWORD cb;
2408 // char tblNameSave[DB_MAX_TABLE_NAME_LEN+1];
2409 wxString tblNameSave;
2410
2411 wxString UserID;
2412
2413 if (userID)
2414 {
2415 if (!wxStrlen(userID))
2416 UserID = uid;
2417 else
2418 UserID = userID;
2419 }
2420 else
2421 UserID = wxT("");
2422
2423 // dBase does not use user names, and some drivers fail if you try to pass one
2424 if (Dbms() == dbmsDBASE)
2425 UserID = wxT("");
2426
2427 // Oracle user names may only be in uppercase, so force
2428 // the name to uppercase
2429 if (Dbms() == dbmsORACLE)
2430 UserID = UserID.Upper();
2431
2432 //-------------------------------------------------------------
2433 pDbInf = new wxDbInf; // Create the Database Arrray
2434 pDbInf->catalog[0] = 0;
2435 pDbInf->schema[0] = 0;
2436 pDbInf->numTables = 0; // Counter for Tables
2437 pDbInf->pTableInf = NULL; // Array of Tables
2438 //-------------------------------------------------------------
2439 // Table Information
2440 // Pass 1 - Determine how many Tables there are.
2441 // Pass 2 - Create the Table array and fill it
2442 // - Create the Cols array = NULL
2443 //-------------------------------------------------------------
2444
2445
2446 for (pass = 1; pass <= 2; pass++)
2447 {
2448 SQLFreeStmt(hstmt, SQL_CLOSE); // Close if Open
2449 tblNameSave = wxT("");
2450
2451 if (wxStrcmp(UserID.c_str(),wxT("")) &&
2452 Dbms() != dbmsMY_SQL &&
2453 Dbms() != dbmsACCESS)
2454 {
2455
2456 retcode = SQLTables(hstmt,
2457 NULL, 0, // All qualifiers
2458 (UCHAR *) UserID.c_str(), SQL_NTS, // User specified
2459 NULL, 0, // All tables
2460 NULL, 0); // All columns
2461 }
2462 else
2463 {
2464
2465 retcode = SQLTables(hstmt,
2466 NULL, 0, // All qualifiers
2467 NULL, 0, // User specified
2468 NULL, 0, // All tables
2469 NULL, 0); // All columns
2470 }
2471
2472 if (retcode != SQL_SUCCESS)
2473 {
2474
2475 DispAllErrors(henv, hdbc, hstmt);
2476 pDbInf = NULL;
2477 SQLFreeStmt(hstmt, SQL_CLOSE);
2478 return pDbInf;
2479 }
2480
2481 while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS) // Table Information
2482 {
2483
2484 if (pass == 1) // First pass, just count the Tables
2485 {
2486 if (pDbInf->numTables == 0)
2487 {
2488 GetData( 1, SQL_C_CHAR, (UCHAR*) pDbInf->catalog, 128+1, &cb);
2489 GetData( 2, SQL_C_CHAR, (UCHAR*) pDbInf->schema, 128+1, &cb);
2490 }
2491 pDbInf->numTables++; // Counter for Tables
2492 } // if (pass == 1)
2493 if (pass == 2) // Create and fill the Table entries
2494 {
2495 if (pDbInf->pTableInf == NULL) // Has the Table Array been created
2496 { // no, then create the Array
2497 pDbInf->pTableInf = new wxDbTableInf[pDbInf->numTables];
2498 for (noTab=0;noTab<pDbInf->numTables;noTab++)
2499 {
2500 (pDbInf->pTableInf+noTab)->tableName[0] = 0;
2501 (pDbInf->pTableInf+noTab)->tableType[0] = 0;
2502 (pDbInf->pTableInf+noTab)->tableRemarks[0] = 0;
2503 (pDbInf->pTableInf+noTab)->numCols = 0;
2504 (pDbInf->pTableInf+noTab)->pColInf = NULL;
2505 }
2506 noTab = 0;
2507 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
2508
2509 GetData( 3, SQL_C_CHAR, (UCHAR*) (pDbInf->pTableInf+noTab)->tableName, DB_MAX_TABLE_NAME_LEN+1, &cb);
2510 GetData( 4, SQL_C_CHAR, (UCHAR*) (pDbInf->pTableInf+noTab)->tableType, 30+1, &cb);
2511 GetData( 5, SQL_C_CHAR, (UCHAR*) (pDbInf->pTableInf+noTab)->tableRemarks, 254+1, &cb);
2512
2513 noTab++;
2514 } // if (pass == 2) We now know the amount of Tables
2515 } // while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
2516 } // for (pass = 1; pass <= 2; pass++)
2517 SQLFreeStmt(hstmt, SQL_CLOSE);
2518
2519 // Query how many columns are in each table
2520 for (noTab=0;noTab<pDbInf->numTables;noTab++)
2521 {
2522 (pDbInf->pTableInf+noTab)->numCols = GetColumnCount((pDbInf->pTableInf+noTab)->tableName,UserID);
2523 }
2524 return pDbInf;
2525 } // wxDb::GetCatalog()
2526
2527
2528 /********** wxDb::Catalog() **********/
2529 bool wxDb::Catalog(const char *userID, const char *fileName)
2530 /*
2531 * Creates the text file specified in 'filename' which will contain
2532 * a minimal data dictionary of all tables accessible by the user specified
2533 * in 'userID'
2534 *
2535 * userID is evaluated in the following manner:
2536 * userID == NULL ... UserID is ignored
2537 * userID == "" ... UserID set equal to 'this->uid'
2538 * userID != "" ... UserID set equal to 'userID'
2539 *
2540 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2541 * by this function. This function should use its own wxDb instance
2542 * to avoid undesired unbinding of columns.
2543 */
2544 {
2545 assert(fileName && wxStrlen(fileName));
2546
2547 RETCODE retcode;
2548 SDWORD cb;
2549 char tblName[DB_MAX_TABLE_NAME_LEN+1];
2550 wxString tblNameSave;
2551 char colName[DB_MAX_COLUMN_NAME_LEN+1];
2552 SWORD sqlDataType;
2553 char typeName[30+1];
2554 SWORD precision, length;
2555
2556 wxString UserID;
2557
2558 FILE *fp = fopen(fileName,"wt");
2559 if (fp == NULL)
2560 return(FALSE);
2561
2562 SQLFreeStmt(hstmt, SQL_CLOSE);
2563
2564 if (userID)
2565 {
2566 if (!wxStrlen(userID))
2567 UserID = uid;
2568 else
2569 UserID = userID;
2570 }
2571 else
2572 UserID = wxT("");
2573
2574 // dBase does not use user names, and some drivers fail if you try to pass one
2575 if (Dbms() == dbmsDBASE)
2576 UserID = wxT("");
2577
2578 // Oracle user names may only be in uppercase, so force
2579 // the name to uppercase
2580 if (Dbms() == dbmsORACLE)
2581 UserID = UserID.Upper();
2582
2583 if (wxStrcmp(UserID.c_str(),wxT("")) &&
2584 Dbms() != dbmsMY_SQL &&
2585 Dbms() != dbmsACCESS)
2586 {
2587 retcode = SQLColumns(hstmt,
2588 NULL, 0, // All qualifiers
2589 (UCHAR *) UserID.c_str(), SQL_NTS, // User specified
2590 NULL, 0, // All tables
2591 NULL, 0); // All columns
2592 }
2593 else
2594 {
2595 retcode = SQLColumns(hstmt,
2596 NULL, 0, // All qualifiers
2597 NULL, 0, // User specified
2598 NULL, 0, // All tables
2599 NULL, 0); // All columns
2600 }
2601 if (retcode != SQL_SUCCESS)
2602 {
2603 DispAllErrors(henv, hdbc, hstmt);
2604 fclose(fp);
2605 return(FALSE);
2606 }
2607
2608 wxString outStr;
2609 tblNameSave = wxT("");
2610 int cnt = 0;
2611
2612 while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
2613 {
2614 if (wxStrcmp(tblName,tblNameSave.c_str()))
2615 {
2616 if (cnt)
2617 fputs("\n", fp);
2618 fputs("================================ ", fp);
2619 fputs("================================ ", fp);
2620 fputs("===================== ", fp);
2621 fputs("========= ", fp);
2622 fputs("=========\n", fp);
2623 outStr.sprintf(wxT("%-32s %-32s %-21s %9s %9s\n"),
2624 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
2625 fputs(outStr.c_str(), fp);
2626 fputs("================================ ", fp);
2627 fputs("================================ ", fp);
2628 fputs("===================== ", fp);
2629 fputs("========= ", fp);
2630 fputs("=========\n", fp);
2631 tblNameSave = tblName;
2632 }
2633
2634 GetData(3,SQL_C_CHAR, (UCHAR *)tblName, DB_MAX_TABLE_NAME_LEN+1, &cb);
2635 GetData(4,SQL_C_CHAR, (UCHAR *)colName, DB_MAX_COLUMN_NAME_LEN+1,&cb);
2636 GetData(5,SQL_C_SSHORT,(UCHAR *)&sqlDataType,0, &cb);
2637 GetData(6,SQL_C_CHAR, (UCHAR *)typeName, sizeof(typeName), &cb);
2638 GetData(7,SQL_C_SSHORT,(UCHAR *)&precision, 0, &cb);
2639 GetData(8,SQL_C_SSHORT,(UCHAR *)&length, 0, &cb);
2640
2641 outStr.sprintf("%-32s %-32s (%04d)%-15s %9d %9d\n",
2642 tblName, colName, sqlDataType, typeName, precision, length);
2643 if (fputs(outStr.c_str(), fp) == EOF)
2644 {
2645 SQLFreeStmt(hstmt, SQL_CLOSE);
2646 fclose(fp);
2647 return(FALSE);
2648 }
2649 cnt++;
2650 }
2651
2652 if (retcode != SQL_NO_DATA_FOUND)
2653 DispAllErrors(henv, hdbc, hstmt);
2654
2655 SQLFreeStmt(hstmt, SQL_CLOSE);
2656
2657 fclose(fp);
2658 return(retcode == SQL_NO_DATA_FOUND);
2659
2660 } // wxDb::Catalog()
2661
2662
2663 bool wxDb::TableExists(const char *tableName, const char *userID, const char *tablePath)
2664 /*
2665 * Table name can refer to a table, view, alias or synonym. Returns true
2666 * if the object exists in the database. This function does not indicate
2667 * whether or not the user has privleges to query or perform other functions
2668 * on the table.
2669 *
2670 * userID is evaluated in the following manner:
2671 * userID == NULL ... UserID is ignored
2672 * userID == "" ... UserID set equal to 'this->uid'
2673 * userID != "" ... UserID set equal to 'userID'
2674 */
2675 {
2676 wxString UserID;
2677 wxString TableName;
2678
2679 assert(tableName && wxStrlen(tableName));
2680
2681 if (Dbms() == dbmsDBASE)
2682 {
2683
2684 wxString dbName;
2685 if (tablePath && wxStrlen(tablePath))
2686 dbName.sprintf("%s\\%s.dbf",tablePath,tableName);
2687 else
2688 dbName.sprintf("%s.dbf",tableName);
2689
2690 bool exists;
2691 exists = wxFileExists(dbName.c_str());
2692 return exists;
2693 }
2694
2695 if (userID)
2696 {
2697 if (!wxStrlen(userID))
2698 UserID = uid;
2699 else
2700 UserID = userID;
2701 }
2702 else
2703 UserID = "";
2704
2705 // Oracle user names may only be in uppercase, so force
2706 // the name to uppercase
2707 if (Dbms() == dbmsORACLE)
2708 UserID = UserID.Upper();
2709
2710 TableName = tableName;
2711 // Oracle table names are uppercase only, so force
2712 // the name to uppercase just in case programmer forgot to do this
2713 if (Dbms() == dbmsORACLE)
2714 TableName = TableName.Upper();
2715
2716 SQLFreeStmt(hstmt, SQL_CLOSE);
2717 RETCODE retcode;
2718
2719 // MySQL and Access cannot accept a user name when looking up table names, so we
2720 // use the call below that leaves out the user name
2721 if (wxStrcmp(UserID,"") &&
2722 Dbms() != dbmsMY_SQL &&
2723 Dbms() != dbmsACCESS)
2724 {
2725 retcode = SQLTables(hstmt,
2726 NULL, 0, // All qualifiers
2727 (UCHAR *) UserID.c_str(), SQL_NTS, // All owners
2728 (UCHAR FAR *)TableName.c_str(), SQL_NTS,
2729 NULL, 0); // All table types
2730 }
2731 else
2732 {
2733 retcode = SQLTables(hstmt,
2734 NULL, 0, // All qualifiers
2735 NULL, 0, // All owners
2736 (UCHAR FAR *)TableName.c_str(), SQL_NTS,
2737 NULL, 0); // All table types
2738 }
2739 if (retcode != SQL_SUCCESS)
2740 return(DispAllErrors(henv, hdbc, hstmt));
2741
2742 retcode = SQLFetch(hstmt);
2743 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
2744 {
2745 SQLFreeStmt(hstmt, SQL_CLOSE);
2746 return(DispAllErrors(henv, hdbc, hstmt));
2747 }
2748
2749 SQLFreeStmt(hstmt, SQL_CLOSE);
2750 return(TRUE);
2751
2752 } // wxDb::TableExists()
2753
2754
2755 /********** wxDb::SetSqlLogging() **********/
2756 bool wxDb::SetSqlLogging(wxDbSqlLogState state, const char *filename, bool append)
2757 {
2758 assert(state == sqlLogON || state == sqlLogOFF);
2759 assert(state == sqlLogOFF || filename);
2760
2761 if (state == sqlLogON)
2762 {
2763 if (fpSqlLog == 0)
2764 {
2765 fpSqlLog = fopen(filename, (append ? "at" : "wt"));
2766 if (fpSqlLog == NULL)
2767 return(FALSE);
2768 }
2769 }
2770 else // sqlLogOFF
2771 {
2772 if (fpSqlLog)
2773 {
2774 if (fclose(fpSqlLog))
2775 return(FALSE);
2776 fpSqlLog = 0;
2777 }
2778 }
2779
2780 sqlLogState = state;
2781 return(TRUE);
2782
2783 } // wxDb::SetSqlLogging()
2784
2785
2786 /********** wxDb::WriteSqlLog() **********/
2787 bool wxDb::WriteSqlLog(const wxChar *logMsg)
2788 {
2789 assert(logMsg);
2790
2791 if (fpSqlLog == 0 || sqlLogState == sqlLogOFF)
2792 return(FALSE);
2793
2794 if (fputs("\n", fpSqlLog) == EOF) return(FALSE);
2795 if (fputs(logMsg, fpSqlLog) == EOF) return(FALSE);
2796 if (fputs("\n", fpSqlLog) == EOF) return(FALSE);
2797
2798 return(TRUE);
2799
2800 } // wxDb::WriteSqlLog()
2801
2802
2803 /********** wxDb::Dbms() **********/
2804 wxDBMS wxDb::Dbms(void)
2805 /*
2806 * Be aware that not all database engines use the exact same syntax, and not
2807 * every ODBC compliant database is compliant to the same level of compliancy.
2808 * Some manufacturers support the minimum Level 1 compliancy, and others up
2809 * through Level 3. Others support subsets of features for levels above 1.
2810 *
2811 * If you find an inconsistency between the wxDb class and a specific database
2812 * engine, and an identifier to this section, and special handle the database in
2813 * the area where behavior is non-conforming with the other databases.
2814 *
2815 *
2816 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
2817 * ---------------------------------------------------
2818 *
2819 * ORACLE
2820 * - Currently the only database supported by the class to support VIEWS
2821 *
2822 * DBASE
2823 * - Does not support the SQL_TIMESTAMP structure
2824 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
2825 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
2826 * is TRUE. The user must create ALL indexes from their program.
2827 * - Table names can only be 8 characters long
2828 * - Column names can only be 10 characters long
2829 *
2830 * SYBASE (all)
2831 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
2832 * after every table name involved in the query/join if that tables matching record(s)
2833 * are to be locked
2834 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
2835 *
2836 * SYBASE (Enterprise)
2837 * - If a column is part of the Primary Key, the column cannot be NULL
2838 *
2839 * MY_SQL
2840 * - If a column is part of the Primary Key, the column cannot be NULL
2841 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
2842 *
2843 * POSTGRES
2844 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
2845 *
2846 *
2847 */
2848 {
2849 wxChar baseName[25+1];
2850 wxStrncpy(baseName,dbInf.dbmsName,25);
2851
2852 // BJO 20000428 : add support for Virtuoso
2853 if (!wxStricmp(dbInf.dbmsName,"OpenLink Virtuoso VDBMS"))
2854 return(dbmsVIRTUOSO);
2855
2856
2857 if (!wxStricmp(dbInf.dbmsName,"Adaptive Server Anywhere"))
2858 return(dbmsSYBASE_ASA);
2859
2860 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
2861 // connected through an OpenLink driver.
2862 // Is it also returned by Sybase Adapatitve server?
2863 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
2864 if (!wxStricmp(dbInf.dbmsName,"SQL Server"))
2865 if (!wxStrncmp(dbInf.driverName, "oplodbc", 7) ||
2866 !wxStrncmp(dbInf.driverName, "OLOD", 4))
2867 return dbmsMS_SQL_SERVER; else return dbmsSYBASE_ASE;
2868
2869
2870 if (!wxStricmp(dbInf.dbmsName,"Microsoft SQL Server"))
2871 return(dbmsMS_SQL_SERVER);
2872 if (!wxStricmp(dbInf.dbmsName,"MySQL"))
2873 return(dbmsMY_SQL);
2874 if (!wxStricmp(dbInf.dbmsName,"PostgreSQL")) // v6.5.0
2875 return(dbmsPOSTGRES);
2876
2877 baseName[8] = 0;
2878 if (!wxStricmp(baseName,"Informix"))
2879 return(dbmsINFORMIX);
2880
2881
2882 baseName[6] = 0;
2883 if (!wxStricmp(baseName,"Oracle"))
2884 return(dbmsORACLE);
2885 if (!wxStricmp(dbInf.dbmsName,"ACCESS"))
2886 return(dbmsACCESS);
2887 if (!wxStricmp(dbInf.dbmsName,"MySQL"))
2888 return(dbmsMY_SQL);
2889 if (!wxStricmp(baseName,"Sybase"))
2890 return(dbmsSYBASE_ASE);
2891
2892 baseName[5] = 0;
2893 if (!wxStricmp(baseName,"DBASE"))
2894 return(dbmsDBASE);
2895
2896 return(dbmsUNIDENTIFIED);
2897 } // wxDb::Dbms()
2898
2899
2900 /********** wxDbGetConnection() **********/
2901 wxDb WXDLLEXPORT *wxDbGetConnection(wxDbConnectInf *pDbConfig, bool FwdOnlyCursors)
2902 {
2903 wxDbList *pList;
2904
2905 // Scan the linked list searching for an available database connection
2906 // that's already been opened but is currently not in use.
2907 for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
2908 {
2909 // The database connection must be for the same datasource
2910 // name and must currently not be in use.
2911 if (pList->Free && (! wxStrcmp(pDbConfig->Dsn, pList->Dsn))) // Found a free connection
2912 {
2913 pList->Free = FALSE;
2914 return(pList->PtrDb);
2915 }
2916 }
2917
2918 // No available connections. A new connection must be made and
2919 // appended to the end of the linked list.
2920 if (PtrBegDbList)
2921 {
2922 // Find the end of the list
2923 for (pList = PtrBegDbList; pList->PtrNext; pList = pList->PtrNext);
2924 // Append a new list item
2925 pList->PtrNext = new wxDbList;
2926 pList->PtrNext->PtrPrev = pList;
2927 pList = pList->PtrNext;
2928 }
2929 else // Empty list
2930 {
2931 // Create the first node on the list
2932 pList = PtrBegDbList = new wxDbList;
2933 pList->PtrPrev = 0;
2934 }
2935
2936 // Initialize new node in the linked list
2937 pList->PtrNext = 0;
2938 pList->Free = FALSE;
2939 wxStrcpy(pList->Dsn, pDbConfig->Dsn);
2940 pList->PtrDb = new wxDb(pDbConfig->Henv,FwdOnlyCursors);
2941
2942 // Connect to the datasource
2943 if (pList->PtrDb->Open(pDbConfig->Dsn, pDbConfig->Uid, pDbConfig->AuthStr))
2944 {
2945 pList->PtrDb->SetSqlLogging(SQLLOGstate,SQLLOGfn,TRUE);
2946 return(pList->PtrDb);
2947 }
2948 else // Unable to connect, destroy list item
2949 {
2950 if (pList->PtrPrev)
2951 pList->PtrPrev->PtrNext = 0;
2952 else
2953 PtrBegDbList = 0; // Empty list again
2954 pList->PtrDb->CommitTrans(); // Commit any open transactions on wxDb object
2955 pList->PtrDb->Close(); // Close the wxDb object
2956 delete pList->PtrDb; // Deletes the wxDb object
2957 delete pList; // Deletes the linked list object
2958 return(0);
2959 }
2960
2961 } // wxDbGetConnection()
2962
2963
2964 /********** wxDbFreeConnection() **********/
2965 bool WXDLLEXPORT wxDbFreeConnection(wxDb *pDb)
2966 {
2967 wxDbList *pList;
2968
2969 // Scan the linked list searching for the database connection
2970 for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
2971 {
2972 if (pList->PtrDb == pDb) // Found it!!!
2973 return(pList->Free = TRUE);
2974 }
2975
2976 // Never found the database object, return failure
2977 return(FALSE);
2978
2979 } // wxDbFreeConnection()
2980
2981
2982 /********** wxDbCloseConnections() **********/
2983 void WXDLLEXPORT wxDbCloseConnections(void)
2984 {
2985 wxDbList *pList, *pNext;
2986
2987 // Traverse the linked list closing database connections and freeing memory as I go.
2988 for (pList = PtrBegDbList; pList; pList = pNext)
2989 {
2990 pNext = pList->PtrNext; // Save the pointer to next
2991 pList->PtrDb->CommitTrans(); // Commit any open transactions on wxDb object
2992 pList->PtrDb->Close(); // Close the wxDb object
2993 delete pList->PtrDb; // Deletes the wxDb object
2994 delete pList; // Deletes the linked list object
2995 }
2996
2997 // Mark the list as empty
2998 PtrBegDbList = 0;
2999
3000 } // wxDbCloseConnections()
3001
3002
3003 /********** wxDbNumberConnectionsInUse() **********/
3004 int WXDLLEXPORT wxDbConnectionsInUse(void)
3005 {
3006 wxDbList *pList;
3007 int cnt = 0;
3008
3009 // Scan the linked list counting db connections that are currently in use
3010 for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
3011 {
3012 if (pList->Free == FALSE)
3013 cnt++;
3014 }
3015
3016 return(cnt);
3017
3018 } // wxDbConnectionsInUse()
3019
3020
3021 /********** wxDbSqlLog() **********/
3022 bool wxDbSqlLog(wxDbSqlLogState state, const wxChar *filename)
3023 {
3024 bool append = FALSE;
3025 wxDbList *pList;
3026
3027 for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
3028 {
3029 if (!pList->PtrDb->SetSqlLogging(state,filename,append))
3030 return(FALSE);
3031 append = TRUE;
3032 }
3033
3034 SQLLOGstate = state;
3035 wxStrcpy(SQLLOGfn,filename);
3036
3037 return(TRUE);
3038
3039 } // wxDbSqlLog()
3040
3041
3042 #if 0
3043 /********** wxDbCreateDataSource() **********/
3044 int wxDbCreateDataSource(const char *driverName, const char *dsn, const char *description,
3045 bool sysDSN, const char *defDir, wxWindow *parent)
3046 /*
3047 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3048 * Very rudimentary creation of an ODBC data source.
3049 */
3050 {
3051 int result = FALSE;
3052
3053 #ifdef __WXMSW__
3054 int dsnLocation;
3055 wxString setupStr;
3056
3057 if (sysDSN)
3058 dsnLocation = ODBC_ADD_SYS_DSN;
3059 else
3060 dsnLocation = ODBC_ADD_DSN;
3061
3062 // NOTE: The decimal 2 is an invalid character in all keyword pairs
3063 // so that is why I used it, as wxString does not deal well with
3064 // embedded nulls in strings
3065 setupStr.sprintf("DSN=%s%cDescription=%s%cDefaultDir=%s%c",dsn,2,description,2,defDir,2);
3066
3067 // Replace the separator from above with the '\0' seperator needed
3068 // by the SQLConfigDataSource() function
3069 int k;
3070 do
3071 {
3072 k = setupStr.Find((wxChar)2,TRUE);
3073 if (k != wxNOT_FOUND)
3074 setupStr[(UINT)k] = '\0';
3075 }
3076 while (k != wxNOT_FOUND);
3077
3078 result = SQLConfigDataSource((HWND)parent->GetHWND(), dsnLocation,
3079 driverName, setupStr.c_str());
3080
3081 if (!result)
3082 {
3083 // check for errors caused by ConfigDSN based functions
3084 DWORD retcode = 0;
3085 WORD cb;
3086 wxChar errMsg[500+1];
3087 errMsg[0] = '\0';
3088
3089 SQLInstallerError(1,&retcode,errMsg,500,&cb);
3090 if (retcode)
3091 {
3092 if (!silent)
3093 {
3094 #ifdef DBDEBUG_CONSOLE
3095 // When run in console mode, use standard out to display errors.
3096 cout << errMsg << endl;
3097 cout << "Press any key to continue..." << endl;
3098 getchar();
3099 #endif // DBDEBUG_CONSOLE
3100
3101 #ifdef __WXDEBUG__
3102 wxLogDebug(errMsg,wxT("ODBC DEBUG MESSAGE"));
3103 #endif // __WXDEBUG__
3104 }
3105 }
3106 }
3107 else
3108 result = TRUE;
3109
3110 #else // __WXMSW__
3111 #ifdef __WXDEBUG__
3112 wxLogDebug("wxDbCreateDataSource() not available except under MSW","DEBUG MESSAGE");
3113 #endif
3114 #endif // __WXMSW__
3115
3116 return result;
3117
3118 } // wxDbCreateDataSource()
3119 #endif
3120
3121
3122 /********** wxDbGetDataSource() **********/
3123 bool wxDbGetDataSource(HENV henv, char *Dsn, SWORD DsnMax, char *DsDesc, SWORD DsDescMax,
3124 UWORD direction)
3125 /*
3126 * Dsn and DsDesc will contain the data source name and data source
3127 * description upon return
3128 */
3129 {
3130 SWORD cb1,cb2;
3131
3132 if (SQLDataSources(henv, direction, (UCHAR FAR *) Dsn, DsnMax, &cb1,
3133 (UCHAR FAR *) DsDesc, DsDescMax, &cb2) == SQL_SUCCESS)
3134 return(TRUE);
3135 else
3136 return(FALSE);
3137
3138 } // wxDbGetDataSource()
3139
3140
3141 // Change this to 0 to remove use of all deprecated functions
3142 #if wxODBC_BACKWARD_COMPATABILITY
3143 /********************************************************************
3144 ********************************************************************
3145 *
3146 * The following functions are all DEPRECATED and are included for
3147 * backward compatability reasons only
3148 *
3149 ********************************************************************
3150 ********************************************************************/
3151 bool SqlLog(sqlLog state, const wxChar *filename)
3152 {
3153 return wxDbSqlLog((enum wxDbSqlLogState)state, filename);
3154 }
3155 /***** DEPRECATED: use wxGetDataSource() *****/
3156 bool GetDataSource(HENV henv, char *Dsn, SWORD DsnMax, char *DsDesc, SWORD DsDescMax,
3157 UWORD direction)
3158 {
3159 return wxDbGetDataSource(henv, Dsn, DsnMax, DsDesc, DsDescMax, direction);
3160 }
3161 /***** DEPRECATED: use wxDbGetConnection() *****/
3162 wxDb WXDLLEXPORT *GetDbConnection(DbStuff *pDbStuff, bool FwdOnlyCursors)
3163 {
3164 return wxDbGetConnection((wxDbConnectInf *)pDbStuff, FwdOnlyCursors);
3165 }
3166 /***** DEPRECATED: use wxDbFreeConnection() *****/
3167 bool WXDLLEXPORT FreeDbConnection(wxDb *pDb)
3168 {
3169 return wxDbFreeConnection(pDb);
3170 }
3171 /***** DEPRECATED: use wxDbCloseConnections() *****/
3172 void WXDLLEXPORT CloseDbConnections(void)
3173 {
3174 wxDbCloseConnections();
3175 }
3176 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
3177 int WXDLLEXPORT NumberDbConnectionsInUse(void)
3178 {
3179 return wxDbConnectionsInUse();
3180 }
3181 #endif
3182
3183
3184 #endif
3185 // wxUSE_ODBC