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 **********/
wxDbConnectInf()104 wxDbConnectInf::wxDbConnectInf()
105 {
106 Henv = 0;
107 freeHenvOnDestroy = false;
108
109 Initialize();
110 } // Constructor
111
112
113 /********** wxDbConnectInf Constructor - form 2 **********/
wxDbConnectInf(HENV henv,const wxString & dsn,const wxString & userID,const wxString & password,const wxString & defaultDir,const wxString & fileType,const wxString & description)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
~wxDbConnectInf()137 wxDbConnectInf::~wxDbConnectInf()
138 {
139 if (freeHenvOnDestroy)
140 {
141 FreeHenv();
142 }
143 } // wxDbConnectInf Destructor
144
145
146
147 /********** wxDbConnectInf::Initialize() **********/
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() **********/
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
FreeHenv()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
SetDsn(const wxString & dsn)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
SetUserID(const wxString & uid)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
SetPassword(const wxString & password)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
SetConnectionStr(const wxString & connectStr)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 **********/
wxDbColFor()240 wxDbColFor::wxDbColFor()
241 {
242 Initialize();
243 } // wxDbColFor::wxDbColFor()
244
245
246 /********** wxDbColFor::Initialize() **********/
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() **********/
Format(int Nation,int dbDataType,SWORD sqlDataType,short columnLength,short decimalDigits)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 **********/
wxDbColInf()364 wxDbColInf::wxDbColInf()
365 {
366 Initialize();
367 } // wxDbColInf::wxDbColInf()
368
369
370 /********** wxDbColInf Destructor ********/
~wxDbColInf()371 wxDbColInf::~wxDbColInf()
372 {
373 if (pColFor)
374 delete pColFor;
375 pColFor = NULL;
376 } // wxDbColInf::~wxDbColInf()
377
378
Initialize()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 ********/
wxDbTableInf()405 wxDbTableInf::wxDbTableInf()
406 {
407 Initialize();
408 } // wxDbTableInf::wxDbTableInf()
409
410
411 /********** wxDbTableInf Constructor ********/
~wxDbTableInf()412 wxDbTableInf::~wxDbTableInf()
413 {
414 if (pColInf)
415 delete [] pColInf;
416 pColInf = NULL;
417 } // wxDbTableInf::~wxDbTableInf()
418
419
Initialize()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 *************/
wxDbInf()433 wxDbInf::wxDbInf()
434 {
435 Initialize();
436 } // wxDbInf::wxDbInf()
437
438
439 /********** wxDbInf Destructor *************/
~wxDbInf()440 wxDbInf::~wxDbInf()
441 {
442 if (pTableInf)
443 delete [] pTableInf;
444 pTableInf = NULL;
445 } // wxDbInf::~wxDbInf()
446
447
448 /********** wxDbInf::Initialize() *************/
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 **********/
wxDb(const HENV & aHenv,bool FwdOnlyCursors)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 **********/
~wxDb()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() **********/
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 //
convertUserID(const wxChar * userID,wxString & UserID)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
determineDataTypes(bool failOnDataTypeUnsupported)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
open(bool failOnDataTypeUnsupported)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
Open(const wxString & inConnectStr,bool failOnDataTypeUnsupported)828 bool wxDb::Open(const wxString& inConnectStr, bool failOnDataTypeUnsupported)
829 {
830 wxASSERT(inConnectStr.length());
831 return Open(inConnectStr, NULL, failOnDataTypeUnsupported);
832 }
833
Open(const wxString & inConnectStr,SQLHWND parentWnd,bool failOnDataTypeUnsupported)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() **********/
Open(const wxString & Dsn,const wxString & Uid,const wxString & AuthStr,bool failOnDataTypeUnsupported)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
Open(wxDbConnectInf * dbConnectInf,bool failOnDataTypeUnsupported)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
Open(wxDb * copyDb)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() **********/
setConnectionOptions(void)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() **********/
getDbInfo(bool failOnDataTypeUnsupported)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;
3191 }
3192
3193 if (noCols == 0) // Probably a bogus table name
3194 return NULL;
3195
3196 // Get the name
3197 int i;
3198 short colNum;
3199 UCHAR name[100];
3200 SWORD Sword;
3201 SDWORD Sdword;
3202 for (colNum = 0; colNum < noCols; colNum++)
3203 {
3204 if (SQLColAttributes(hstmt,colNum+1, SQL_COLUMN_NAME,
3205 name, sizeof(name),
3206 &Sword, &Sdword) != SQL_SUCCESS)
3207 {
3208 DispAllErrors(henv, hdbc, hstmt);
3209 return NULL;
3210 }
3211
3212 wxString Name1 = name;
3213 Name1 = Name1.Upper();
3214
3215 // Where is this name in the array ?
3216 for (i = colNum ; i < noCols ; i++)
3217 {
3218 wxString Name2 = colInf[i].colName;
3219 Name2 = Name2.Upper();
3220 if (Name2 == Name1)
3221 {
3222 if (colNum != i) // swap to sort
3223 {
3224 wxDbColInf tmpColInf = colInf[colNum];
3225 colInf[colNum] = colInf[i];
3226 colInf[i] = tmpColInf;
3227 }
3228 break;
3229 }
3230 }
3231 }
3232 SQLFreeStmt(hstmt, SQL_CLOSE);
3233
3234 ///////////////////////////////////////////////////////////////////////////
3235 // End sorting
3236 ///////////////////////////////////////////////////////////////////////////
3237
3238 if (numCols)
3239 *numCols = noCols;
3240 return colInf;
3241
3242 } // wxDb::GetColumns()
3243
3244
3245 #endif // #else OLD_GETCOLUMNS
3246
3247
3248 /********** wxDb::GetColumnCount() **********/
3249 int wxDb::GetColumnCount(const wxString &tableName, const wxChar *userID)
3250 /*
3251 * Returns a count of how many columns are in a table.
3252 * If an error occurs in computing the number of columns
3253 * this function will return a -1 for the count
3254 *
3255 * userID is evaluated in the following manner:
3256 * userID == NULL ... UserID is ignored
3257 * userID == "" ... UserID set equal to 'this->uid'
3258 * userID != "" ... UserID set equal to 'userID'
3259 *
3260 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3261 * by this function. This function should use its own wxDb instance
3262 * to avoid undesired unbinding of columns.
3263 */
3264 {
3265 UWORD noCols = 0;
3266
3267 RETCODE retcode;
3268
3269 wxString TableName;
3270
3271 wxString UserID;
3272 convertUserID(userID,UserID);
3273
3274 TableName = tableName;
3275 // Oracle and Interbase table names are uppercase only, so force
3276 // the name to uppercase just in case programmer forgot to do this
3277 if ((Dbms() == dbmsORACLE) ||
3278 (Dbms() == dbmsFIREBIRD) ||
3279 (Dbms() == dbmsINTERBASE))
3280 TableName = TableName.Upper();
3281
3282 SQLFreeStmt(hstmt, SQL_CLOSE);
3283
3284 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3285 // use the call below that leaves out the user name
3286 if (!UserID.empty() &&
3287 Dbms() != dbmsMY_SQL &&
3288 Dbms() != dbmsACCESS &&
3289 Dbms() != dbmsMS_SQL_SERVER)
3290 {
3291 retcode = SQLColumns(hstmt,
3292 NULL, 0, // All qualifiers
3293 (SQLTCHAR *) UserID.c_str(), SQL_NTS, // Owner
3294 (SQLTCHAR *) TableName.c_str(), SQL_NTS,
3295 NULL, 0); // All columns
3296 }
3297 else
3298 {
3299 retcode = SQLColumns(hstmt,
3300 NULL, 0, // All qualifiers
3301 NULL, 0, // Owner
3302 (SQLTCHAR *) TableName.c_str(), SQL_NTS,
3303 NULL, 0); // All columns
3304 }
3305 if (retcode != SQL_SUCCESS)
3306 { // Error occurred, abort
3307 DispAllErrors(henv, hdbc, hstmt);
3308 SQLFreeStmt(hstmt, SQL_CLOSE);
3309 return(-1);
3310 }
3311
3312 // Count the columns
3313 while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
3314 noCols++;
3315
3316 if (retcode != SQL_NO_DATA_FOUND)
3317 { // Error occurred, abort
3318 DispAllErrors(henv, hdbc, hstmt);
3319 SQLFreeStmt(hstmt, SQL_CLOSE);
3320 return(-1);
3321 }
3322
3323 SQLFreeStmt(hstmt, SQL_CLOSE);
3324 return noCols;
3325
3326 } // wxDb::GetColumnCount()
3327
3328
3329 /********** wxDb::GetCatalog() *******/
3330 wxDbInf *wxDb::GetCatalog(const wxChar *userID)
3331 /*
3332 * ---------------------------------------------------------------------
3333 * -- 19991203 : mj10777 : Create ------
3334 * -- : Creates a wxDbInf with Tables / Cols Array ------
3335 * -- : uses SQLTables and fills pTableInf; ------
3336 * -- : pColInf is set to NULL and numCols to 0; ------
3337 * -- : returns pDbInf (wxDbInf) ------
3338 * -- - if unsuccessful (pDbInf == NULL) ------
3339 * -- : pColInf can be filled with GetColumns(..); ------
3340 * -- : numCols can be filled with GetColumnCount(..); ------
3341 * ---------------------------------------------------------------------
3342 *
3343 * userID is evaluated in the following manner:
3344 * userID == NULL ... UserID is ignored
3345 * userID == "" ... UserID set equal to 'this->uid'
3346 * userID != "" ... UserID set equal to 'userID'
3347 *
3348 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3349 * by this function. This function should use its own wxDb instance
3350 * to avoid undesired unbinding of columns.
3351 */
3352 {
3353 int noTab = 0; // Counter while filling table entries
3354 int pass;
3355 RETCODE retcode;
3356 SQLLEN cb;
3357 wxString tblNameSave;
3358
3359 wxString UserID;
3360 convertUserID(userID,UserID);
3361
3362 //-------------------------------------------------------------
3363 // Create the Database Array of catalog entries
3364
3365 wxDbInf *pDbInf = new wxDbInf;
3366
3367 //-------------------------------------------------------------
3368 // Table Information
3369 // Pass 1 - Determine how many Tables there are.
3370 // Pass 2 - Create the Table array and fill it
3371 // - Create the Cols array = NULL
3372 //-------------------------------------------------------------
3373
3374 for (pass = 1; pass <= 2; pass++)
3375 {
3376 SQLFreeStmt(hstmt, SQL_CLOSE); // Close if Open
3377 tblNameSave.Empty();
3378
3379 if (!UserID.empty() &&
3380 Dbms() != dbmsMY_SQL &&
3381 Dbms() != dbmsACCESS &&
3382 Dbms() != dbmsMS_SQL_SERVER)
3383 {
3384 retcode = SQLTables(hstmt,
3385 NULL, 0, // All qualifiers
3386 (SQLTCHAR *) UserID.c_str(), SQL_NTS, // User specified
3387 NULL, 0, // All tables
3388 NULL, 0); // All columns
3389 }
3390 else
3391 {
3392 retcode = SQLTables(hstmt,
3393 NULL, 0, // All qualifiers
3394 NULL, 0, // User specified
3395 NULL, 0, // All tables
3396 NULL, 0); // All columns
3397 }
3398
3399 if (retcode != SQL_SUCCESS)
3400 {
3401 DispAllErrors(henv, hdbc, hstmt);
3402 pDbInf = NULL;
3403 SQLFreeStmt(hstmt, SQL_CLOSE);
3404 return pDbInf;
3405 }
3406
3407 while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS) // Table Information
3408 {
3409 if (pass == 1) // First pass, just count the Tables
3410 {
3411 if (pDbInf->numTables == 0)
3412 {
3413 GetData( 1, SQL_C_WXCHAR, (UCHAR*) pDbInf->catalog, 128+1, &cb);
3414 GetData( 2, SQL_C_WXCHAR, (UCHAR*) pDbInf->schema, 128+1, &cb);
3415 }
3416 pDbInf->numTables++; // Counter for Tables
3417 } // if (pass == 1)
3418 if (pass == 2) // Create and fill the Table entries
3419 {
3420 if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3421 { // no, then create the Array
3422 pDbInf->pTableInf = new wxDbTableInf[pDbInf->numTables];
3423 noTab = 0;
3424 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3425
3426 GetData( 3, SQL_C_WXCHAR, (UCHAR*) (pDbInf->pTableInf+noTab)->tableName, DB_MAX_TABLE_NAME_LEN+1, &cb);
3427 GetData( 4, SQL_C_WXCHAR, (UCHAR*) (pDbInf->pTableInf+noTab)->tableType, 30+1, &cb);
3428 GetData( 5, SQL_C_WXCHAR, (UCHAR*) (pDbInf->pTableInf+noTab)->tableRemarks, 254+1, &cb);
3429
3430 noTab++;
3431 } // if
3432 } // while
3433 } // for
3434 SQLFreeStmt(hstmt, SQL_CLOSE);
3435
3436 // Query how many columns are in each table
3437 for (noTab=0;noTab<pDbInf->numTables;noTab++)
3438 {
3439 (pDbInf->pTableInf+noTab)->numCols = (UWORD)GetColumnCount((pDbInf->pTableInf+noTab)->tableName,UserID);
3440 }
3441
3442 return pDbInf;
3443
3444 } // wxDb::GetCatalog()
3445
3446
3447 /********** wxDb::Catalog() **********/
3448 bool wxDb::Catalog(const wxChar *userID, const wxString &fileName)
3449 /*
3450 * Creates the text file specified in 'filename' which will contain
3451 * a minimal data dictionary of all tables accessible by the user specified
3452 * in 'userID'
3453 *
3454 * userID is evaluated in the following manner:
3455 * userID == NULL ... UserID is ignored
3456 * userID == "" ... UserID set equal to 'this->uid'
3457 * userID != "" ... UserID set equal to 'userID'
3458 *
3459 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3460 * by this function. This function should use its own wxDb instance
3461 * to avoid undesired unbinding of columns.
3462 */
3463 {
3464 wxASSERT(fileName.length());
3465
3466 RETCODE retcode;
3467 SQLLEN cb;
3468 wxChar tblName[DB_MAX_TABLE_NAME_LEN+1];
3469 wxString tblNameSave;
3470 wxChar colName[DB_MAX_COLUMN_NAME_LEN+1];
3471 SWORD sqlDataType;
3472 wxChar typeName[30+1];
3473 SDWORD precision, length;
3474
3475 FILE *fp = wxFopen(fileName.c_str(),wxT("wt"));
3476 if (fp == NULL)
3477 return false;
3478
3479 SQLFreeStmt(hstmt, SQL_CLOSE);
3480
3481 wxString UserID;
3482 convertUserID(userID,UserID);
3483
3484 if (!UserID.empty() &&
3485 Dbms() != dbmsMY_SQL &&
3486 Dbms() != dbmsACCESS &&
3487 Dbms() != dbmsFIREBIRD &&
3488 Dbms() != dbmsINTERBASE &&
3489 Dbms() != dbmsMS_SQL_SERVER)
3490 {
3491 retcode = SQLColumns(hstmt,
3492 NULL, 0, // All qualifiers
3493 (SQLTCHAR *) UserID.c_str(), SQL_NTS, // User specified
3494 NULL, 0, // All tables
3495 NULL, 0); // All columns
3496 }
3497 else
3498 {
3499 retcode = SQLColumns(hstmt,
3500 NULL, 0, // All qualifiers
3501 NULL, 0, // User specified
3502 NULL, 0, // All tables
3503 NULL, 0); // All columns
3504 }
3505 if (retcode != SQL_SUCCESS)
3506 {
3507 DispAllErrors(henv, hdbc, hstmt);
3508 fclose(fp);
3509 return false;
3510 }
3511
3512 wxString outStr;
3513 tblNameSave.Empty();
3514 int cnt = 0;
3515
3516 while (true)
3517 {
3518 retcode = SQLFetch(hstmt);
3519 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
3520 break;
3521
3522 GetData(3,SQL_C_WXCHAR, (UCHAR *) tblName, DB_MAX_TABLE_NAME_LEN+1, &cb);
3523 GetData(4,SQL_C_WXCHAR, (UCHAR *) colName, DB_MAX_COLUMN_NAME_LEN+1,&cb);
3524 GetData(5,SQL_C_SSHORT, (UCHAR *)&sqlDataType, 0, &cb);
3525 GetData(6,SQL_C_WXCHAR, (UCHAR *) typeName, sizeof(typeName), &cb);
3526 GetData(7,SQL_C_SLONG, (UCHAR *)&precision, 0, &cb);
3527 GetData(8,SQL_C_SLONG, (UCHAR *)&length, 0, &cb);
3528
3529 if (wxStrcmp(tblName, tblNameSave.c_str()))
3530 {
3531 if (cnt)
3532 wxFputs(wxT("\n"), fp);
3533 wxFputs(wxT("================================ "), fp);
3534 wxFputs(wxT("================================ "), fp);
3535 wxFputs(wxT("===================== "), fp);
3536 wxFputs(wxT("========= "), fp);
3537 wxFputs(wxT("=========\n"), fp);
3538 outStr.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3539 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3540 wxFputs(outStr.c_str(), fp);
3541 wxFputs(wxT("================================ "), fp);
3542 wxFputs(wxT("================================ "), fp);
3543 wxFputs(wxT("===================== "), fp);
3544 wxFputs(wxT("========= "), fp);
3545 wxFputs(wxT("=========\n"), fp);
3546 tblNameSave = tblName;
3547 }
3548
3549 outStr.Printf(wxT("%-32s %-32s (%04d)%-15s %9ld %9ld\n"),
3550 tblName, colName, sqlDataType, typeName, precision, length);
3551 if (wxFputs(outStr.c_str(), fp) == EOF)
3552 {
3553 SQLFreeStmt(hstmt, SQL_CLOSE);
3554 fclose(fp);
3555 return false;
3556 }
3557 cnt++;
3558 }
3559
3560 if (retcode != SQL_NO_DATA_FOUND)
3561 DispAllErrors(henv, hdbc, hstmt);
3562
3563 SQLFreeStmt(hstmt, SQL_CLOSE);
3564
3565 fclose(fp);
3566 return(retcode == SQL_NO_DATA_FOUND);
3567
3568 } // wxDb::Catalog()
3569
3570
3571 bool wxDb::TableExists(const wxString &tableName, const wxChar *userID, const wxString &tablePath)
3572 /*
3573 * Table name can refer to a table, view, alias or synonym. Returns true
3574 * if the object exists in the database. This function does not indicate
3575 * whether or not the user has privleges to query or perform other functions
3576 * on the table.
3577 *
3578 * userID is evaluated in the following manner:
3579 * userID == NULL ... UserID is ignored
3580 * userID == "" ... UserID set equal to 'this->uid'
3581 * userID != "" ... UserID set equal to 'userID'
3582 */
3583 {
3584 wxASSERT(tableName.length());
3585
3586 wxString TableName;
3587
3588 if (Dbms() == dbmsDBASE)
3589 {
3590 wxString dbName;
3591 if (tablePath.length())
3592 dbName.Printf(wxT("%s/%s.dbf"), tablePath.c_str(), tableName.c_str());
3593 else
3594 dbName.Printf(wxT("%s.dbf"), tableName.c_str());
3595
3596 bool exists;
3597 exists = wxFileExists(dbName);
3598 return exists;
3599 }
3600
3601 wxString UserID;
3602 convertUserID(userID,UserID);
3603
3604 TableName = tableName;
3605 // Oracle and Interbase table names are uppercase only, so force
3606 // the name to uppercase just in case programmer forgot to do this
3607 if ((Dbms() == dbmsORACLE) ||
3608 (Dbms() == dbmsFIREBIRD) ||
3609 (Dbms() == dbmsINTERBASE))
3610 TableName = TableName.Upper();
3611
3612 SQLFreeStmt(hstmt, SQL_CLOSE);
3613 RETCODE retcode;
3614
3615 // Some databases cannot accept a user name when looking up table names,
3616 // so we use the call below that leaves out the user name
3617 if (!UserID.empty() &&
3618 Dbms() != dbmsMY_SQL &&
3619 Dbms() != dbmsACCESS &&
3620 Dbms() != dbmsMS_SQL_SERVER &&
3621 Dbms() != dbmsDB2 &&
3622 Dbms() != dbmsFIREBIRD &&
3623 Dbms() != dbmsINTERBASE &&
3624 Dbms() != dbmsPERVASIVE_SQL)
3625 {
3626 retcode = SQLTables(hstmt,
3627 NULL, 0, // All qualifiers
3628 (SQLTCHAR *) UserID.c_str(), SQL_NTS, // Only tables owned by this user
3629 (SQLTCHAR FAR *)TableName.c_str(), SQL_NTS,
3630 NULL, 0); // All table types
3631 }
3632 else
3633 {
3634 retcode = SQLTables(hstmt,
3635 NULL, 0, // All qualifiers
3636 NULL, 0, // All owners
3637 (SQLTCHAR FAR *)TableName.c_str(), SQL_NTS,
3638 NULL, 0); // All table types
3639 }
3640 if (retcode != SQL_SUCCESS)
3641 return(DispAllErrors(henv, hdbc, hstmt));
3642
3643 retcode = SQLFetch(hstmt);
3644 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
3645 {
3646 SQLFreeStmt(hstmt, SQL_CLOSE);
3647 return(DispAllErrors(henv, hdbc, hstmt));
3648 }
3649
3650 SQLFreeStmt(hstmt, SQL_CLOSE);
3651
3652 return true;
3653
3654 } // wxDb::TableExists()
3655
3656
3657 /********** wxDb::TablePrivileges() **********/
3658 bool wxDb::TablePrivileges(const wxString &tableName, const wxString &priv, const wxChar *userID,
3659 const wxChar *schema, const wxString &WXUNUSED(tablePath))
3660 {
3661 wxASSERT(tableName.length());
3662
3663 wxDbTablePrivilegeInfo result;
3664 SQLLEN cbRetVal;
3665 RETCODE retcode;
3666
3667 // We probably need to be able to dynamically set this based on
3668 // the driver type, and state.
3669 wxChar curRole[]=wxT("public");
3670
3671 wxString TableName;
3672
3673 wxString UserID,Schema;
3674 convertUserID(userID,UserID);
3675 convertUserID(schema,Schema);
3676
3677 TableName = tableName;
3678 // Oracle and Interbase table names are uppercase only, so force
3679 // the name to uppercase just in case programmer forgot to do this
3680 if ((Dbms() == dbmsORACLE) ||
3681 (Dbms() == dbmsFIREBIRD) ||
3682 (Dbms() == dbmsINTERBASE))
3683 TableName = TableName.Upper();
3684
3685 SQLFreeStmt(hstmt, SQL_CLOSE);
3686
3687 // Some databases cannot accept a user name when looking up table names,
3688 // so we use the call below that leaves out the user name
3689 if (!Schema.empty() &&
3690 Dbms() != dbmsMY_SQL &&
3691 Dbms() != dbmsACCESS &&
3692 Dbms() != dbmsMS_SQL_SERVER)
3693 {
3694 retcode = SQLTablePrivileges(hstmt,
3695 NULL, 0, // Catalog
3696 (SQLTCHAR FAR *)Schema.c_str(), SQL_NTS, // Schema
3697 (SQLTCHAR FAR *)TableName.c_str(), SQL_NTS);
3698 }
3699 else
3700 {
3701 retcode = SQLTablePrivileges(hstmt,
3702 NULL, 0, // Catalog
3703 NULL, 0, // Schema
3704 (SQLTCHAR FAR *)TableName.c_str(), SQL_NTS);
3705 }
3706
3707 #ifdef DBDEBUG_CONSOLE
3708 wxFprintf(stderr ,wxT("SQLTablePrivileges() returned %i \n"),retcode);
3709 #endif
3710
3711 if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
3712 return (DispAllErrors(henv, hdbc, hstmt));
3713
3714 bool failed = false;
3715 retcode = SQLFetch(hstmt);
3716 while (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
3717 {
3718 if (SQLGetData(hstmt, 1, SQL_C_WXCHAR, (UCHAR*) result.tableQual, sizeof(result.tableQual), &cbRetVal) != SQL_SUCCESS)
3719 failed = true;
3720
3721 if (!failed && SQLGetData(hstmt, 2, SQL_C_WXCHAR, (UCHAR*) result.tableOwner, sizeof(result.tableOwner), &cbRetVal) != SQL_SUCCESS)
3722 failed = true;
3723
3724 if (!failed && SQLGetData(hstmt, 3, SQL_C_WXCHAR, (UCHAR*) result.tableName, sizeof(result.tableName), &cbRetVal) != SQL_SUCCESS)
3725 failed = true;
3726
3727 if (!failed && SQLGetData(hstmt, 4, SQL_C_WXCHAR, (UCHAR*) result.grantor, sizeof(result.grantor), &cbRetVal) != SQL_SUCCESS)
3728 failed = true;
3729
3730 if (!failed && SQLGetData(hstmt, 5, SQL_C_WXCHAR, (UCHAR*) result.grantee, sizeof(result.grantee), &cbRetVal) != SQL_SUCCESS)
3731 failed = true;
3732
3733 if (!failed && SQLGetData(hstmt, 6, SQL_C_WXCHAR, (UCHAR*) result.privilege, sizeof(result.privilege), &cbRetVal) != SQL_SUCCESS)
3734 failed = true;
3735
3736 if (!failed && SQLGetData(hstmt, 7, SQL_C_WXCHAR, (UCHAR*) result.grantable, sizeof(result.grantable), &cbRetVal) != SQL_SUCCESS)
3737 failed = true;
3738
3739 if (failed)
3740 {
3741 return(DispAllErrors(henv, hdbc, hstmt));
3742 }
3743 #ifdef DBDEBUG_CONSOLE
3744 wxFprintf(stderr,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3745 result.privilege,result.tableOwner,result.tableName,
3746 result.grantor, result.grantee);
3747 #endif
3748
3749 if (UserID.IsSameAs(result.tableOwner,false))
3750 {
3751 SQLFreeStmt(hstmt, SQL_CLOSE);
3752 return true;
3753 }
3754
3755 if (UserID.IsSameAs(result.grantee,false) &&
3756 !wxStrcmp(result.privilege,priv))
3757 {
3758 SQLFreeStmt(hstmt, SQL_CLOSE);
3759 return true;
3760 }
3761
3762 if (!wxStrcmp(result.grantee,curRole) &&
3763 !wxStrcmp(result.privilege,priv))
3764 {
3765 SQLFreeStmt(hstmt, SQL_CLOSE);
3766 return true;
3767 }
3768
3769 retcode = SQLFetch(hstmt);
3770 }
3771
3772 SQLFreeStmt(hstmt, SQL_CLOSE);
3773 return false;
3774
3775 } // wxDb::TablePrivileges
3776
3777
3778 const wxString wxDb::SQLTableName(const wxChar *tableName)
3779 {
3780 wxString TableName;
3781
3782 if (Dbms() == dbmsACCESS)
3783 TableName = _T("\"");
3784 TableName += tableName;
3785 if (Dbms() == dbmsACCESS)
3786 TableName += _T("\"");
3787
3788 return TableName;
3789 } // wxDb::SQLTableName()
3790
3791
3792 const wxString wxDb::SQLColumnName(const wxChar *colName)
3793 {
3794 wxString ColName;
3795
3796 if (Dbms() == dbmsACCESS)
3797 ColName = _T("\"");
3798 ColName += colName;
3799 if (Dbms() == dbmsACCESS)
3800 ColName += _T("\"");
3801
3802 return ColName;
3803 } // wxDb::SQLColumnName()
3804
3805
3806 /********** wxDb::SetSqlLogging() **********/
3807 bool wxDb::SetSqlLogging(wxDbSqlLogState state, const wxString &filename, bool append)
3808 {
3809 wxASSERT(state == sqlLogON || state == sqlLogOFF);
3810 wxASSERT(state == sqlLogOFF || filename.length());
3811
3812 if (state == sqlLogON)
3813 {
3814 if (fpSqlLog == 0)
3815 {
3816 fpSqlLog = wxFopen(filename.c_str(), (append ? wxT("at") : wxT("wt")));
3817 if (fpSqlLog == NULL)
3818 return false;
3819 }
3820 }
3821 else // sqlLogOFF
3822 {
3823 if (fpSqlLog)
3824 {
3825 if (fclose(fpSqlLog))
3826 return false;
3827 fpSqlLog = 0;
3828 }
3829 }
3830
3831 sqlLogState = state;
3832 return true;
3833
3834 } // wxDb::SetSqlLogging()
3835
3836
3837 /********** wxDb::WriteSqlLog() **********/
3838 bool wxDb::WriteSqlLog(const wxString &logMsg)
3839 {
3840 wxASSERT(logMsg.length());
3841
3842 if (fpSqlLog == 0 || sqlLogState == sqlLogOFF)
3843 return false;
3844
3845 if (wxFputs(wxT("\n"), fpSqlLog) == EOF)
3846 return false;
3847 if (wxFputs(logMsg, fpSqlLog) == EOF)
3848 return false;
3849 if (wxFputs(wxT("\n"), fpSqlLog) == EOF)
3850 return false;
3851
3852 return true;
3853
3854 } // wxDb::WriteSqlLog()
3855
3856
3857 /********** wxDb::Dbms() **********/
3858 wxDBMS wxDb::Dbms(void)
3859 /*
3860 * Be aware that not all database engines use the exact same syntax, and not
3861 * every ODBC compliant database is compliant to the same level of compliancy.
3862 * Some manufacturers support the minimum Level 1 compliancy, and others up
3863 * through Level 3. Others support subsets of features for levels above 1.
3864 *
3865 * If you find an inconsistency between the wxDb class and a specific database
3866 * engine, and an identifier to this section, and special handle the database in
3867 * the area where behavior is non-conforming with the other databases.
3868 *
3869 *
3870 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3871 * ---------------------------------------------------
3872 *
3873 * ORACLE
3874 * - Currently the only database supported by the class to support VIEWS
3875 *
3876 * DBASE
3877 * - Does not support the SQL_TIMESTAMP structure
3878 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3879 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3880 * is true. The user must create ALL indexes from their program.
3881 * - Table names can only be 8 characters long
3882 * - Column names can only be 10 characters long
3883 *
3884 * SYBASE (all)
3885 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3886 * after every table name involved in the query/join if that tables matching record(s)
3887 * are to be locked
3888 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3889 *
3890 * SYBASE (Enterprise)
3891 * - If a column is part of the Primary Key, the column cannot be NULL
3892 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3893 *
3894 * MY_SQL
3895 * - If a column is part of the Primary Key, the column cannot be NULL
3896 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3897 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3898 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3899 * column definition if it is not defined correctly, but it is experimental
3900 * - Does not support sub-queries in SQL statements
3901 *
3902 * POSTGRES
3903 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3904 * - Does not support sub-queries in SQL statements
3905 *
3906 * DB2
3907 * - Primary keys must be declared as NOT NULL
3908 * - Table and index names must not be longer than 13 characters in length (technically
3909 * table names can be up to 18 characters, but the primary index is created using the
3910 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3911 *
3912 * PERVASIVE SQL
3913 *
3914 * INTERBASE
3915 * - Columns that are part of primary keys must be defined as being NOT NULL
3916 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3917 * column definition if it is not defined correctly, but it is experimental
3918 */
3919 {
3920 // Should only need to do this once for each new database connection
3921 // so return the value we already determined it to be to save time
3922 // and lots of string comparisons
3923 if (dbmsType != dbmsUNIDENTIFIED)
3924 return(dbmsType);
3925
3926 #ifdef DBDEBUG_CONSOLE
3927 // When run in console mode, use standard out to display errors.
3928 cout << "Database connecting to: " << dbInf.dbmsName << endl;
3929 #endif // DBDEBUG_CONSOLE
3930
3931 wxLogDebug(wxT("Database connecting to: "));
3932 wxLogDebug(dbInf.dbmsName);
3933
3934 wxChar baseName[25+1];
3935 wxStrncpy(baseName, dbInf.dbmsName, 25);
3936 baseName[25] = 0;
3937
3938 // RGG 20001025 : add support for Interbase
3939 // GT : Integrated to base classes on 20001121
3940 if (!wxStricmp(dbInf.dbmsName,wxT("Interbase")))
3941 return((wxDBMS)(dbmsType = dbmsINTERBASE));
3942
3943 // BJO 20000428 : add support for Virtuoso
3944 if (!wxStricmp(dbInf.dbmsName,wxT("OpenLink Virtuoso VDBMS")))
3945 return((wxDBMS)(dbmsType = dbmsVIRTUOSO));
3946
3947 if (!wxStricmp(dbInf.dbmsName,wxT("Adaptive Server Anywhere")))
3948 return((wxDBMS)(dbmsType = dbmsSYBASE_ASA));
3949
3950 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3951 // connected through an OpenLink driver.
3952 // Is it also returned by Sybase Adapatitve server?
3953 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3954 if (!wxStricmp(dbInf.dbmsName,wxT("SQL Server")))
3955 {
3956 if (!wxStrncmp(dbInf.driverName, wxT("oplodbc"), 7) ||
3957 !wxStrncmp(dbInf.driverName, wxT("OLOD"), 4))
3958 return ((wxDBMS)(dbmsMS_SQL_SERVER));
3959 else
3960 return ((wxDBMS)(dbmsType = dbmsSYBASE_ASE));
3961 }
3962
3963 if (!wxStricmp(dbInf.dbmsName,wxT("Microsoft SQL Server")))
3964 return((wxDBMS)(dbmsType = dbmsMS_SQL_SERVER));
3965
3966 baseName[10] = 0;
3967 if (!wxStricmp(baseName,wxT("PostgreSQL"))) // v6.5.0
3968 return((wxDBMS)(dbmsType = dbmsPOSTGRES));
3969
3970 baseName[9] = 0;
3971 if (!wxStricmp(baseName,wxT("Pervasive")))
3972 return((wxDBMS)(dbmsType = dbmsPERVASIVE_SQL));
3973
3974 baseName[8] = 0;
3975 if (!wxStricmp(baseName,wxT("Informix")))
3976 return((wxDBMS)(dbmsType = dbmsINFORMIX));
3977
3978 if (!wxStricmp(baseName,wxT("Firebird")))
3979 return((wxDBMS)(dbmsType = dbmsFIREBIRD));
3980
3981 baseName[6] = 0;
3982 if (!wxStricmp(baseName,wxT("Oracle")))
3983 return((wxDBMS)(dbmsType = dbmsORACLE));
3984 if (!wxStricmp(baseName,wxT("ACCESS")))
3985 return((wxDBMS)(dbmsType = dbmsACCESS));
3986 if (!wxStricmp(baseName,wxT("Sybase")))
3987 return((wxDBMS)(dbmsType = dbmsSYBASE_ASE));
3988
3989 baseName[5] = 0;
3990 if (!wxStricmp(baseName,wxT("DBASE")))
3991 return((wxDBMS)(dbmsType = dbmsDBASE));
3992 if (!wxStricmp(baseName,wxT("xBase")))
3993 return((wxDBMS)(dbmsType = dbmsXBASE_SEQUITER));
3994 if (!wxStricmp(baseName,wxT("MySQL")))
3995 return((wxDBMS)(dbmsType = dbmsMY_SQL));
3996 if (!wxStricmp(baseName,wxT("MaxDB")))
3997 return((wxDBMS)(dbmsType = dbmsMAXDB));
3998
3999 baseName[3] = 0;
4000 if (!wxStricmp(baseName,wxT("DB2")))
4001 return((wxDBMS)(dbmsType = dbmsDB2));
4002
4003 return((wxDBMS)(dbmsType = dbmsUNIDENTIFIED));
4004
4005 } // wxDb::Dbms()
4006
4007
4008 bool wxDb::ModifyColumn(const wxString &tableName, const wxString &columnName,
4009 int dataType, ULONG columnLength,
4010 const wxString &optionalParam)
4011 {
4012 wxASSERT(tableName.length());
4013 wxASSERT(columnName.length());
4014 wxASSERT((dataType == DB_DATA_TYPE_VARCHAR && columnLength > 0) ||
4015 dataType != DB_DATA_TYPE_VARCHAR);
4016
4017 // Must specify a columnLength if modifying a VARCHAR type column
4018 if (dataType == DB_DATA_TYPE_VARCHAR && !columnLength)
4019 return false;
4020
4021 wxString dataTypeName;
4022 wxString sqlStmt;
4023 wxString alterSlashModify;
4024
4025 switch(dataType)
4026 {
4027 case DB_DATA_TYPE_VARCHAR :
4028 dataTypeName = typeInfVarchar.TypeName;
4029 break;
4030 case DB_DATA_TYPE_INTEGER :
4031 dataTypeName = typeInfInteger.TypeName;
4032 break;
4033 case DB_DATA_TYPE_FLOAT :
4034 dataTypeName = typeInfFloat.TypeName;
4035 break;
4036 case DB_DATA_TYPE_DATE :
4037 dataTypeName = typeInfDate.TypeName;
4038 break;
4039 case DB_DATA_TYPE_BLOB :
4040 dataTypeName = typeInfBlob.TypeName;
4041 break;
4042 default:
4043 return false;
4044 }
4045
4046 // Set the modify or alter syntax depending on the type of database connected to
4047 switch (Dbms())
4048 {
4049 case dbmsORACLE :
4050 alterSlashModify = _T("MODIFY");
4051 break;
4052 case dbmsMS_SQL_SERVER :
4053 alterSlashModify = _T("ALTER COLUMN");
4054 break;
4055 case dbmsUNIDENTIFIED :
4056 return false;
4057 case dbmsSYBASE_ASA :
4058 case dbmsSYBASE_ASE :
4059 case dbmsMY_SQL :
4060 case dbmsPOSTGRES :
4061 case dbmsACCESS :
4062 case dbmsDBASE :
4063 case dbmsXBASE_SEQUITER :
4064 default :
4065 alterSlashModify = _T("MODIFY");
4066 break;
4067 }
4068
4069 // create the SQL statement
4070 if ( Dbms() == dbmsMY_SQL )
4071 {
4072 sqlStmt.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName.c_str(), alterSlashModify.c_str(),
4073 columnName.c_str(), dataTypeName.c_str());
4074 }
4075 else
4076 {
4077 sqlStmt.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName.c_str(), alterSlashModify.c_str(),
4078 columnName.c_str(), dataTypeName.c_str());
4079 }
4080
4081 // For varchars only, append the size of the column
4082 if (dataType == DB_DATA_TYPE_VARCHAR &&
4083 (Dbms() != dbmsMY_SQL || dataTypeName != _T("text")))
4084 {
4085 wxString s;
4086 s.Printf(wxT("(%lu)"), columnLength);
4087 sqlStmt += s;
4088 }
4089
4090 // for passing things like "NOT NULL"
4091 if (optionalParam.length())
4092 {
4093 sqlStmt += wxT(" ");
4094 sqlStmt += optionalParam;
4095 }
4096
4097 return ExecSql(sqlStmt);
4098
4099 } // wxDb::ModifyColumn()
4100
4101 /********** wxDb::EscapeSqlChars() **********/
4102 wxString wxDb::EscapeSqlChars(const wxString& valueOrig)
4103 {
4104 wxString value(valueOrig);
4105 switch (Dbms())
4106 {
4107 case dbmsACCESS:
4108 // Access doesn't seem to care about backslashes, so only escape single quotes.
4109 value.Replace(wxT("'"), wxT("''"));
4110 break;
4111
4112 default:
4113 // All the others are supposed to be the same for now, add special
4114 // handling for them if necessary
4115 value.Replace(wxT("\\"), wxT("\\\\"));
4116 value.Replace(wxT("'"), wxT("\\'"));
4117 break;
4118 }
4119
4120 return value;
4121 } // wxDb::EscapeSqlChars()
4122
4123
4124 /********** wxDbGetConnection() **********/
4125 wxDb WXDLLIMPEXP_ODBC *wxDbGetConnection(wxDbConnectInf *pDbConfig, bool FwdOnlyCursors)
4126 {
4127 wxDbList *pList;
4128
4129 // Used to keep a pointer to a DB connection that matches the requested
4130 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
4131 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
4132 // rather than having to re-query the datasource to get all the values
4133 // using the wxDb::Open(Dsn,Uid,AuthStr) function
4134 wxDb *matchingDbConnection = NULL;
4135
4136 // Scan the linked list searching for an available database connection
4137 // that's already been opened but is currently not in use.
4138 for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
4139 {
4140 // The database connection must be for the same datasource
4141 // name and must currently not be in use.
4142 if (pList->Free &&
4143 (pList->PtrDb->FwdOnlyCursors() == FwdOnlyCursors))
4144 {
4145 if (pDbConfig->UseConnectionStr())
4146 {
4147 if (pList->PtrDb->OpenedWithConnectionString() &&
4148 (!wxStrcmp(pDbConfig->GetConnectionStr(), pList->ConnectionStr)))
4149 {
4150 // Found a free connection
4151 pList->Free = false;
4152 return(pList->PtrDb);
4153 }
4154 }
4155 else
4156 {
4157 if (!pList->PtrDb->OpenedWithConnectionString() &&
4158 (!wxStrcmp(pDbConfig->GetDsn(), pList->Dsn)))
4159 {
4160 // Found a free connection
4161 pList->Free = false;
4162 return(pList->PtrDb);
4163 }
4164 }
4165 }
4166
4167 if (pDbConfig->UseConnectionStr())
4168 {
4169 if (!wxStrcmp(pDbConfig->GetConnectionStr(), pList->ConnectionStr))
4170 matchingDbConnection = pList->PtrDb;
4171 }
4172 else
4173 {
4174 if (!wxStrcmp(pDbConfig->GetDsn(), pList->Dsn) &&
4175 !wxStrcmp(pDbConfig->GetUserID(), pList->Uid) &&
4176 !wxStrcmp(pDbConfig->GetPassword(), pList->AuthStr))
4177 matchingDbConnection = pList->PtrDb;
4178 }
4179 }
4180
4181 // No available connections. A new connection must be made and
4182 // appended to the end of the linked list.
4183 if (PtrBegDbList)
4184 {
4185 // Find the end of the list
4186 for (pList = PtrBegDbList; pList->PtrNext; pList = pList->PtrNext);
4187 // Append a new list item
4188 pList->PtrNext = new wxDbList;
4189 pList->PtrNext->PtrPrev = pList;
4190 pList = pList->PtrNext;
4191 }
4192 else // Empty list
4193 {
4194 // Create the first node on the list
4195 pList = PtrBegDbList = new wxDbList;
4196 pList->PtrPrev = 0;
4197 }
4198
4199 // Initialize new node in the linked list
4200 pList->PtrNext = 0;
4201 pList->Free = false;
4202 pList->Dsn = pDbConfig->GetDsn();
4203 pList->Uid = pDbConfig->GetUserID();
4204 pList->AuthStr = pDbConfig->GetPassword();
4205 pList->ConnectionStr = pDbConfig->GetConnectionStr();
4206
4207 pList->PtrDb = new wxDb(pDbConfig->GetHenv(), FwdOnlyCursors);
4208
4209 bool opened;
4210
4211 if (!matchingDbConnection)
4212 {
4213 if (pDbConfig->UseConnectionStr())
4214 {
4215 opened = pList->PtrDb->Open(pDbConfig->GetConnectionStr());
4216 }
4217 else
4218 {
4219 opened = pList->PtrDb->Open(pDbConfig->GetDsn(), pDbConfig->GetUserID(), pDbConfig->GetPassword());
4220 }
4221 }
4222 else
4223 opened = pList->PtrDb->Open(matchingDbConnection);
4224
4225 // Connect to the datasource
4226 if (opened)
4227 {
4228 pList->PtrDb->setCached(true); // Prevent a user from deleting a cached connection
4229 pList->PtrDb->SetSqlLogging(SQLLOGstate, SQLLOGfn, true);
4230 return(pList->PtrDb);
4231 }
4232 else // Unable to connect, destroy list item
4233 {
4234 if (pList->PtrPrev)
4235 pList->PtrPrev->PtrNext = 0;
4236 else
4237 PtrBegDbList = 0; // Empty list again
4238
4239 pList->PtrDb->CommitTrans(); // Commit any open transactions on wxDb object
4240 pList->PtrDb->Close(); // Close the wxDb object
4241 delete pList->PtrDb; // Deletes the wxDb object
4242 delete pList; // Deletes the linked list object
4243 return(0);
4244 }
4245
4246 } // wxDbGetConnection()
4247
4248
4249 /********** wxDbFreeConnection() **********/
4250 bool WXDLLIMPEXP_ODBC wxDbFreeConnection(wxDb *pDb)
4251 {
4252 wxDbList *pList;
4253
4254 // Scan the linked list searching for the database connection
4255 for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
4256 {
4257 if (pList->PtrDb == pDb) // Found it, now free it!!!
4258 return (pList->Free = true);
4259 }
4260
4261 // Never found the database object, return failure
4262 return false;
4263
4264 } // wxDbFreeConnection()
4265
4266
4267 /********** wxDbCloseConnections() **********/
4268 void WXDLLIMPEXP_ODBC wxDbCloseConnections(void)
4269 {
4270 wxDbList *pList, *pNext;
4271
4272 // Traverse the linked list closing database connections and freeing memory as I go.
4273 for (pList = PtrBegDbList; pList; pList = pNext)
4274 {
4275 pNext = pList->PtrNext; // Save the pointer to next
4276 pList->PtrDb->CommitTrans(); // Commit any open transactions on wxDb object
4277 pList->PtrDb->Close(); // Close the wxDb object
4278 pList->PtrDb->setCached(false); // Allows deletion of the wxDb instance
4279 delete pList->PtrDb; // Deletes the wxDb object
4280 delete pList; // Deletes the linked list object
4281 }
4282
4283 // Mark the list as empty
4284 PtrBegDbList = 0;
4285
4286 } // wxDbCloseConnections()
4287
4288
4289 /********** wxDbConnectionsInUse() **********/
4290 int WXDLLIMPEXP_ODBC wxDbConnectionsInUse(void)
4291 {
4292 wxDbList *pList;
4293 int cnt = 0;
4294
4295 // Scan the linked list counting db connections that are currently in use
4296 for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
4297 {
4298 if (pList->Free == false)
4299 cnt++;
4300 }
4301
4302 return(cnt);
4303
4304 } // wxDbConnectionsInUse()
4305
4306
4307
4308 /********** wxDbLogExtendedErrorMsg() **********/
4309 // DEBUG ONLY function
4310 const wxChar WXDLLIMPEXP_ODBC *wxDbLogExtendedErrorMsg(const wxChar *userText,
4311 wxDb *pDb,
4312 const wxChar *ErrFile,
4313 int ErrLine)
4314 {
4315 static wxString msg;
4316 msg = userText;
4317
4318 wxString tStr;
4319
4320 if (ErrFile || ErrLine)
4321 {
4322 msg += wxT("File: ");
4323 msg += ErrFile;
4324 msg += wxT(" Line: ");
4325 tStr.Printf(wxT("%d"),ErrLine);
4326 msg += tStr.c_str();
4327 msg += wxT("\n");
4328 }
4329
4330 msg.Append (wxT("\nODBC errors:\n"));
4331 msg += wxT("\n");
4332
4333 // Display errors for this connection
4334 int i;
4335 for (i = 0; i < DB_MAX_ERROR_HISTORY; i++)
4336 {
4337 if (pDb->errorList[i])
4338 {
4339 msg.Append(pDb->errorList[i]);
4340 if (wxStrcmp(pDb->errorList[i], wxEmptyString) != 0)
4341 msg.Append(wxT("\n"));
4342 // Clear the errmsg buffer so the next error will not
4343 // end up showing the previous error that have occurred
4344 wxStrcpy(pDb->errorList[i], wxEmptyString);
4345 }
4346 }
4347 msg += wxT("\n");
4348
4349 wxLogDebug(msg.c_str());
4350
4351 return msg.c_str();
4352 } // wxDbLogExtendedErrorMsg()
4353
4354
4355 /********** wxDbSqlLog() **********/
4356 bool wxDbSqlLog(wxDbSqlLogState state, const wxChar *filename)
4357 {
4358 bool append = false;
4359 wxDbList *pList;
4360
4361 for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
4362 {
4363 if (!pList->PtrDb->SetSqlLogging(state,filename,append))
4364 return false;
4365 append = true;
4366 }
4367
4368 SQLLOGstate = state;
4369 SQLLOGfn = filename;
4370
4371 return true;
4372
4373 } // wxDbSqlLog()
4374
4375
4376 #if 0
4377 /********** wxDbCreateDataSource() **********/
4378 int wxDbCreateDataSource(const wxString &driverName, const wxString &dsn, const wxString &description,
4379 bool sysDSN, const wxString &defDir, wxWindow *parent)
4380 /*
4381 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4382 * Very rudimentary creation of an ODBC data source.
4383 *
4384 * ODBC driver must be ODBC 3.0 compliant to use this function
4385 */
4386 {
4387 int result = FALSE;
4388
4389 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4390 #ifdef __VISUALC__
4391 int dsnLocation;
4392 wxString setupStr;
4393
4394 if (sysDSN)
4395 dsnLocation = ODBC_ADD_SYS_DSN;
4396 else
4397 dsnLocation = ODBC_ADD_DSN;
4398
4399 // NOTE: The decimal 2 is an invalid character in all keyword pairs
4400 // so that is why I used it, as wxString does not deal well with
4401 // embedded nulls in strings
4402 setupStr.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn,2,description,2,defDir,2);
4403
4404 // Replace the separator from above with the '\0' separator needed
4405 // by the SQLConfigDataSource() function
4406 int k;
4407 do
4408 {
4409 k = setupStr.Find((wxChar)2,true);
4410 if (k != wxNOT_FOUND)
4411 setupStr[(UINT)k] = wxT('\0');
4412 }
4413 while (k != wxNOT_FOUND);
4414
4415 result = SQLConfigDataSource((HWND)parent->GetHWND(), dsnLocation,
4416 driverName, setupStr.c_str());
4417
4418 if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
4419 {
4420 // check for errors caused by ConfigDSN based functions
4421 DWORD retcode = 0;
4422 WORD cb;
4423 wxChar errMsg[SQL_MAX_MESSAGE_LENGTH];
4424 errMsg[0] = wxT('\0');
4425
4426 // This function is only supported in ODBC drivers v3.0 compliant and above
4427 SQLInstallerError(1,&retcode,errMsg,SQL_MAX_MESSAGE_LENGTH-1,&cb);
4428 if (retcode)
4429 {
4430 #ifdef DBDEBUG_CONSOLE
4431 // When run in console mode, use standard out to display errors.
4432 cout << errMsg << endl;
4433 cout << wxT("Press any key to continue...") << endl;
4434 getchar();
4435 #endif // DBDEBUG_CONSOLE
4436
4437 #ifdef __WXDEBUG__
4438 wxLogDebug(errMsg,wxT("ODBC DEBUG MESSAGE"));
4439 #endif // __WXDEBUG__
4440 }
4441 }
4442 else
4443 result = TRUE;
4444 #else
4445 // Using iODBC/unixODBC or some other compiler which does not support the APIs
4446 // necessary to use this function, so this function is not supported
4447 #ifdef __WXDEBUG__
4448 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
4449 #endif
4450 result = FALSE;
4451 #endif // __VISUALC__
4452
4453 return result;
4454
4455 } // wxDbCreateDataSource()
4456 #endif
4457
4458
4459 /********** wxDbGetDataSource() **********/
4460 bool wxDbGetDataSource(HENV henv, wxChar *Dsn, SWORD DsnMaxLength, wxChar *DsDesc,
4461 SWORD DsDescMaxLength, UWORD direction)
4462 /*
4463 * Dsn and DsDesc will contain the data source name and data source
4464 * description upon return
4465 */
4466 {
4467 SWORD cb1,cb2;
4468 SWORD lengthDsn = (SWORD)(DsnMaxLength*sizeof(wxChar));
4469 SWORD lengthDsDesc = (SWORD)(DsDescMaxLength*sizeof(wxChar));
4470
4471 if (SQLDataSources(henv, direction, (SQLTCHAR FAR *) Dsn, lengthDsn, &cb1,
4472 (SQLTCHAR FAR *) DsDesc, lengthDsDesc, &cb2) == SQL_SUCCESS)
4473 return true;
4474 else
4475 return false;
4476
4477 } // wxDbGetDataSource()
4478
4479
4480 // Change this to 0 to remove use of all deprecated functions
4481 #if wxODBC_BACKWARD_COMPATABILITY
4482 /********************************************************************
4483 ********************************************************************
4484 *
4485 * The following functions are all DEPRECATED and are included for
4486 * backward compatibility reasons only
4487 *
4488 ********************************************************************
4489 ********************************************************************/
4490 bool SqlLog(sqlLog state, const wxChar *filename)
4491 {
4492 return wxDbSqlLog((enum wxDbSqlLogState)state, filename);
4493 }
4494 /***** DEPRECATED: use wxGetDataSource() *****/
4495 bool GetDataSource(HENV henv, char *Dsn, SWORD DsnMax, char *DsDesc, SWORD DsDescMax,
4496 UWORD direction)
4497 {
4498 return wxDbGetDataSource(henv, Dsn, DsnMax, DsDesc, DsDescMax, direction);
4499 }
4500 /***** DEPRECATED: use wxDbGetConnection() *****/
4501 wxDb WXDLLIMPEXP_ODBC *GetDbConnection(DbStuff *pDbStuff, bool FwdOnlyCursors)
4502 {
4503 return wxDbGetConnection((wxDbConnectInf *)pDbStuff, FwdOnlyCursors);
4504 }
4505 /***** DEPRECATED: use wxDbFreeConnection() *****/
4506 bool WXDLLIMPEXP_ODBC FreeDbConnection(wxDb *pDb)
4507 {
4508 return wxDbFreeConnection(pDb);
4509 }
4510 /***** DEPRECATED: use wxDbCloseConnections() *****/
4511 void WXDLLIMPEXP_ODBC CloseDbConnections(void)
4512 {
4513 wxDbCloseConnections();
4514 }
4515 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
4516 int WXDLLIMPEXP_ODBC NumberDbConnectionsInUse(void)
4517 {
4518 return wxDbConnectionsInUse();
4519 }
4520 #endif
4521
4522
4523 #endif
4524 // wxUSE_ODBC
4525