/[pcsx2_0.9.7]/trunk/3rdparty/wxWidgets/src/common/db.cpp
ViewVC logotype

Contents of /trunk/3rdparty/wxWidgets/src/common/db.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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