]> git.saurik.com Git - wxWidgets.git/blob - src/common/db.cpp
Use stock labels. Native wxRadioButton. Getting position and size for the controls...
[wxWidgets.git] / src / common / db.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: db.cpp
3 // Purpose: Implementation of the wxDb class. The wxDb class represents a connection
4 // to an ODBC data source. The wxDb class allows operations on the data
5 // source such as opening and closing the data source.
6 // Author: Doug Card
7 // Modified by: George Tasker
8 // 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$
20 // Copyright: (c) 1996 Remstar International, Inc.
21 // Licence: wxWindows licence
22 ///////////////////////////////////////////////////////////////////////////////
23
24 /*
25 // SYNOPSIS START
26 // SYNOPSIS STOP
27 */
28 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
29 #pragma implementation "db.h"
30 #endif
31
32 #include "wx/wxprec.h"
33
34 #ifdef __BORLANDC__
35 #pragma hdrstop
36 #endif
37
38 #ifdef DBDEBUG_CONSOLE
39 #include "wx/ioswrap.h"
40 #endif
41
42 #ifndef WX_PRECOMP
43 #include "wx/string.h"
44 #include "wx/object.h"
45 #include "wx/list.h"
46 #include "wx/utils.h"
47 #include "wx/log.h"
48 #endif
49 #include "wx/filefn.h"
50 #include "wx/wxchar.h"
51
52 #if wxUSE_ODBC
53
54 #include <stdio.h>
55 #include <string.h>
56 #include <assert.h>
57 #include <stdlib.h>
58 #include <ctype.h>
59
60 #include "wx/db.h"
61
62 // DLL options compatibility check:
63 #include "wx/app.h"
64 WX_CHECK_BUILD_OPTIONS("wxODBC")
65
66 WXDLLIMPEXP_DATA_ODBC(wxDbList*) PtrBegDbList = 0;
67
68 wxChar const *SQL_LOG_FILENAME = wxT("sqllog.txt");
69 wxChar const *SQL_CATALOG_FILENAME = wxT("catalog.txt");
70
71 #ifdef __WXDEBUG__
72 extern wxList TablesInUse;
73 #endif
74
75 // SQL Log defaults to be used by GetDbConnection
76 wxDbSqlLogState SQLLOGstate = sqlLogOFF;
77
78 static wxString SQLLOGfn = SQL_LOG_FILENAME;
79
80 // The wxDb::errorList is copied to this variable when the wxDb object
81 // is closed. This way, the error list is still available after the
82 // database object is closed. This is necessary if the database
83 // connection fails so the calling application can show the operator
84 // why the connection failed. Note: as each wxDb object is closed, it
85 // will overwrite the errors of the previously destroyed wxDb object in
86 // this variable. NOTE: This occurs during a CLOSE, not a FREEing of the
87 // connection
88 wxChar DBerrorList[DB_MAX_ERROR_HISTORY][DB_MAX_ERROR_MSG_LEN];
89
90
91 // This type defines the return row-struct form
92 // SQLTablePrivileges, and is used by wxDB::TablePrivileges.
93 typedef struct
94 {
95 wxChar tableQual[128+1];
96 wxChar tableOwner[128+1];
97 wxChar tableName[128+1];
98 wxChar grantor[128+1];
99 wxChar grantee[128+1];
100 wxChar privilege[128+1];
101 wxChar grantable[3+1];
102 } wxDbTablePrivilegeInfo;
103
104
105 /********** wxDbConnectInf Constructor - form 1 **********/
106 wxDbConnectInf::wxDbConnectInf()
107 {
108 Henv = 0;
109 freeHenvOnDestroy = false;
110
111 Initialize();
112 } // Constructor
113
114
115 /********** wxDbConnectInf Constructor - form 2 **********/
116 wxDbConnectInf::wxDbConnectInf(HENV henv, const wxString &dsn, const wxString &userID,
117 const wxString &password, const wxString &defaultDir,
118 const wxString &fileType, const wxString &description)
119 {
120 Henv = 0;
121 freeHenvOnDestroy = false;
122
123 Initialize();
124
125 if (henv)
126 SetHenv(henv);
127 else
128 AllocHenv();
129
130 SetDsn(dsn);
131 SetUserID(userID);
132 SetPassword(password);
133 SetDescription(description);
134 SetFileType(fileType);
135 SetDefaultDir(defaultDir);
136 } // wxDbConnectInf Constructor
137
138
139 wxDbConnectInf::~wxDbConnectInf()
140 {
141 if (freeHenvOnDestroy)
142 {
143 FreeHenv();
144 }
145 } // wxDbConnectInf Destructor
146
147
148
149 /********** wxDbConnectInf::Initialize() **********/
150 bool wxDbConnectInf::Initialize()
151 {
152 freeHenvOnDestroy = false;
153
154 if (freeHenvOnDestroy && Henv)
155 FreeHenv();
156
157 Henv = 0;
158 Dsn[0] = 0;
159 Uid[0] = 0;
160 AuthStr[0] = 0;
161 ConnectionStr[0] = 0;
162 Description.Empty();
163 FileType.Empty();
164 DefaultDir.Empty();
165
166 useConnectionStr = false;
167
168 return true;
169 } // wxDbConnectInf::Initialize()
170
171
172 /********** wxDbConnectInf::AllocHenv() **********/
173 bool wxDbConnectInf::AllocHenv()
174 {
175 // This is here to help trap if you are getting a new henv
176 // without releasing an existing henv
177 wxASSERT(!Henv);
178
179 // Initialize the ODBC Environment for Database Operations
180 if (SQLAllocEnv(&Henv) != SQL_SUCCESS)
181 {
182 wxLogDebug(wxT("A problem occured while trying to get a connection to the data source"));
183 return false;
184 }
185
186 freeHenvOnDestroy = true;
187
188 return true;
189 } // wxDbConnectInf::AllocHenv()
190
191
192 void wxDbConnectInf::FreeHenv()
193 {
194 wxASSERT(Henv);
195
196 if (Henv)
197 SQLFreeEnv(Henv);
198
199 Henv = 0;
200 freeHenvOnDestroy = false;
201
202 } // wxDbConnectInf::FreeHenv()
203
204
205 void wxDbConnectInf::SetDsn(const wxString &dsn)
206 {
207 wxASSERT(dsn.Length() < sizeof(Dsn));
208
209 wxStrncpy(Dsn, dsn, sizeof(Dsn)-1);
210 Dsn[sizeof(Dsn)-1] = 0; // Prevent buffer overrun
211 } // wxDbConnectInf::SetDsn()
212
213
214 void wxDbConnectInf::SetUserID(const wxString &uid)
215 {
216 wxASSERT(uid.Length() < sizeof(Uid));
217 wxStrncpy(Uid, uid, sizeof(Uid)-1);
218 Uid[sizeof(Uid)-1] = 0; // Prevent buffer overrun
219 } // wxDbConnectInf::SetUserID()
220
221
222 void wxDbConnectInf::SetPassword(const wxString &password)
223 {
224 wxASSERT(password.Length() < sizeof(AuthStr));
225
226 wxStrncpy(AuthStr, password, sizeof(AuthStr)-1);
227 AuthStr[sizeof(AuthStr)-1] = 0; // Prevent buffer overrun
228 } // wxDbConnectInf::SetPassword()
229
230 void wxDbConnectInf::SetConnectionStr(const wxString &connectStr)
231 {
232 wxASSERT(connectStr.Length() < sizeof(ConnectionStr));
233
234 useConnectionStr = wxStrlen(connectStr) > 0;
235
236 wxStrncpy(ConnectionStr, connectStr, sizeof(ConnectionStr)-1);
237 ConnectionStr[sizeof(ConnectionStr)-1] = 0; // Prevent buffer overrun
238 } // wxDbConnectInf::SetConnectionStr()
239
240
241 /********** wxDbColFor Constructor **********/
242 wxDbColFor::wxDbColFor()
243 {
244 Initialize();
245 } // wxDbColFor::wxDbColFor()
246
247
248 /********** wxDbColFor::Initialize() **********/
249 void wxDbColFor::Initialize()
250 {
251 s_Field.Empty();
252 int i;
253 for (i=0; i<7; i++)
254 {
255 s_Format[i].Empty();
256 s_Amount[i].Empty();
257 i_Amount[i] = 0;
258 }
259 i_Nation = 0; // 0=EU, 1=UK, 2=International, 3=US
260 i_dbDataType = 0;
261 i_sqlDataType = 0;
262 Format(1,DB_DATA_TYPE_VARCHAR,0,0,0); // the Function that does the work
263 } // wxDbColFor::Initialize()
264
265
266 /********** wxDbColFor::Format() **********/
267 int wxDbColFor::Format(int Nation, int dbDataType, SWORD sqlDataType,
268 short columnLength, short decimalDigits)
269 {
270 // ----------------------------------------------------------------------------------------
271 // -- 19991224 : mj10777 : Create
272 // There is still a lot of work to do here, but it is a start
273 // It handles all the basic data-types that I have run into up to now
274 // The main work will have be with Dates and float Formatting
275 // (US 1,000.00 ; EU 1.000,00)
276 // There are wxWindow plans for locale support and the new wxDateTime. If
277 // they define some constants (wxEUROPEAN) that can be gloably used,
278 // they should be used here.
279 // ----------------------------------------------------------------------------------------
280 // There should also be a function to scan in a string to fill the variable
281 // ----------------------------------------------------------------------------------------
282 wxString tempStr;
283 i_Nation = Nation; // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
284 i_dbDataType = dbDataType;
285 i_sqlDataType = sqlDataType;
286 s_Field.Printf(wxT("%s%d"),s_Amount[1].c_str(),i_Amount[1]); // OK for VARCHAR, INTEGER and FLOAT
287
288 if (i_dbDataType == 0) // Filter unsupported dbDataTypes
289 {
290 if ((i_sqlDataType == SQL_VARCHAR) || (i_sqlDataType == SQL_LONGVARCHAR) ||
291 (i_sqlDataType == SQL_WCHAR) || (i_sqlDataType == SQL_WVARCHAR))
292 i_dbDataType = DB_DATA_TYPE_VARCHAR;
293 if ((i_sqlDataType == SQL_C_DATE) || (i_sqlDataType == SQL_C_TIMESTAMP))
294 i_dbDataType = DB_DATA_TYPE_DATE;
295 if (i_sqlDataType == SQL_C_BIT)
296 i_dbDataType = DB_DATA_TYPE_INTEGER;
297 if (i_sqlDataType == SQL_NUMERIC)
298 i_dbDataType = DB_DATA_TYPE_VARCHAR; // glt - ??? is this right?
299 if (i_sqlDataType == SQL_REAL)
300 i_dbDataType = DB_DATA_TYPE_FLOAT;
301 if (i_sqlDataType == SQL_C_BINARY)
302 i_dbDataType = DB_DATA_TYPE_BLOB;
303 }
304
305 if ((i_dbDataType == DB_DATA_TYPE_INTEGER) && (i_sqlDataType == SQL_C_DOUBLE))
306 { // DBASE Numeric
307 i_dbDataType = DB_DATA_TYPE_FLOAT;
308 }
309
310 switch(i_dbDataType) // TBD: Still a lot of proper formatting to do
311 {
312 case DB_DATA_TYPE_VARCHAR:
313 s_Field = wxT("%s");
314 break;
315 case DB_DATA_TYPE_INTEGER:
316 s_Field = wxT("%d");
317 break;
318 case DB_DATA_TYPE_FLOAT:
319 if (decimalDigits == 0)
320 decimalDigits = 2;
321 tempStr = wxT("%");
322 tempStr.Printf(wxT("%s%d.%d"), tempStr.c_str(),columnLength, decimalDigits);
323 s_Field.Printf(wxT("%sf"), tempStr.c_str());
324 break;
325 case DB_DATA_TYPE_DATE:
326 if (i_Nation == 0) // timestamp YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
327 {
328 s_Field = wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
329 }
330 if (i_Nation == 1) // European DD.MM.YYYY HH:MM:SS.SSS
331 {
332 s_Field = wxT("%02d.%02d.%04d %02d:%02d:%02d.%03d");
333 }
334 if (i_Nation == 2) // UK DD/MM/YYYY HH:MM:SS.SSS
335 {
336 s_Field = wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
337 }
338 if (i_Nation == 3) // International YYYY-MM-DD HH:MM:SS.SSS
339 {
340 s_Field = wxT("%04d-%02d-%02d %02d:%02d:%02d.%03d");
341 }
342 if (i_Nation == 4) // US MM/DD/YYYY HH:MM:SS.SSS
343 {
344 s_Field = wxT("%02d/%02d/%04d %02d:%02d:%02d.%03d");
345 }
346 break;
347 case DB_DATA_TYPE_BLOB:
348 s_Field.Printf(wxT("Unable to format(%d)-SQL(%d)"), dbDataType,sqlDataType); //
349 break;
350 default:
351 s_Field.Printf(wxT("Unknown Format(%d)-SQL(%d)"), dbDataType,sqlDataType); //
352 break;
353 };
354 return TRUE;
355 } // wxDbColFor::Format()
356
357
358 /********** wxDbColInf Constructor **********/
359 wxDbColInf::wxDbColInf()
360 {
361 Initialize();
362 } // wxDbColInf::wxDbColInf()
363
364
365 /********** wxDbColInf Destructor ********/
366 wxDbColInf::~wxDbColInf()
367 {
368 if (pColFor)
369 delete pColFor;
370 pColFor = NULL;
371 } // wxDbColInf::~wxDbColInf()
372
373
374 bool wxDbColInf::Initialize()
375 {
376 catalog[0] = 0;
377 schema[0] = 0;
378 tableName[0] = 0;
379 colName[0] = 0;
380 sqlDataType = 0;
381 typeName[0] = 0;
382 columnLength = 0;
383 bufferSize = 0;
384 decimalDigits = 0;
385 numPrecRadix = 0;
386 nullable = 0;
387 remarks[0] = 0;
388 dbDataType = 0;
389 PkCol = 0;
390 PkTableName[0] = 0;
391 FkCol = 0;
392 FkTableName[0] = 0;
393 pColFor = NULL;
394
395 return true;
396 } // wxDbColInf::Initialize()
397
398
399 /********** wxDbTableInf Constructor ********/
400 wxDbTableInf::wxDbTableInf()
401 {
402 Initialize();
403 } // wxDbTableInf::wxDbTableInf()
404
405
406 /********** wxDbTableInf Constructor ********/
407 wxDbTableInf::~wxDbTableInf()
408 {
409 if (pColInf)
410 delete [] pColInf;
411 pColInf = NULL;
412 } // wxDbTableInf::~wxDbTableInf()
413
414
415 bool wxDbTableInf::Initialize()
416 {
417 tableName[0] = 0;
418 tableType[0] = 0;
419 tableRemarks[0] = 0;
420 numCols = 0;
421 pColInf = NULL;
422
423 return true;
424 } // wxDbTableInf::Initialize()
425
426
427 /********** wxDbInf Constructor *************/
428 wxDbInf::wxDbInf()
429 {
430 Initialize();
431 } // wxDbInf::wxDbInf()
432
433
434 /********** wxDbInf Destructor *************/
435 wxDbInf::~wxDbInf()
436 {
437 if (pTableInf)
438 delete [] pTableInf;
439 pTableInf = NULL;
440 } // wxDbInf::~wxDbInf()
441
442
443 /********** wxDbInf::Initialize() *************/
444 bool wxDbInf::Initialize()
445 {
446 catalog[0] = 0;
447 schema[0] = 0;
448 numTables = 0;
449 pTableInf = NULL;
450
451 return true;
452 } // wxDbInf::Initialize()
453
454
455 /********** wxDb Constructor **********/
456 wxDb::wxDb(const HENV &aHenv, bool FwdOnlyCursors)
457 {
458 // Copy the HENV into the db class
459 henv = aHenv;
460 fwdOnlyCursors = FwdOnlyCursors;
461
462 initialize();
463 } // wxDb::wxDb()
464
465
466 /********** wxDb Destructor **********/
467 wxDb::~wxDb()
468 {
469 wxASSERT_MSG(!IsCached(),wxT("Cached connections must not be manually deleted, use\nwxDbFreeConnection() or wxDbCloseConnections()."));
470
471 if (IsOpen())
472 {
473 Close();
474 }
475 } // wxDb destructor
476
477
478
479 /********** PRIVATE! wxDb::initialize PRIVATE! **********/
480 /********** wxDb::initialize() **********/
481 void wxDb::initialize()
482 /*
483 * Private member function that sets all wxDb member variables to
484 * known values at creation of the wxDb
485 */
486 {
487 int i;
488
489 fpSqlLog = 0; // Sql Log file pointer
490 sqlLogState = sqlLogOFF; // By default, logging is turned off
491 nTables = 0;
492 dbmsType = dbmsUNIDENTIFIED;
493
494 wxStrcpy(sqlState,wxEmptyString);
495 wxStrcpy(errorMsg,wxEmptyString);
496 nativeError = cbErrorMsg = 0;
497 for (i = 0; i < DB_MAX_ERROR_HISTORY; i++)
498 wxStrcpy(errorList[i], wxEmptyString);
499
500 // Init typeInf structures
501 typeInfVarchar.TypeName.Empty();
502 typeInfVarchar.FsqlType = 0;
503 typeInfVarchar.Precision = 0;
504 typeInfVarchar.CaseSensitive = 0;
505 typeInfVarchar.MaximumScale = 0;
506
507 typeInfInteger.TypeName.Empty();
508 typeInfInteger.FsqlType = 0;
509 typeInfInteger.Precision = 0;
510 typeInfInteger.CaseSensitive = 0;
511 typeInfInteger.MaximumScale = 0;
512
513 typeInfFloat.TypeName.Empty();
514 typeInfFloat.FsqlType = 0;
515 typeInfFloat.Precision = 0;
516 typeInfFloat.CaseSensitive = 0;
517 typeInfFloat.MaximumScale = 0;
518
519 typeInfDate.TypeName.Empty();
520 typeInfDate.FsqlType = 0;
521 typeInfDate.Precision = 0;
522 typeInfDate.CaseSensitive = 0;
523 typeInfDate.MaximumScale = 0;
524
525 typeInfBlob.TypeName.Empty();
526 typeInfBlob.FsqlType = 0;
527 typeInfBlob.Precision = 0;
528 typeInfBlob.CaseSensitive = 0;
529 typeInfBlob.MaximumScale = 0;
530
531 // Error reporting is turned OFF by default
532 silent = true;
533
534 // Allocate a data source connection handle
535 if (SQLAllocConnect(henv, &hdbc) != SQL_SUCCESS)
536 DispAllErrors(henv);
537
538 // Initialize the db status flag
539 DB_STATUS = 0;
540
541 // Mark database as not open as of yet
542 dbIsOpen = false;
543 dbIsCached = false;
544 dbOpenedWithConnectionString = false;
545 } // wxDb::initialize()
546
547
548 /********** PRIVATE! wxDb::convertUserID PRIVATE! **********/
549 //
550 // NOTE: Return value from this function MUST be copied
551 // immediately, as the value is not good after
552 // this function has left scope.
553 //
554 const wxChar *wxDb::convertUserID(const wxChar *userID, wxString &UserID)
555 {
556 if (userID)
557 {
558 if (!wxStrlen(userID))
559 UserID = uid;
560 else
561 UserID = userID;
562 }
563 else
564 UserID.Empty();
565
566 // dBase does not use user names, and some drivers fail if you try to pass one
567 if ( Dbms() == dbmsDBASE
568 || Dbms() == dbmsXBASE_SEQUITER )
569 UserID.Empty();
570
571 // Oracle user names may only be in uppercase, so force
572 // the name to uppercase
573 if (Dbms() == dbmsORACLE)
574 UserID = UserID.Upper();
575
576 return UserID.c_str();
577 } // wxDb::convertUserID()
578
579
580 bool wxDb::determineDataTypes(bool failOnDataTypeUnsupported)
581 {
582 size_t iIndex;
583
584 // These are the possible SQL types we check for use against the datasource we are connected
585 // to for the purpose of determining which data type to use for the basic character strings
586 // column types
587 //
588 // NOTE: The first type in this enumeration that is determined to be supported by the
589 // datasource/driver is the one that will be used.
590 SWORD PossibleSqlCharTypes[] = {
591 #if wxUSE_UNICODE && defined(SQL_WVARCHAR)
592 SQL_WVARCHAR,
593 #endif
594 SQL_VARCHAR,
595 #if wxUSE_UNICODE && defined(SQL_WVARCHAR)
596 SQL_WCHAR,
597 #endif
598 SQL_CHAR
599 };
600
601 // These are the possible SQL types we check for use against the datasource we are connected
602 // to for the purpose of determining which data type to use for the basic non-floating point
603 // column types
604 //
605 // NOTE: The first type in this enumeration that is determined to be supported by the
606 // datasource/driver is the one that will be used.
607 SWORD PossibleSqlIntegerTypes[] = {
608 SQL_INTEGER
609 };
610
611 // These are the possible SQL types we check for use against the datasource we are connected
612 // to for the purpose of determining which data type to use for the basic floating point number
613 // column types
614 //
615 // NOTE: The first type in this enumeration that is determined to be supported by the
616 // datasource/driver is the one that will be used.
617 SWORD PossibleSqlFloatTypes[] = {
618 SQL_DOUBLE,
619 SQL_REAL,
620 SQL_FLOAT,
621 SQL_DECIMAL,
622 SQL_NUMERIC
623 };
624
625 // These are the possible SQL types we check for use agains the datasource we are connected
626 // to for the purpose of determining which data type to use for the date/time column types
627 //
628 // NOTE: The first type in this enumeration that is determined to be supported by the
629 // datasource/driver is the one that will be used.
630 SWORD PossibleSqlDateTypes[] = {
631 SQL_TIMESTAMP,
632 SQL_DATE,
633 #ifdef SQL_DATETIME
634 SQL_DATETIME
635 #endif
636 };
637
638 // These are the possible SQL types we check for use agains the datasource we are connected
639 // to for the purpose of determining which data type to use for the BLOB column types.
640 //
641 // NOTE: The first type in this enumeration that is determined to be supported by the
642 // datasource/driver is the one that will be used.
643 SWORD PossibleSqlBlobTypes[] = {
644 SQL_LONGVARBINARY,
645 SQL_VARBINARY
646 };
647
648
649 // Query the data source regarding data type information
650
651 //
652 // The way it was determined which SQL data types to use was by calling SQLGetInfo
653 // for all of the possible SQL data types to see which ones were supported. If
654 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
655 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
656 // types I've selected below will not always be what we want. These are just
657 // what happened to work against an Oracle 7/Intersolv combination. The following is
658 // a complete list of the results I got back against the Oracle 7 database:
659 //
660 // SQL_BIGINT SQL_NO_DATA_FOUND
661 // SQL_BINARY SQL_NO_DATA_FOUND
662 // SQL_BIT SQL_NO_DATA_FOUND
663 // SQL_CHAR type name = 'CHAR', Precision = 255
664 // SQL_DATE SQL_NO_DATA_FOUND
665 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
666 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
667 // SQL_FLOAT SQL_NO_DATA_FOUND
668 // SQL_INTEGER SQL_NO_DATA_FOUND
669 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
670 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
671 // SQL_NUMERIC SQL_NO_DATA_FOUND
672 // SQL_REAL SQL_NO_DATA_FOUND
673 // SQL_SMALLINT SQL_NO_DATA_FOUND
674 // SQL_TIME SQL_NO_DATA_FOUND
675 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
676 // SQL_VARBINARY type name = 'RAW', Precision = 255
677 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
678 // =====================================================================
679 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
680 //
681 // SQL_VARCHAR type name = 'TEXT', Precision = 255
682 // SQL_TIMESTAMP type name = 'DATETIME'
683 // SQL_DECIMAL SQL_NO_DATA_FOUND
684 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
685 // SQL_FLOAT SQL_NO_DATA_FOUND
686 // SQL_REAL type name = 'SINGLE', Precision = 7
687 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
688 // SQL_INTEGER type name = 'LONG', Precision = 10
689
690 // Query the data source for info about itself
691 if (!getDbInfo(failOnDataTypeUnsupported))
692 return false;
693
694 // --------------- Varchar - (Variable length character string) ---------------
695 for (iIndex = 0; iIndex < WXSIZEOF(PossibleSqlCharTypes) &&
696 !getDataTypeInfo(PossibleSqlCharTypes[iIndex], typeInfVarchar); ++iIndex)
697 {}
698
699 if (iIndex < WXSIZEOF(PossibleSqlCharTypes))
700 typeInfVarchar.FsqlType = PossibleSqlCharTypes[iIndex];
701 else if (failOnDataTypeUnsupported)
702 return false;
703
704 // --------------- Float ---------------
705 for (iIndex = 0; iIndex < WXSIZEOF(PossibleSqlFloatTypes) &&
706 !getDataTypeInfo(PossibleSqlFloatTypes[iIndex], typeInfFloat); ++iIndex)
707 {}
708
709 if (iIndex < WXSIZEOF(PossibleSqlFloatTypes))
710 typeInfFloat.FsqlType = PossibleSqlFloatTypes[iIndex];
711 else if (failOnDataTypeUnsupported)
712 return false;
713
714 // --------------- Integer -------------
715 for (iIndex = 0; iIndex < WXSIZEOF(PossibleSqlIntegerTypes) &&
716 !getDataTypeInfo(PossibleSqlIntegerTypes[iIndex], typeInfInteger); ++iIndex)
717 {}
718
719 if (iIndex < WXSIZEOF(PossibleSqlIntegerTypes))
720 typeInfInteger.FsqlType = PossibleSqlIntegerTypes[iIndex];
721 else if (failOnDataTypeUnsupported)
722 {
723 // If no non-floating point data types are supported, we'll
724 // use the type assigned for floats to store integers as well
725 if (!getDataTypeInfo(typeInfFloat.FsqlType, typeInfInteger))
726 {
727 if (failOnDataTypeUnsupported)
728 return false;
729 }
730 else
731 typeInfInteger.FsqlType = typeInfFloat.FsqlType;
732 }
733
734 // --------------- Date/Time ---------------
735 for (iIndex = 0; iIndex < WXSIZEOF(PossibleSqlDateTypes) &&
736 !getDataTypeInfo(PossibleSqlDateTypes[iIndex], typeInfDate); ++iIndex)
737 {}
738
739 if (iIndex < WXSIZEOF(PossibleSqlDateTypes))
740 typeInfDate.FsqlType = PossibleSqlDateTypes[iIndex];
741 else if (failOnDataTypeUnsupported)
742 return false;
743
744 // --------------- BLOB ---------------
745 for (iIndex = 0; iIndex < WXSIZEOF(PossibleSqlBlobTypes) &&
746 !getDataTypeInfo(PossibleSqlBlobTypes[iIndex], typeInfBlob); ++iIndex)
747 {}
748
749 if (iIndex < WXSIZEOF(PossibleSqlBlobTypes))
750 typeInfBlob.FsqlType = PossibleSqlBlobTypes[iIndex];
751 else if (failOnDataTypeUnsupported)
752 return false;
753
754 return true;
755 } // wxDb::determineDataTypes
756
757
758 bool wxDb::open(bool failOnDataTypeUnsupported)
759 {
760 /*
761 If using Intersolv branded ODBC drivers, this is the place where you would substitute
762 your branded driver license information
763
764 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
765 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
766 */
767
768 // Mark database as open
769 dbIsOpen = true;
770
771 // Allocate a statement handle for the database connection
772 if (SQLAllocStmt(hdbc, &hstmt) != SQL_SUCCESS)
773 return(DispAllErrors(henv, hdbc));
774
775 // Set Connection Options
776 if (!setConnectionOptions())
777 return false;
778
779 if (!determineDataTypes(failOnDataTypeUnsupported))
780 return false;
781
782 #ifdef DBDEBUG_CONSOLE
783 cout << wxT("VARCHAR DATA TYPE: ") << typeInfVarchar.TypeName << endl;
784 cout << wxT("INTEGER DATA TYPE: ") << typeInfInteger.TypeName << endl;
785 cout << wxT("FLOAT DATA TYPE: ") << typeInfFloat.TypeName << endl;
786 cout << wxT("DATE DATA TYPE: ") << typeInfDate.TypeName << endl;
787 cout << wxT("BLOB DATA TYPE: ") << typeInfBlob.TypeName << endl;
788 cout << endl;
789 #endif
790
791 // Completed Successfully
792 return true;
793 }
794
795 bool wxDb::Open(const wxString& inConnectStr, bool failOnDataTypeUnsupported)
796 {
797 wxASSERT(inConnectStr.Length());
798 dsn = wxT("");
799 uid = wxT("");
800 authStr = wxT("");
801
802 RETCODE retcode;
803
804 if (!FwdOnlyCursors())
805 {
806 // Specify that the ODBC cursor library be used, if needed. This must be
807 // specified before the connection is made.
808 retcode = SQLSetConnectOption(hdbc, SQL_ODBC_CURSORS, SQL_CUR_USE_IF_NEEDED);
809
810 #ifdef DBDEBUG_CONSOLE
811 if (retcode == SQL_SUCCESS)
812 cout << wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl;
813 else
814 cout << wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl;
815 #else
816 wxUnusedVar(retcode);
817 #endif
818 }
819
820 // Connect to the data source
821 SQLTCHAR outConnectBuffer[SQL_MAX_CONNECTSTR_LEN+1]; // MS recommends at least 1k buffer
822 short outConnectBufferLen;
823
824 inConnectionStr = inConnectStr;
825
826 retcode = SQLDriverConnect(hdbc, NULL, (SQLTCHAR FAR *)inConnectionStr.c_str(),
827 (SWORD)inConnectionStr.Length(), (SQLTCHAR FAR *)outConnectBuffer,
828 sizeof(outConnectBuffer), &outConnectBufferLen, SQL_DRIVER_COMPLETE );
829
830 if ((retcode != SQL_SUCCESS) &&
831 (retcode != SQL_SUCCESS_WITH_INFO))
832 return(DispAllErrors(henv, hdbc));
833
834 outConnectBuffer[outConnectBufferLen] = 0;
835 outConnectionStr = outConnectBuffer;
836 dbOpenedWithConnectionString = true;
837
838 return open(failOnDataTypeUnsupported);
839 }
840
841 /********** wxDb::Open() **********/
842 bool wxDb::Open(const wxString &Dsn, const wxString &Uid, const wxString &AuthStr, bool failOnDataTypeUnsupported)
843 {
844 wxASSERT(Dsn.Length());
845 dsn = Dsn;
846 uid = Uid;
847 authStr = AuthStr;
848
849 inConnectionStr = wxT("");
850 outConnectionStr = wxT("");
851
852 RETCODE retcode;
853
854 if (!FwdOnlyCursors())
855 {
856 // Specify that the ODBC cursor library be used, if needed. This must be
857 // specified before the connection is made.
858 retcode = SQLSetConnectOption(hdbc, SQL_ODBC_CURSORS, SQL_CUR_USE_IF_NEEDED);
859
860 #ifdef DBDEBUG_CONSOLE
861 if (retcode == SQL_SUCCESS)
862 cout << wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl;
863 else
864 cout << wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl;
865 #else
866 wxUnusedVar( retcode );
867 #endif
868 }
869
870 // Connect to the data source
871 retcode = SQLConnect(hdbc, (SQLTCHAR FAR *) dsn.c_str(), SQL_NTS,
872 (SQLTCHAR FAR *) uid.c_str(), SQL_NTS,
873 (SQLTCHAR FAR *) authStr.c_str(), SQL_NTS);
874
875 if ((retcode != SQL_SUCCESS) &&
876 (retcode != SQL_SUCCESS_WITH_INFO))
877 return(DispAllErrors(henv, hdbc));
878
879 return open(failOnDataTypeUnsupported);
880
881 } // wxDb::Open()
882
883
884 bool wxDb::Open(wxDbConnectInf *dbConnectInf, bool failOnDataTypeUnsupported)
885 {
886 wxASSERT(dbConnectInf);
887
888 // Use the connection string if one is present
889 if (dbConnectInf->UseConnectionStr())
890 return Open(GetConnectionInStr(), failOnDataTypeUnsupported);
891 else
892 return Open(dbConnectInf->GetDsn(), dbConnectInf->GetUserID(),
893 dbConnectInf->GetPassword(), failOnDataTypeUnsupported);
894 } // wxDb::Open()
895
896
897 bool wxDb::Open(wxDb *copyDb)
898 {
899 dsn = copyDb->GetDatasourceName();
900 uid = copyDb->GetUsername();
901 authStr = copyDb->GetPassword();
902 inConnectionStr = copyDb->GetConnectionInStr();
903 outConnectionStr = copyDb->GetConnectionOutStr();
904
905 RETCODE retcode;
906
907 if (!FwdOnlyCursors())
908 {
909 // Specify that the ODBC cursor library be used, if needed. This must be
910 // specified before the connection is made.
911 retcode = SQLSetConnectOption(hdbc, SQL_ODBC_CURSORS, SQL_CUR_USE_IF_NEEDED);
912
913 #ifdef DBDEBUG_CONSOLE
914 if (retcode == SQL_SUCCESS)
915 cout << wxT("SQLSetConnectOption(CURSOR_LIB) successful") << endl;
916 else
917 cout << wxT("SQLSetConnectOption(CURSOR_LIB) failed") << endl;
918 #else
919 wxUnusedVar( retcode );
920 #endif
921 }
922
923 if (copyDb->OpenedWithConnectionString())
924 {
925 // Connect to the data source
926 SQLTCHAR outConnectBuffer[SQL_MAX_CONNECTSTR_LEN+1];
927 short outConnectBufferLen;
928
929 inConnectionStr = copyDb->GetConnectionInStr();
930
931 retcode = SQLDriverConnect(hdbc, NULL, (SQLTCHAR FAR *)inConnectionStr.c_str(),
932 (SWORD)inConnectionStr.Length(), (SQLTCHAR FAR *)outConnectBuffer,
933 sizeof(outConnectBuffer), &outConnectBufferLen, SQL_DRIVER_COMPLETE);
934
935 if ((retcode != SQL_SUCCESS) &&
936 (retcode != SQL_SUCCESS_WITH_INFO))
937 return(DispAllErrors(henv, hdbc));
938
939 outConnectBuffer[outConnectBufferLen] = 0;
940 outConnectionStr = outConnectBuffer;
941 dbOpenedWithConnectionString = true;
942 }
943 else
944 {
945 // Connect to the data source
946 retcode = SQLConnect(hdbc, (SQLTCHAR FAR *) dsn.c_str(), SQL_NTS,
947 (SQLTCHAR FAR *) uid.c_str(), SQL_NTS,
948 (SQLTCHAR FAR *) authStr.c_str(), SQL_NTS);
949 }
950
951 if ((retcode != SQL_SUCCESS) &&
952 (retcode != SQL_SUCCESS_WITH_INFO))
953 return(DispAllErrors(henv, hdbc));
954
955 /*
956 If using Intersolv branded ODBC drivers, this is the place where you would substitute
957 your branded driver license information
958
959 SQLSetConnectOption(hdbc, 1041, (UDWORD) wxEmptyString);
960 SQLSetConnectOption(hdbc, 1042, (UDWORD) wxEmptyString);
961 */
962
963 // Mark database as open
964 dbIsOpen = true;
965
966 // Allocate a statement handle for the database connection
967 if (SQLAllocStmt(hdbc, &hstmt) != SQL_SUCCESS)
968 return(DispAllErrors(henv, hdbc));
969
970 // Set Connection Options
971 if (!setConnectionOptions())
972 return false;
973
974 // Instead of Querying the data source for info about itself, it can just be copied
975 // from the wxDb instance that was passed in (copyDb).
976 wxStrcpy(dbInf.serverName,copyDb->dbInf.serverName);
977 wxStrcpy(dbInf.databaseName,copyDb->dbInf.databaseName);
978 wxStrcpy(dbInf.dbmsName,copyDb->dbInf.dbmsName);
979 wxStrcpy(dbInf.dbmsVer,copyDb->dbInf.dbmsVer);
980 dbInf.maxConnections = copyDb->dbInf.maxConnections;
981 dbInf.maxStmts = copyDb->dbInf.maxStmts;
982 wxStrcpy(dbInf.driverName,copyDb->dbInf.driverName);
983 wxStrcpy(dbInf.odbcVer,copyDb->dbInf.odbcVer);
984 wxStrcpy(dbInf.drvMgrOdbcVer,copyDb->dbInf.drvMgrOdbcVer);
985 wxStrcpy(dbInf.driverVer,copyDb->dbInf.driverVer);
986 dbInf.apiConfLvl = copyDb->dbInf.apiConfLvl;
987 dbInf.cliConfLvl = copyDb->dbInf.cliConfLvl;
988 dbInf.sqlConfLvl = copyDb->dbInf.sqlConfLvl;
989 wxStrcpy(dbInf.outerJoins,copyDb->dbInf.outerJoins);
990 wxStrcpy(dbInf.procedureSupport,copyDb->dbInf.procedureSupport);
991 wxStrcpy(dbInf.accessibleTables,copyDb->dbInf.accessibleTables);
992 dbInf.cursorCommitBehavior = copyDb->dbInf.cursorCommitBehavior;
993 dbInf.cursorRollbackBehavior = copyDb->dbInf.cursorRollbackBehavior;
994 dbInf.supportNotNullClause = copyDb->dbInf.supportNotNullClause;
995 wxStrcpy(dbInf.supportIEF,copyDb->dbInf.supportIEF);
996 dbInf.txnIsolation = copyDb->dbInf.txnIsolation;
997 dbInf.txnIsolationOptions = copyDb->dbInf.txnIsolationOptions;
998 dbInf.fetchDirections = copyDb->dbInf.fetchDirections;
999 dbInf.lockTypes = copyDb->dbInf.lockTypes;
1000 dbInf.posOperations = copyDb->dbInf.posOperations;
1001 dbInf.posStmts = copyDb->dbInf.posStmts;
1002 dbInf.scrollConcurrency = copyDb->dbInf.scrollConcurrency;
1003 dbInf.scrollOptions = copyDb->dbInf.scrollOptions;
1004 dbInf.staticSensitivity = copyDb->dbInf.staticSensitivity;
1005 dbInf.txnCapable = copyDb->dbInf.txnCapable;
1006 dbInf.loginTimeout = copyDb->dbInf.loginTimeout;
1007
1008 // VARCHAR = Variable length character string
1009 typeInfVarchar.FsqlType = copyDb->typeInfVarchar.FsqlType;
1010 typeInfVarchar.TypeName = copyDb->typeInfVarchar.TypeName;
1011 typeInfVarchar.Precision = copyDb->typeInfVarchar.Precision;
1012 typeInfVarchar.CaseSensitive = copyDb->typeInfVarchar.CaseSensitive;
1013 typeInfVarchar.MaximumScale = copyDb->typeInfVarchar.MaximumScale;
1014
1015 // Float
1016 typeInfFloat.FsqlType = copyDb->typeInfFloat.FsqlType;
1017 typeInfFloat.TypeName = copyDb->typeInfFloat.TypeName;
1018 typeInfFloat.Precision = copyDb->typeInfFloat.Precision;
1019 typeInfFloat.CaseSensitive = copyDb->typeInfFloat.CaseSensitive;
1020 typeInfFloat.MaximumScale = copyDb->typeInfFloat.MaximumScale;
1021
1022 // Integer
1023 typeInfInteger.FsqlType = copyDb->typeInfInteger.FsqlType;
1024 typeInfInteger.TypeName = copyDb->typeInfInteger.TypeName;
1025 typeInfInteger.Precision = copyDb->typeInfInteger.Precision;
1026 typeInfInteger.CaseSensitive = copyDb->typeInfInteger.CaseSensitive;
1027 typeInfInteger.MaximumScale = copyDb->typeInfInteger.MaximumScale;
1028
1029 // Date/Time
1030 typeInfDate.FsqlType = copyDb->typeInfDate.FsqlType;
1031 typeInfDate.TypeName = copyDb->typeInfDate.TypeName;
1032 typeInfDate.Precision = copyDb->typeInfDate.Precision;
1033 typeInfDate.CaseSensitive = copyDb->typeInfDate.CaseSensitive;
1034 typeInfDate.MaximumScale = copyDb->typeInfDate.MaximumScale;
1035
1036 // Blob
1037 typeInfBlob.FsqlType = copyDb->typeInfBlob.FsqlType;
1038 typeInfBlob.TypeName = copyDb->typeInfBlob.TypeName;
1039 typeInfBlob.Precision = copyDb->typeInfBlob.Precision;
1040 typeInfBlob.CaseSensitive = copyDb->typeInfBlob.CaseSensitive;
1041 typeInfBlob.MaximumScale = copyDb->typeInfBlob.MaximumScale;
1042
1043 #ifdef DBDEBUG_CONSOLE
1044 cout << wxT("VARCHAR DATA TYPE: ") << typeInfVarchar.TypeName << endl;
1045 cout << wxT("INTEGER DATA TYPE: ") << typeInfInteger.TypeName << endl;
1046 cout << wxT("FLOAT DATA TYPE: ") << typeInfFloat.TypeName << endl;
1047 cout << wxT("DATE DATA TYPE: ") << typeInfDate.TypeName << endl;
1048 cout << wxT("BLOB DATA TYPE: ") << typeInfBlob.TypeName << endl;
1049 cout << endl;
1050 #endif
1051
1052 // Completed Successfully
1053 return true;
1054 } // wxDb::Open() 2
1055
1056
1057 /********** wxDb::setConnectionOptions() **********/
1058 bool wxDb::setConnectionOptions(void)
1059 /*
1060 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
1061 */
1062 {
1063 SWORD cb;
1064
1065 // I need to get the DBMS name here, because some of the connection options
1066 // are database specific and need to call the Dbms() function.
1067 RETCODE retcode;
1068
1069 retcode = SQLGetInfo(hdbc, SQL_DBMS_NAME, (UCHAR *) dbInf.dbmsName, sizeof(dbInf.dbmsName), &cb);
1070 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
1071 return(DispAllErrors(henv, hdbc));
1072
1073 /* retcode = */ SQLSetConnectOption(hdbc, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF);
1074 /* retcode = */ SQLSetConnectOption(hdbc, SQL_OPT_TRACE, SQL_OPT_TRACE_OFF);
1075 // SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED); // No dirty reads
1076
1077 // By default, MS Sql Server closes cursors on commit and rollback. The following
1078 // call to SQLSetConnectOption() is needed to force SQL Server to preserve cursors
1079 // after a transaction. This is a driver specific option and is not part of the
1080 // ODBC standard. Note: this behavior is specific to the ODBC interface to SQL Server.
1081 // The database settings don't have any effect one way or the other.
1082 if (Dbms() == dbmsMS_SQL_SERVER)
1083 {
1084 const long SQL_PRESERVE_CURSORS = 1204L;
1085 const long SQL_PC_ON = 1L;
1086 /* retcode = */ SQLSetConnectOption(hdbc, SQL_PRESERVE_CURSORS, SQL_PC_ON);
1087 }
1088
1089 // Display the connection options to verify them
1090 #ifdef DBDEBUG_CONSOLE
1091 long l;
1092 cout << wxT("****** CONNECTION OPTIONS ******") << endl;
1093
1094 retcode = SQLGetConnectOption(hdbc, SQL_AUTOCOMMIT, &l);
1095 if (retcode != SQL_SUCCESS)
1096 return(DispAllErrors(henv, hdbc));
1097 cout << wxT("AUTOCOMMIT: ") << (l == SQL_AUTOCOMMIT_OFF ? "OFF" : "ON") << endl;
1098
1099 retcode = SQLGetConnectOption(hdbc, SQL_ODBC_CURSORS, &l);
1100 if (retcode != SQL_SUCCESS)
1101 return(DispAllErrors(henv, hdbc));
1102 cout << wxT("ODBC CURSORS: ");
1103 switch(l)
1104 {
1105 case(SQL_CUR_USE_IF_NEEDED):
1106 cout << wxT("SQL_CUR_USE_IF_NEEDED");
1107 break;
1108 case(SQL_CUR_USE_ODBC):
1109 cout << wxT("SQL_CUR_USE_ODBC");
1110 break;
1111 case(SQL_CUR_USE_DRIVER):
1112 cout << wxT("SQL_CUR_USE_DRIVER");
1113 break;
1114 }
1115 cout << endl;
1116
1117 retcode = SQLGetConnectOption(hdbc, SQL_OPT_TRACE, &l)
1118 if (retcode != SQL_SUCCESS)
1119 return(DispAllErrors(henv, hdbc));
1120 cout << wxT("TRACING: ") << (l == SQL_OPT_TRACE_OFF ? wxT("OFF") : wxT("ON")) << endl;
1121
1122 cout << endl;
1123 #endif
1124
1125 // Completed Successfully
1126 return true;
1127
1128 } // wxDb::setConnectionOptions()
1129
1130
1131 /********** wxDb::getDbInfo() **********/
1132 bool wxDb::getDbInfo(bool failOnDataTypeUnsupported)
1133 {
1134 SWORD cb;
1135 RETCODE retcode;
1136
1137 retcode = SQLGetInfo(hdbc, SQL_SERVER_NAME, (UCHAR*) dbInf.serverName, sizeof(dbInf.serverName), &cb);
1138 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1139 {
1140 DispAllErrors(henv, hdbc);
1141 if (failOnDataTypeUnsupported)
1142 return false;
1143 }
1144
1145 retcode = SQLGetInfo(hdbc, SQL_DATABASE_NAME, (UCHAR*) dbInf.databaseName, sizeof(dbInf.databaseName), &cb);
1146 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1147 {
1148 DispAllErrors(henv, hdbc);
1149 if (failOnDataTypeUnsupported)
1150 return false;
1151 }
1152
1153 retcode = SQLGetInfo(hdbc, SQL_DBMS_NAME, (UCHAR*) dbInf.dbmsName, sizeof(dbInf.dbmsName), &cb);
1154 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1155 {
1156 DispAllErrors(henv, hdbc);
1157 if (failOnDataTypeUnsupported)
1158 return false;
1159 }
1160
1161 // 16-Mar-1999
1162 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
1163 // causing database connectivity to fail in some cases.
1164 retcode = SQLGetInfo(hdbc, SQL_DBMS_VER, (UCHAR*) dbInf.dbmsVer, sizeof(dbInf.dbmsVer), &cb);
1165 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1166 {
1167 DispAllErrors(henv, hdbc);
1168 if (failOnDataTypeUnsupported)
1169 return false;
1170 }
1171
1172 retcode = SQLGetInfo(hdbc, SQL_ACTIVE_CONNECTIONS, (UCHAR*) &dbInf.maxConnections, sizeof(dbInf.maxConnections), &cb);
1173 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1174 {
1175 DispAllErrors(henv, hdbc);
1176 if (failOnDataTypeUnsupported)
1177 return false;
1178 }
1179
1180 retcode = SQLGetInfo(hdbc, SQL_ACTIVE_STATEMENTS, (UCHAR*) &dbInf.maxStmts, sizeof(dbInf.maxStmts), &cb);
1181 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1182 {
1183 DispAllErrors(henv, hdbc);
1184 if (failOnDataTypeUnsupported)
1185 return false;
1186 }
1187
1188 retcode = SQLGetInfo(hdbc, SQL_DRIVER_NAME, (UCHAR*) dbInf.driverName, sizeof(dbInf.driverName), &cb);
1189 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1190 {
1191 DispAllErrors(henv, hdbc);
1192 if (failOnDataTypeUnsupported)
1193 return false;
1194 }
1195
1196 retcode = SQLGetInfo(hdbc, SQL_DRIVER_ODBC_VER, (UCHAR*) dbInf.odbcVer, sizeof(dbInf.odbcVer), &cb);
1197 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1198 {
1199 DispAllErrors(henv, hdbc);
1200 if (failOnDataTypeUnsupported)
1201 return false;
1202 }
1203
1204 retcode = SQLGetInfo(hdbc, SQL_ODBC_VER, (UCHAR*) dbInf.drvMgrOdbcVer, sizeof(dbInf.drvMgrOdbcVer), &cb);
1205 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
1206 {
1207 DispAllErrors(henv, hdbc);
1208 if (failOnDataTypeUnsupported)
1209 return false;
1210 }
1211
1212 retcode = SQLGetInfo(hdbc, SQL_DRIVER_VER, (UCHAR*) dbInf.driverVer, sizeof(dbInf.driverVer), &cb);
1213 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1214 {
1215 DispAllErrors(henv, hdbc);
1216 if (failOnDataTypeUnsupported)
1217 return false;
1218 }
1219
1220 retcode = SQLGetInfo(hdbc, SQL_ODBC_API_CONFORMANCE, (UCHAR*) &dbInf.apiConfLvl, sizeof(dbInf.apiConfLvl), &cb);
1221 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1222 {
1223 DispAllErrors(henv, hdbc);
1224 if (failOnDataTypeUnsupported)
1225 return false;
1226 }
1227
1228 retcode = SQLGetInfo(hdbc, SQL_ODBC_SAG_CLI_CONFORMANCE, (UCHAR*) &dbInf.cliConfLvl, sizeof(dbInf.cliConfLvl), &cb);
1229 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1230 {
1231 // Not all drivers support this call - Nick Gorham(unixODBC)
1232 dbInf.cliConfLvl = 0;
1233 DispAllErrors(henv, hdbc);
1234 if (failOnDataTypeUnsupported)
1235 return false;
1236 }
1237
1238 retcode = SQLGetInfo(hdbc, SQL_ODBC_SQL_CONFORMANCE, (UCHAR*) &dbInf.sqlConfLvl, sizeof(dbInf.sqlConfLvl), &cb);
1239 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1240 {
1241 DispAllErrors(henv, hdbc);
1242 if (failOnDataTypeUnsupported)
1243 return false;
1244 }
1245
1246 retcode = SQLGetInfo(hdbc, SQL_OUTER_JOINS, (UCHAR*) dbInf.outerJoins, sizeof(dbInf.outerJoins), &cb);
1247 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1248 {
1249 DispAllErrors(henv, hdbc);
1250 if (failOnDataTypeUnsupported)
1251 return false;
1252 }
1253
1254 retcode = SQLGetInfo(hdbc, SQL_PROCEDURES, (UCHAR*) dbInf.procedureSupport, sizeof(dbInf.procedureSupport), &cb);
1255 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1256 {
1257 DispAllErrors(henv, hdbc);
1258 if (failOnDataTypeUnsupported)
1259 return false;
1260 }
1261
1262 retcode = SQLGetInfo(hdbc, SQL_ACCESSIBLE_TABLES, (UCHAR*) dbInf.accessibleTables, sizeof(dbInf.accessibleTables), &cb);
1263 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1264 {
1265 DispAllErrors(henv, hdbc);
1266 if (failOnDataTypeUnsupported)
1267 return false;
1268 }
1269
1270 retcode = SQLGetInfo(hdbc, SQL_CURSOR_COMMIT_BEHAVIOR, (UCHAR*) &dbInf.cursorCommitBehavior, sizeof(dbInf.cursorCommitBehavior), &cb);
1271 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1272 {
1273 DispAllErrors(henv, hdbc);
1274 if (failOnDataTypeUnsupported)
1275 return false;
1276 }
1277
1278 retcode = SQLGetInfo(hdbc, SQL_CURSOR_ROLLBACK_BEHAVIOR, (UCHAR*) &dbInf.cursorRollbackBehavior, sizeof(dbInf.cursorRollbackBehavior), &cb);
1279 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1280 {
1281 DispAllErrors(henv, hdbc);
1282 if (failOnDataTypeUnsupported)
1283 return false;
1284 }
1285
1286 retcode = SQLGetInfo(hdbc, SQL_NON_NULLABLE_COLUMNS, (UCHAR*) &dbInf.supportNotNullClause, sizeof(dbInf.supportNotNullClause), &cb);
1287 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1288 {
1289 DispAllErrors(henv, hdbc);
1290 if (failOnDataTypeUnsupported)
1291 return false;
1292 }
1293
1294 retcode = SQLGetInfo(hdbc, SQL_ODBC_SQL_OPT_IEF, (UCHAR*) dbInf.supportIEF, sizeof(dbInf.supportIEF), &cb);
1295 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1296 {
1297 DispAllErrors(henv, hdbc);
1298 if (failOnDataTypeUnsupported)
1299 return false;
1300 }
1301
1302 retcode = SQLGetInfo(hdbc, SQL_DEFAULT_TXN_ISOLATION, (UCHAR*) &dbInf.txnIsolation, sizeof(dbInf.txnIsolation), &cb);
1303 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1304 {
1305 DispAllErrors(henv, hdbc);
1306 if (failOnDataTypeUnsupported)
1307 return false;
1308 }
1309
1310 retcode = SQLGetInfo(hdbc, SQL_TXN_ISOLATION_OPTION, (UCHAR*) &dbInf.txnIsolationOptions, sizeof(dbInf.txnIsolationOptions), &cb);
1311 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1312 {
1313 DispAllErrors(henv, hdbc);
1314 if (failOnDataTypeUnsupported)
1315 return false;
1316 }
1317
1318 retcode = SQLGetInfo(hdbc, SQL_FETCH_DIRECTION, (UCHAR*) &dbInf.fetchDirections, sizeof(dbInf.fetchDirections), &cb);
1319 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1320 {
1321 DispAllErrors(henv, hdbc);
1322 if (failOnDataTypeUnsupported)
1323 return false;
1324 }
1325
1326 retcode = SQLGetInfo(hdbc, SQL_LOCK_TYPES, (UCHAR*) &dbInf.lockTypes, sizeof(dbInf.lockTypes), &cb);
1327 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1328 {
1329 DispAllErrors(henv, hdbc);
1330 if (failOnDataTypeUnsupported)
1331 return false;
1332 }
1333
1334 retcode = SQLGetInfo(hdbc, SQL_POS_OPERATIONS, (UCHAR*) &dbInf.posOperations, sizeof(dbInf.posOperations), &cb);
1335 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1336 {
1337 DispAllErrors(henv, hdbc);
1338 if (failOnDataTypeUnsupported)
1339 return false;
1340 }
1341
1342 retcode = SQLGetInfo(hdbc, SQL_POSITIONED_STATEMENTS, (UCHAR*) &dbInf.posStmts, sizeof(dbInf.posStmts), &cb);
1343 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1344 {
1345 DispAllErrors(henv, hdbc);
1346 if (failOnDataTypeUnsupported)
1347 return false;
1348 }
1349
1350 retcode = SQLGetInfo(hdbc, SQL_SCROLL_CONCURRENCY, (UCHAR*) &dbInf.scrollConcurrency, sizeof(dbInf.scrollConcurrency), &cb);
1351 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1352 {
1353 DispAllErrors(henv, hdbc);
1354 if (failOnDataTypeUnsupported)
1355 return false;
1356 }
1357
1358 retcode = SQLGetInfo(hdbc, SQL_SCROLL_OPTIONS, (UCHAR*) &dbInf.scrollOptions, sizeof(dbInf.scrollOptions), &cb);
1359 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1360 {
1361 DispAllErrors(henv, hdbc);
1362 if (failOnDataTypeUnsupported)
1363 return false;
1364 }
1365
1366 retcode = SQLGetInfo(hdbc, SQL_STATIC_SENSITIVITY, (UCHAR*) &dbInf.staticSensitivity, sizeof(dbInf.staticSensitivity), &cb);
1367 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1368 {
1369 DispAllErrors(henv, hdbc);
1370 if (failOnDataTypeUnsupported)
1371 return false;
1372 }
1373
1374 retcode = SQLGetInfo(hdbc, SQL_TXN_CAPABLE, (UCHAR*) &dbInf.txnCapable, sizeof(dbInf.txnCapable), &cb);
1375 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1376 {
1377 DispAllErrors(henv, hdbc);
1378 if (failOnDataTypeUnsupported)
1379 return false;
1380 }
1381
1382 retcode = SQLGetInfo(hdbc, SQL_LOGIN_TIMEOUT, (UCHAR*) &dbInf.loginTimeout, sizeof(dbInf.loginTimeout), &cb);
1383 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
1384 {
1385 DispAllErrors(henv, hdbc);
1386 if (failOnDataTypeUnsupported)
1387 return false;
1388 }
1389
1390 #ifdef DBDEBUG_CONSOLE
1391 cout << wxT("***** DATA SOURCE INFORMATION *****") << endl;
1392 cout << wxT(wxT("SERVER Name: ") << dbInf.serverName << endl;
1393 cout << wxT("DBMS Name: ") << dbInf.dbmsName << wxT("; DBMS Version: ") << dbInf.dbmsVer << endl;
1394 cout << wxT("ODBC Version: ") << dbInf.odbcVer << wxT("; Driver Version: ") << dbInf.driverVer << endl;
1395
1396 cout << wxT("API Conf. Level: ");
1397 switch(dbInf.apiConfLvl)
1398 {
1399 case SQL_OAC_NONE: cout << wxT("None"); break;
1400 case SQL_OAC_LEVEL1: cout << wxT("Level 1"); break;
1401 case SQL_OAC_LEVEL2: cout << wxT("Level 2"); break;
1402 }
1403 cout << endl;
1404
1405 cout << wxT("SAG CLI Conf. Level: ");
1406 switch(dbInf.cliConfLvl)
1407 {
1408 case SQL_OSCC_NOT_COMPLIANT: cout << wxT("Not Compliant"); break;
1409 case SQL_OSCC_COMPLIANT: cout << wxT("Compliant"); break;
1410 }
1411 cout << endl;
1412
1413 cout << wxT("SQL Conf. Level: ");
1414 switch(dbInf.sqlConfLvl)
1415 {
1416 case SQL_OSC_MINIMUM: cout << wxT("Minimum Grammar"); break;
1417 case SQL_OSC_CORE: cout << wxT("Core Grammar"); break;
1418 case SQL_OSC_EXTENDED: cout << wxT("Extended Grammar"); break;
1419 }
1420 cout << endl;
1421
1422 cout << wxT("Max. Connections: ") << dbInf.maxConnections << endl;
1423 cout << wxT("Outer Joins: ") << dbInf.outerJoins << endl;
1424 cout << wxT("Support for Procedures: ") << dbInf.procedureSupport << endl;
1425 cout << wxT("All tables accessible : ") << dbInf.accessibleTables << endl;
1426 cout << wxT("Cursor COMMIT Behavior: ");
1427 switch(dbInf.cursorCommitBehavior)
1428 {
1429 case SQL_CB_DELETE: cout << wxT("Delete cursors"); break;
1430 case SQL_CB_CLOSE: cout << wxT("Close cursors"); break;
1431 case SQL_CB_PRESERVE: cout << wxT("Preserve cursors"); break;
1432 }
1433 cout << endl;
1434
1435 cout << wxT("Cursor ROLLBACK Behavior: ");
1436 switch(dbInf.cursorRollbackBehavior)
1437 {
1438 case SQL_CB_DELETE: cout << wxT("Delete cursors"); break;
1439 case SQL_CB_CLOSE: cout << wxT("Close cursors"); break;
1440 case SQL_CB_PRESERVE: cout << wxT("Preserve cursors"); break;
1441 }
1442 cout << endl;
1443
1444 cout << wxT("Support NOT NULL clause: ");
1445 switch(dbInf.supportNotNullClause)
1446 {
1447 case SQL_NNC_NULL: cout << wxT("No"); break;
1448 case SQL_NNC_NON_NULL: cout << wxT("Yes"); break;
1449 }
1450 cout << endl;
1451
1452 cout << wxT("Support IEF (Ref. Integrity): ") << dbInf.supportIEF << endl;
1453 cout << wxT("Login Timeout: ") << dbInf.loginTimeout << endl;
1454
1455 cout << endl << endl << wxT("more ...") << endl;
1456 getchar();
1457
1458 cout << wxT("Default Transaction Isolation: ";
1459 switch(dbInf.txnIsolation)
1460 {
1461 case SQL_TXN_READ_UNCOMMITTED: cout << wxT("Read Uncommitted"); break;
1462 case SQL_TXN_READ_COMMITTED: cout << wxT("Read Committed"); break;
1463 case SQL_TXN_REPEATABLE_READ: cout << wxT("Repeatable Read"); break;
1464 case SQL_TXN_SERIALIZABLE: cout << wxT("Serializable"); break;
1465 #ifdef ODBC_V20
1466 case SQL_TXN_VERSIONING: cout << wxT("Versioning"); break;
1467 #endif
1468 }
1469 cout << endl;
1470
1471 cout << wxT("Transaction Isolation Options: ");
1472 if (dbInf.txnIsolationOptions & SQL_TXN_READ_UNCOMMITTED)
1473 cout << wxT("Read Uncommitted, ");
1474 if (dbInf.txnIsolationOptions & SQL_TXN_READ_COMMITTED)
1475 cout << wxT("Read Committed, ");
1476 if (dbInf.txnIsolationOptions & SQL_TXN_REPEATABLE_READ)
1477 cout << wxT("Repeatable Read, ");
1478 if (dbInf.txnIsolationOptions & SQL_TXN_SERIALIZABLE)
1479 cout << wxT("Serializable, ");
1480 #ifdef ODBC_V20
1481 if (dbInf.txnIsolationOptions & SQL_TXN_VERSIONING)
1482 cout << wxT("Versioning");
1483 #endif
1484 cout << endl;
1485
1486 cout << wxT("Fetch Directions Supported:") << endl << wxT(" ");
1487 if (dbInf.fetchDirections & SQL_FD_FETCH_NEXT)
1488 cout << wxT("Next, ");
1489 if (dbInf.fetchDirections & SQL_FD_FETCH_PRIOR)
1490 cout << wxT("Prev, ");
1491 if (dbInf.fetchDirections & SQL_FD_FETCH_FIRST)
1492 cout << wxT("First, ");
1493 if (dbInf.fetchDirections & SQL_FD_FETCH_LAST)
1494 cout << wxT("Last, ");
1495 if (dbInf.fetchDirections & SQL_FD_FETCH_ABSOLUTE)
1496 cout << wxT("Absolute, ");
1497 if (dbInf.fetchDirections & SQL_FD_FETCH_RELATIVE)
1498 cout << wxT("Relative, ");
1499 #ifdef ODBC_V20
1500 if (dbInf.fetchDirections & SQL_FD_FETCH_RESUME)
1501 cout << wxT("Resume, ");
1502 #endif
1503 if (dbInf.fetchDirections & SQL_FD_FETCH_BOOKMARK)
1504 cout << wxT("Bookmark");
1505 cout << endl;
1506
1507 cout << wxT("Lock Types Supported (SQLSetPos): ");
1508 if (dbInf.lockTypes & SQL_LCK_NO_CHANGE)
1509 cout << wxT("No Change, ");
1510 if (dbInf.lockTypes & SQL_LCK_EXCLUSIVE)
1511 cout << wxT("Exclusive, ");
1512 if (dbInf.lockTypes & SQL_LCK_UNLOCK)
1513 cout << wxT("UnLock");
1514 cout << endl;
1515
1516 cout << wxT("Position Operations Supported (SQLSetPos): ");
1517 if (dbInf.posOperations & SQL_POS_POSITION)
1518 cout << wxT("Position, ");
1519 if (dbInf.posOperations & SQL_POS_REFRESH)
1520 cout << wxT("Refresh, ");
1521 if (dbInf.posOperations & SQL_POS_UPDATE)
1522 cout << wxT("Upd, "));
1523 if (dbInf.posOperations & SQL_POS_DELETE)
1524 cout << wxT("Del, ");
1525 if (dbInf.posOperations & SQL_POS_ADD)
1526 cout << wxT("Add");
1527 cout << endl;
1528
1529 cout << wxT("Positioned Statements Supported: ");
1530 if (dbInf.posStmts & SQL_PS_POSITIONED_DELETE)
1531 cout << wxT("Pos delete, ");
1532 if (dbInf.posStmts & SQL_PS_POSITIONED_UPDATE)
1533 cout << wxT("Pos update, ");
1534 if (dbInf.posStmts & SQL_PS_SELECT_FOR_UPDATE)
1535 cout << wxT("Select for update");
1536 cout << endl;
1537
1538 cout << wxT("Scroll Concurrency: ");
1539 if (dbInf.scrollConcurrency & SQL_SCCO_READ_ONLY)
1540 cout << wxT("Read Only, ");
1541 if (dbInf.scrollConcurrency & SQL_SCCO_LOCK)
1542 cout << wxT("Lock, ");
1543 if (dbInf.scrollConcurrency & SQL_SCCO_OPT_ROWVER)
1544 cout << wxT("Opt. Rowver, ");
1545 if (dbInf.scrollConcurrency & SQL_SCCO_OPT_VALUES)
1546 cout << wxT("Opt. Values");
1547 cout << endl;
1548
1549 cout << wxT("Scroll Options: ");
1550 if (dbInf.scrollOptions & SQL_SO_FORWARD_ONLY)
1551 cout << wxT("Fwd Only, ");
1552 if (dbInf.scrollOptions & SQL_SO_STATIC)
1553 cout << wxT("Static, ");
1554 if (dbInf.scrollOptions & SQL_SO_KEYSET_DRIVEN)
1555 cout << wxT("Keyset Driven, ");
1556 if (dbInf.scrollOptions & SQL_SO_DYNAMIC)
1557 cout << wxT("Dynamic, ");
1558 if (dbInf.scrollOptions & SQL_SO_MIXED)
1559 cout << wxT("Mixed");
1560 cout << endl;
1561
1562 cout << wxT("Static Sensitivity: ");
1563 if (dbInf.staticSensitivity & SQL_SS_ADDITIONS)
1564 cout << wxT("Additions, ");
1565 if (dbInf.staticSensitivity & SQL_SS_DELETIONS)
1566 cout << wxT("Deletions, ");
1567 if (dbInf.staticSensitivity & SQL_SS_UPDATES)
1568 cout << wxT("Updates");
1569 cout << endl;
1570
1571 cout << wxT("Transaction Capable?: ");
1572 switch(dbInf.txnCapable)
1573 {
1574 case SQL_TC_NONE: cout << wxT("No"); break;
1575 case SQL_TC_DML: cout << wxT("DML Only"); break;
1576 case SQL_TC_DDL_COMMIT: cout << wxT("DDL Commit"); break;
1577 case SQL_TC_DDL_IGNORE: cout << wxT("DDL Ignore"); break;
1578 case SQL_TC_ALL: cout << wxT("DDL & DML"); break;
1579 }
1580 cout << endl;
1581
1582 cout << endl;
1583 #endif
1584
1585 // Completed Successfully
1586 return true;
1587
1588 } // wxDb::getDbInfo()
1589
1590
1591 /********** wxDb::getDataTypeInfo() **********/
1592 bool wxDb::getDataTypeInfo(SWORD fSqlType, wxDbSqlTypeInfo &structSQLTypeInfo)
1593 {
1594 /*
1595 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1596 * the data type inf. is gathered for.
1597 *
1598 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
1599 */
1600 RETCODE retcode;
1601 SDWORD cbRet;
1602
1603 // Get information about the data type specified
1604 if (SQLGetTypeInfo(hstmt, fSqlType) != SQL_SUCCESS)
1605 return(DispAllErrors(henv, hdbc, hstmt));
1606
1607 // Fetch the record
1608 retcode = SQLFetch(hstmt);
1609 if (retcode != SQL_SUCCESS)
1610 {
1611 #ifdef DBDEBUG_CONSOLE
1612 if (retcode == SQL_NO_DATA_FOUND)
1613 cout << wxT("SQL_NO_DATA_FOUND fetching information about data type.") << endl;
1614 #endif
1615 DispAllErrors(henv, hdbc, hstmt);
1616 SQLFreeStmt(hstmt, SQL_CLOSE);
1617 return false;
1618 }
1619
1620 wxChar typeName[DB_TYPE_NAME_LEN+1];
1621
1622 // Obtain columns from the record
1623 if (SQLGetData(hstmt, 1, SQL_C_WXCHAR, typeName, sizeof(typeName), &cbRet) != SQL_SUCCESS)
1624 return(DispAllErrors(henv, hdbc, hstmt));
1625
1626 structSQLTypeInfo.TypeName = typeName;
1627
1628 // BJO 20000503: no more needed with new GetColumns...
1629 #if OLD_GETCOLUMNS
1630 // BJO 991209
1631 if (Dbms() == dbmsMY_SQL)
1632 {
1633 if (structSQLTypeInfo.TypeName == wxT("middleint"))
1634 structSQLTypeInfo.TypeName = wxT("mediumint");
1635 else if (structSQLTypeInfo.TypeName == wxT("middleint unsigned"))
1636 structSQLTypeInfo.TypeName = wxT("mediumint unsigned");
1637 else if (structSQLTypeInfo.TypeName == wxT("integer"))
1638 structSQLTypeInfo.TypeName = wxT("int");
1639 else if (structSQLTypeInfo.TypeName == wxT("integer unsigned"))
1640 structSQLTypeInfo.TypeName = wxT("int unsigned");
1641 else if (structSQLTypeInfo.TypeName == wxT("middleint"))
1642 structSQLTypeInfo.TypeName = wxT("mediumint");
1643 else if (structSQLTypeInfo.TypeName == wxT("varchar"))
1644 structSQLTypeInfo.TypeName = wxT("char");
1645 }
1646
1647 // BJO 20000427 : OpenLink driver
1648 if (!wxStrncmp(dbInf.driverName, wxT("oplodbc"), 7) ||
1649 !wxStrncmp(dbInf.driverName, wxT("OLOD"), 4))
1650 {
1651 if (structSQLTypeInfo.TypeName == wxT("double precision"))
1652 structSQLTypeInfo.TypeName = wxT("real");
1653 }
1654 #endif
1655
1656 if (SQLGetData(hstmt, 3, SQL_C_LONG, (UCHAR*) &structSQLTypeInfo.Precision, 0, &cbRet) != SQL_SUCCESS)
1657 return(DispAllErrors(henv, hdbc, hstmt));
1658 if (SQLGetData(hstmt, 8, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.CaseSensitive, 0, &cbRet) != SQL_SUCCESS)
1659 return(DispAllErrors(henv, hdbc, hstmt));
1660 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
1661 // return(DispAllErrors(henv, hdbc, hstmt));
1662
1663 if (SQLGetData(hstmt, 15, SQL_C_SHORT,(UCHAR*) &structSQLTypeInfo.MaximumScale, 0, &cbRet) != SQL_SUCCESS)
1664 return(DispAllErrors(henv, hdbc, hstmt));
1665
1666 if (structSQLTypeInfo.MaximumScale < 0)
1667 structSQLTypeInfo.MaximumScale = 0;
1668
1669 // Close the statement handle which closes open cursors
1670 if (SQLFreeStmt(hstmt, SQL_CLOSE) != SQL_SUCCESS)
1671 return(DispAllErrors(henv, hdbc, hstmt));
1672
1673 // Completed Successfully
1674 return true;
1675
1676 } // wxDb::getDataTypeInfo()
1677
1678
1679 /********** wxDb::Close() **********/
1680 void wxDb::Close(void)
1681 {
1682 // Close the Sql Log file
1683 if (fpSqlLog)
1684 {
1685 fclose(fpSqlLog);
1686 fpSqlLog = 0;
1687 }
1688
1689 // Free statement handle
1690 if (dbIsOpen)
1691 {
1692 if (SQLFreeStmt(hstmt, SQL_DROP) != SQL_SUCCESS)
1693 DispAllErrors(henv, hdbc);
1694 }
1695
1696 // Disconnect from the datasource
1697 if (SQLDisconnect(hdbc) != SQL_SUCCESS)
1698 DispAllErrors(henv, hdbc);
1699
1700 // Free the connection to the datasource
1701 if (SQLFreeConnect(hdbc) != SQL_SUCCESS)
1702 DispAllErrors(henv, hdbc);
1703
1704 // There should be zero Ctable objects still connected to this db object
1705 wxASSERT(nTables == 0);
1706
1707 #ifdef __WXDEBUG__
1708 wxTablesInUse *tiu;
1709 wxList::compatibility_iterator pNode;
1710 pNode = TablesInUse.GetFirst();
1711 wxString s,s2;
1712 while (pNode)
1713 {
1714 tiu = (wxTablesInUse *)pNode->GetData();
1715 if (tiu->pDb == this)
1716 {
1717 s.Printf(wxT("(%-20s) tableID:[%6lu] pDb:[%p]"), tiu->tableName,tiu->tableID,tiu->pDb);
1718 s2.Printf(wxT("Orphaned table found using pDb:[%p]"),this);
1719 wxLogDebug(s.c_str(),s2.c_str());
1720 }
1721 pNode = pNode->GetNext();
1722 }
1723 #endif
1724
1725 // Copy the error messages to a global variable
1726 int i;
1727 for (i = 0; i < DB_MAX_ERROR_HISTORY; i++)
1728 wxStrcpy(DBerrorList[i], errorList[i]);
1729
1730 dbmsType = dbmsUNIDENTIFIED;
1731 dbIsOpen = false;
1732
1733 } // wxDb::Close()
1734
1735
1736 /********** wxDb::CommitTrans() **********/
1737 bool wxDb::CommitTrans(void)
1738 {
1739 if (this)
1740 {
1741 // Commit the transaction
1742 if (SQLTransact(henv, hdbc, SQL_COMMIT) != SQL_SUCCESS)
1743 return(DispAllErrors(henv, hdbc));
1744 }
1745
1746 // Completed successfully
1747 return true;
1748
1749 } // wxDb::CommitTrans()
1750
1751
1752 /********** wxDb::RollbackTrans() **********/
1753 bool wxDb::RollbackTrans(void)
1754 {
1755 // Rollback the transaction
1756 if (SQLTransact(henv, hdbc, SQL_ROLLBACK) != SQL_SUCCESS)
1757 return(DispAllErrors(henv, hdbc));
1758
1759 // Completed successfully
1760 return true;
1761
1762 } // wxDb::RollbackTrans()
1763
1764
1765 /********** wxDb::DispAllErrors() **********/
1766 bool wxDb::DispAllErrors(HENV aHenv, HDBC aHdbc, HSTMT aHstmt)
1767 /*
1768 * This function is called internally whenever an error condition prevents the user's
1769 * request from being executed. This function will query the datasource as to the
1770 * actual error(s) that just occured on the previous request of the datasource.
1771 *
1772 * The function will retrieve each error condition from the datasource and
1773 * Printf the codes/text values into a string which it then logs via logError().
1774 * If in DBDEBUG_CONSOLE mode, the constructed string will be displayed in the console
1775 * window and program execution will be paused until the user presses a key.
1776 *
1777 * This function always returns a false, so that functions which call this function
1778 * can have a line like "return (DispAllErrors(henv, hdbc));" to indicate the failure
1779 * of the users request, so that the calling code can then process the error msg log
1780 */
1781 {
1782 wxString odbcErrMsg;
1783
1784 #ifdef __VMS
1785 while (SQLError(aHenv, aHdbc, aHstmt, (SQLTCHAR FAR *) sqlState, (SQLINTEGER *) &nativeError, (SQLTCHAR FAR *) errorMsg, SQL_MAX_MESSAGE_LENGTH - 1, &cbErrorMsg) == SQL_SUCCESS)
1786 #else
1787 while (SQLError(aHenv, aHdbc, aHstmt, (SQLTCHAR FAR *) sqlState, (long*) &nativeError, (SQLTCHAR FAR *) errorMsg, SQL_MAX_MESSAGE_LENGTH - 1, &cbErrorMsg) == SQL_SUCCESS)
1788 #endif
1789 {
1790 odbcErrMsg.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState, nativeError, errorMsg);
1791 logError(odbcErrMsg, sqlState);
1792 if (!silent)
1793 {
1794 #ifdef DBDEBUG_CONSOLE
1795 // When run in console mode, use standard out to display errors.
1796 cout << odbcErrMsg.c_str() << endl;
1797 cout << wxT("Press any key to continue...") << endl;
1798 getchar();
1799 #endif
1800
1801 #ifdef __WXDEBUG__
1802 wxLogDebug(odbcErrMsg,wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
1803 #endif
1804 }
1805 }
1806
1807 return false; // This function always returns false.
1808
1809 } // wxDb::DispAllErrors()
1810
1811
1812 /********** wxDb::GetNextError() **********/
1813 bool wxDb::GetNextError(HENV aHenv, HDBC aHdbc, HSTMT aHstmt)
1814 {
1815 #ifdef __VMS
1816 if (SQLError(aHenv, aHdbc, aHstmt, (SQLTCHAR FAR *) sqlState, (SQLINTEGER *) &nativeError, (SQLTCHAR FAR *) errorMsg, SQL_MAX_MESSAGE_LENGTH - 1, &cbErrorMsg) == SQL_SUCCESS)
1817 #else
1818 if (SQLError(aHenv, aHdbc, aHstmt, (SQLTCHAR FAR *) sqlState, (long*) &nativeError, (SQLTCHAR FAR *) errorMsg, SQL_MAX_MESSAGE_LENGTH - 1, &cbErrorMsg) == SQL_SUCCESS)
1819 #endif
1820 return true;
1821 else
1822 return false;
1823
1824 } // wxDb::GetNextError()
1825
1826
1827 /********** wxDb::DispNextError() **********/
1828 void wxDb::DispNextError(void)
1829 {
1830 wxString odbcErrMsg;
1831
1832 odbcErrMsg.Printf(wxT("SQL State = %s\nNative Error Code = %li\nError Message = %s\n"), sqlState, nativeError, errorMsg);
1833 logError(odbcErrMsg, sqlState);
1834
1835 if (silent)
1836 return;
1837
1838 #ifdef DBDEBUG_CONSOLE
1839 // When run in console mode, use standard out to display errors.
1840 cout << odbcErrMsg.c_str() << endl;
1841 cout << wxT("Press any key to continue...") << endl;
1842 getchar();
1843 #endif
1844
1845 #ifdef __WXDEBUG__
1846 wxLogDebug(odbcErrMsg,wxT("ODBC DEBUG MESSAGE"));
1847 #endif // __WXDEBUG__
1848
1849 } // wxDb::DispNextError()
1850
1851
1852 /********** wxDb::logError() **********/
1853 void wxDb::logError(const wxString &errMsg, const wxString &SQLState)
1854 {
1855 wxASSERT(errMsg.Length());
1856
1857 static int pLast = -1;
1858 int dbStatus;
1859
1860 if (++pLast == DB_MAX_ERROR_HISTORY)
1861 {
1862 int i;
1863 for (i = 0; i < DB_MAX_ERROR_HISTORY; i++)
1864 wxStrcpy(errorList[i], errorList[i+1]);
1865 pLast--;
1866 }
1867
1868 wxStrncpy(errorList[pLast], errMsg, DB_MAX_ERROR_MSG_LEN);
1869 errorList[pLast][DB_MAX_ERROR_MSG_LEN] = 0;
1870
1871 if (SQLState.Length())
1872 if ((dbStatus = TranslateSqlState(SQLState)) != DB_ERR_FUNCTION_SEQUENCE_ERROR)
1873 DB_STATUS = dbStatus;
1874
1875 // Add the errmsg to the sql log
1876 WriteSqlLog(errMsg);
1877
1878 } // wxDb::logError()
1879
1880
1881 /**********wxDb::TranslateSqlState() **********/
1882 int wxDb::TranslateSqlState(const wxString &SQLState)
1883 {
1884 if (!wxStrcmp(SQLState, wxT("01000")))
1885 return(DB_ERR_GENERAL_WARNING);
1886 if (!wxStrcmp(SQLState, wxT("01002")))
1887 return(DB_ERR_DISCONNECT_ERROR);
1888 if (!wxStrcmp(SQLState, wxT("01004")))
1889 return(DB_ERR_DATA_TRUNCATED);
1890 if (!wxStrcmp(SQLState, wxT("01006")))
1891 return(DB_ERR_PRIV_NOT_REVOKED);
1892 if (!wxStrcmp(SQLState, wxT("01S00")))
1893 return(DB_ERR_INVALID_CONN_STR_ATTR);
1894 if (!wxStrcmp(SQLState, wxT("01S01")))
1895 return(DB_ERR_ERROR_IN_ROW);
1896 if (!wxStrcmp(SQLState, wxT("01S02")))
1897 return(DB_ERR_OPTION_VALUE_CHANGED);
1898 if (!wxStrcmp(SQLState, wxT("01S03")))
1899 return(DB_ERR_NO_ROWS_UPD_OR_DEL);
1900 if (!wxStrcmp(SQLState, wxT("01S04")))
1901 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL);
1902 if (!wxStrcmp(SQLState, wxT("07001")))
1903 return(DB_ERR_WRONG_NO_OF_PARAMS);
1904 if (!wxStrcmp(SQLState, wxT("07006")))
1905 return(DB_ERR_DATA_TYPE_ATTR_VIOL);
1906 if (!wxStrcmp(SQLState, wxT("08001")))
1907 return(DB_ERR_UNABLE_TO_CONNECT);
1908 if (!wxStrcmp(SQLState, wxT("08002")))
1909 return(DB_ERR_CONNECTION_IN_USE);
1910 if (!wxStrcmp(SQLState, wxT("08003")))
1911 return(DB_ERR_CONNECTION_NOT_OPEN);
1912 if (!wxStrcmp(SQLState, wxT("08004")))
1913 return(DB_ERR_REJECTED_CONNECTION);
1914 if (!wxStrcmp(SQLState, wxT("08007")))
1915 return(DB_ERR_CONN_FAIL_IN_TRANS);
1916 if (!wxStrcmp(SQLState, wxT("08S01")))
1917 return(DB_ERR_COMM_LINK_FAILURE);
1918 if (!wxStrcmp(SQLState, wxT("21S01")))
1919 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH);
1920 if (!wxStrcmp(SQLState, wxT("21S02")))
1921 return(DB_ERR_DERIVED_TABLE_MISMATCH);
1922 if (!wxStrcmp(SQLState, wxT("22001")))
1923 return(DB_ERR_STRING_RIGHT_TRUNC);
1924 if (!wxStrcmp(SQLState, wxT("22003")))
1925 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG);
1926 if (!wxStrcmp(SQLState, wxT("22005")))
1927 return(DB_ERR_ERROR_IN_ASSIGNMENT);
1928 if (!wxStrcmp(SQLState, wxT("22008")))
1929 return(DB_ERR_DATETIME_FLD_OVERFLOW);
1930 if (!wxStrcmp(SQLState, wxT("22012")))
1931 return(DB_ERR_DIVIDE_BY_ZERO);
1932 if (!wxStrcmp(SQLState, wxT("22026")))
1933 return(DB_ERR_STR_DATA_LENGTH_MISMATCH);
1934 if (!wxStrcmp(SQLState, wxT("23000")))
1935 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL);
1936 if (!wxStrcmp(SQLState, wxT("24000")))
1937 return(DB_ERR_INVALID_CURSOR_STATE);
1938 if (!wxStrcmp(SQLState, wxT("25000")))
1939 return(DB_ERR_INVALID_TRANS_STATE);
1940 if (!wxStrcmp(SQLState, wxT("28000")))
1941 return(DB_ERR_INVALID_AUTH_SPEC);
1942 if (!wxStrcmp(SQLState, wxT("34000")))
1943 return(DB_ERR_INVALID_CURSOR_NAME);
1944 if (!wxStrcmp(SQLState, wxT("37000")))
1945 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL);
1946 if (!wxStrcmp(SQLState, wxT("3C000")))
1947 return(DB_ERR_DUPLICATE_CURSOR_NAME);
1948 if (!wxStrcmp(SQLState, wxT("40001")))
1949 return(DB_ERR_SERIALIZATION_FAILURE);
1950 if (!wxStrcmp(SQLState, wxT("42000")))
1951 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2);
1952 if (!wxStrcmp(SQLState, wxT("70100")))
1953 return(DB_ERR_OPERATION_ABORTED);
1954 if (!wxStrcmp(SQLState, wxT("IM001")))
1955 return(DB_ERR_UNSUPPORTED_FUNCTION);
1956 if (!wxStrcmp(SQLState, wxT("IM002")))
1957 return(DB_ERR_NO_DATA_SOURCE);
1958 if (!wxStrcmp(SQLState, wxT("IM003")))
1959 return(DB_ERR_DRIVER_LOAD_ERROR);
1960 if (!wxStrcmp(SQLState, wxT("IM004")))
1961 return(DB_ERR_SQLALLOCENV_FAILED);
1962 if (!wxStrcmp(SQLState, wxT("IM005")))
1963 return(DB_ERR_SQLALLOCCONNECT_FAILED);
1964 if (!wxStrcmp(SQLState, wxT("IM006")))
1965 return(DB_ERR_SQLSETCONNECTOPTION_FAILED);
1966 if (!wxStrcmp(SQLState, wxT("IM007")))
1967 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB);
1968 if (!wxStrcmp(SQLState, wxT("IM008")))
1969 return(DB_ERR_DIALOG_FAILED);
1970 if (!wxStrcmp(SQLState, wxT("IM009")))
1971 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL);
1972 if (!wxStrcmp(SQLState, wxT("IM010")))
1973 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG);
1974 if (!wxStrcmp(SQLState, wxT("IM011")))
1975 return(DB_ERR_DRIVER_NAME_TOO_LONG);
1976 if (!wxStrcmp(SQLState, wxT("IM012")))
1977 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR);
1978 if (!wxStrcmp(SQLState, wxT("IM013")))
1979 return(DB_ERR_TRACE_FILE_ERROR);
1980 if (!wxStrcmp(SQLState, wxT("S0001")))
1981 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS);
1982 if (!wxStrcmp(SQLState, wxT("S0002")))
1983 return(DB_ERR_TABLE_NOT_FOUND);
1984 if (!wxStrcmp(SQLState, wxT("S0011")))
1985 return(DB_ERR_INDEX_ALREADY_EXISTS);
1986 if (!wxStrcmp(SQLState, wxT("S0012")))
1987 return(DB_ERR_INDEX_NOT_FOUND);
1988 if (!wxStrcmp(SQLState, wxT("S0021")))
1989 return(DB_ERR_COLUMN_ALREADY_EXISTS);
1990 if (!wxStrcmp(SQLState, wxT("S0022")))
1991 return(DB_ERR_COLUMN_NOT_FOUND);
1992 if (!wxStrcmp(SQLState, wxT("S0023")))
1993 return(DB_ERR_NO_DEFAULT_FOR_COLUMN);
1994 if (!wxStrcmp(SQLState, wxT("S1000")))
1995 return(DB_ERR_GENERAL_ERROR);
1996 if (!wxStrcmp(SQLState, wxT("S1001")))
1997 return(DB_ERR_MEMORY_ALLOCATION_FAILURE);
1998 if (!wxStrcmp(SQLState, wxT("S1002")))
1999 return(DB_ERR_INVALID_COLUMN_NUMBER);
2000 if (!wxStrcmp(SQLState, wxT("S1003")))
2001 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE);
2002 if (!wxStrcmp(SQLState, wxT("S1004")))
2003 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE);
2004 if (!wxStrcmp(SQLState, wxT("S1008")))
2005 return(DB_ERR_OPERATION_CANCELLED);
2006 if (!wxStrcmp(SQLState, wxT("S1009")))
2007 return(DB_ERR_INVALID_ARGUMENT_VALUE);
2008 if (!wxStrcmp(SQLState, wxT("S1010")))
2009 return(DB_ERR_FUNCTION_SEQUENCE_ERROR);
2010 if (!wxStrcmp(SQLState, wxT("S1011")))
2011 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME);
2012 if (!wxStrcmp(SQLState, wxT("S1012")))
2013 return(DB_ERR_INVALID_TRANS_OPERATION_CODE);
2014 if (!wxStrcmp(SQLState, wxT("S1015")))
2015 return(DB_ERR_NO_CURSOR_NAME_AVAIL);
2016 if (!wxStrcmp(SQLState, wxT("S1090")))
2017 return(DB_ERR_INVALID_STR_OR_BUF_LEN);
2018 if (!wxStrcmp(SQLState, wxT("S1091")))
2019 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE);
2020 if (!wxStrcmp(SQLState, wxT("S1092")))
2021 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE);
2022 if (!wxStrcmp(SQLState, wxT("S1093")))
2023 return(DB_ERR_INVALID_PARAM_NO);
2024 if (!wxStrcmp(SQLState, wxT("S1094")))
2025 return(DB_ERR_INVALID_SCALE_VALUE);
2026 if (!wxStrcmp(SQLState, wxT("S1095")))
2027 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE);
2028 if (!wxStrcmp(SQLState, wxT("S1096")))
2029 return(DB_ERR_INF_TYPE_OUT_OF_RANGE);
2030 if (!wxStrcmp(SQLState, wxT("S1097")))
2031 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE);
2032 if (!wxStrcmp(SQLState, wxT("S1098")))
2033 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE);
2034 if (!wxStrcmp(SQLState, wxT("S1099")))
2035 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE);
2036 if (!wxStrcmp(SQLState, wxT("S1100")))
2037 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE);
2038 if (!wxStrcmp(SQLState, wxT("S1101")))
2039 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE);
2040 if (!wxStrcmp(SQLState, wxT("S1103")))
2041 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE);
2042 if (!wxStrcmp(SQLState, wxT("S1104")))
2043 return(DB_ERR_INVALID_PRECISION_VALUE);
2044 if (!wxStrcmp(SQLState, wxT("S1105")))
2045 return(DB_ERR_INVALID_PARAM_TYPE);
2046 if (!wxStrcmp(SQLState, wxT("S1106")))
2047 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE);
2048 if (!wxStrcmp(SQLState, wxT("S1107")))
2049 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE);
2050 if (!wxStrcmp(SQLState, wxT("S1108")))
2051 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE);
2052 if (!wxStrcmp(SQLState, wxT("S1109")))
2053 return(DB_ERR_INVALID_CURSOR_POSITION);
2054 if (!wxStrcmp(SQLState, wxT("S1110")))
2055 return(DB_ERR_INVALID_DRIVER_COMPLETION);
2056 if (!wxStrcmp(SQLState, wxT("S1111")))
2057 return(DB_ERR_INVALID_BOOKMARK_VALUE);
2058 if (!wxStrcmp(SQLState, wxT("S1C00")))
2059 return(DB_ERR_DRIVER_NOT_CAPABLE);
2060 if (!wxStrcmp(SQLState, wxT("S1T00")))
2061 return(DB_ERR_TIMEOUT_EXPIRED);
2062
2063 // No match
2064 return(0);
2065
2066 } // wxDb::TranslateSqlState()
2067
2068
2069 /********** wxDb::Grant() **********/
2070 bool wxDb::Grant(int privileges, const wxString &tableName, const wxString &userList)
2071 {
2072 wxString sqlStmt;
2073
2074 // Build the grant statement
2075 sqlStmt = wxT("GRANT ");
2076 if (privileges == DB_GRANT_ALL)
2077 sqlStmt += wxT("ALL");
2078 else
2079 {
2080 int c = 0;
2081 if (privileges & DB_GRANT_SELECT)
2082 {
2083 sqlStmt += wxT("SELECT");
2084 c++;
2085 }
2086 if (privileges & DB_GRANT_INSERT)
2087 {
2088 if (c++)
2089 sqlStmt += wxT(", ");
2090 sqlStmt += wxT("INSERT");
2091 }
2092 if (privileges & DB_GRANT_UPDATE)
2093 {
2094 if (c++)
2095 sqlStmt += wxT(", ");
2096 sqlStmt += wxT("UPDATE");
2097 }
2098 if (privileges & DB_GRANT_DELETE)
2099 {
2100 if (c++)
2101 sqlStmt += wxT(", ");
2102 sqlStmt += wxT("DELETE");
2103 }
2104 }
2105
2106 sqlStmt += wxT(" ON ");
2107 sqlStmt += SQLTableName(tableName);
2108 sqlStmt += wxT(" TO ");
2109 sqlStmt += userList;
2110
2111 #ifdef DBDEBUG_CONSOLE
2112 cout << endl << sqlStmt.c_str() << endl;
2113 #endif
2114
2115 WriteSqlLog(sqlStmt);
2116
2117 return(ExecSql(sqlStmt));
2118
2119 } // wxDb::Grant()
2120
2121
2122 /********** wxDb::CreateView() **********/
2123 bool wxDb::CreateView(const wxString &viewName, const wxString &colList,
2124 const wxString &pSqlStmt, bool attemptDrop)
2125 {
2126 wxString sqlStmt;
2127
2128 // Drop the view first
2129 if (attemptDrop && !DropView(viewName))
2130 return false;
2131
2132 // Build the create view statement
2133 sqlStmt = wxT("CREATE VIEW ");
2134 sqlStmt += viewName;
2135
2136 if (colList.Length())
2137 {
2138 sqlStmt += wxT(" (");
2139 sqlStmt += colList;
2140 sqlStmt += wxT(")");
2141 }
2142
2143 sqlStmt += wxT(" AS ");
2144 sqlStmt += pSqlStmt;
2145
2146 WriteSqlLog(sqlStmt);
2147
2148 #ifdef DBDEBUG_CONSOLE
2149 cout << sqlStmt.c_str() << endl;
2150 #endif
2151
2152 return(ExecSql(sqlStmt));
2153
2154 } // wxDb::CreateView()
2155
2156
2157 /********** wxDb::DropView() **********/
2158 bool wxDb::DropView(const wxString &viewName)
2159 {
2160 /*
2161 * NOTE: This function returns true if the View does not exist, but
2162 * only for identified databases. Code will need to be added
2163 * below for any other databases when those databases are defined
2164 * to handle this situation consistently
2165 */
2166 wxString sqlStmt;
2167
2168 sqlStmt.Printf(wxT("DROP VIEW %s"), viewName.c_str());
2169
2170 WriteSqlLog(sqlStmt);
2171
2172 #ifdef DBDEBUG_CONSOLE
2173 cout << endl << sqlStmt.c_str() << endl;
2174 #endif
2175
2176 if (SQLExecDirect(hstmt, (SQLTCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
2177 {
2178 // Check for "Base table not found" error and ignore
2179 GetNextError(henv, hdbc, hstmt);
2180 if (wxStrcmp(sqlState,wxT("S0002"))) // "Base table not found"
2181 {
2182 // Check for product specific error codes
2183 if (!((Dbms() == dbmsSYBASE_ASA && !wxStrcmp(sqlState,wxT("42000"))))) // 5.x (and lower?)
2184 {
2185 DispNextError();
2186 DispAllErrors(henv, hdbc, hstmt);
2187 RollbackTrans();
2188 return false;
2189 }
2190 }
2191 }
2192
2193 // Commit the transaction
2194 if (!CommitTrans())
2195 return false;
2196
2197 return true;
2198
2199 } // wxDb::DropView()
2200
2201
2202 /********** wxDb::ExecSql() **********/
2203 bool wxDb::ExecSql(const wxString &pSqlStmt)
2204 {
2205 RETCODE retcode;
2206
2207 SQLFreeStmt(hstmt, SQL_CLOSE);
2208
2209 retcode = SQLExecDirect(hstmt, (SQLTCHAR FAR *) pSqlStmt.c_str(), SQL_NTS);
2210 if (retcode == SQL_SUCCESS ||
2211 (Dbms() == dbmsDB2 && (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_NO_DATA_FOUND)))
2212 {
2213 return true;
2214 }
2215 else
2216 {
2217 DispAllErrors(henv, hdbc, hstmt);
2218 return false;
2219 }
2220
2221 } // wxDb::ExecSql()
2222
2223
2224 /********** wxDb::ExecSql() with column info **********/
2225 bool wxDb::ExecSql(const wxString &pSqlStmt, wxDbColInf** columns, short& numcols)
2226 {
2227 //execute the statement first
2228 if (!ExecSql(pSqlStmt))
2229 return false;
2230
2231 SWORD noCols;
2232 if (SQLNumResultCols(hstmt, &noCols) != SQL_SUCCESS)
2233 {
2234 DispAllErrors(henv, hdbc, hstmt);
2235 return false;
2236 }
2237
2238 if (noCols == 0)
2239 return false;
2240 else
2241 numcols = noCols;
2242
2243 // Get column information
2244 short colNum;
2245 wxChar name[DB_MAX_COLUMN_NAME_LEN+1];
2246 SWORD Sword;
2247 SDWORD Sdword;
2248 wxDbColInf* pColInf = new wxDbColInf[noCols];
2249
2250 // Fill in column information (name, datatype)
2251 for (colNum = 0; colNum < noCols; colNum++)
2252 {
2253 if (SQLColAttributes(hstmt, (UWORD)(colNum+1), SQL_COLUMN_NAME,
2254 name, sizeof(name),
2255 &Sword, &Sdword) != SQL_SUCCESS)
2256 {
2257 DispAllErrors(henv, hdbc, hstmt);
2258 delete[] pColInf;
2259 return false;
2260 }
2261
2262 wxStrncpy(pColInf[colNum].colName, name, DB_MAX_COLUMN_NAME_LEN);
2263 pColInf[colNum].colName[DB_MAX_COLUMN_NAME_LEN] = 0; // Prevent buffer overrun
2264
2265 if (SQLColAttributes(hstmt, (UWORD)(colNum+1), SQL_COLUMN_TYPE,
2266 NULL, 0, &Sword, &Sdword) != SQL_SUCCESS)
2267 {
2268 DispAllErrors(henv, hdbc, hstmt);
2269 delete[] pColInf;
2270 return false;
2271 }
2272
2273 switch (Sdword)
2274 {
2275 case SQL_WCHAR:
2276 case SQL_WVARCHAR:
2277 case SQL_VARCHAR:
2278 case SQL_CHAR:
2279 pColInf[colNum].dbDataType = DB_DATA_TYPE_VARCHAR;
2280 break;
2281 case SQL_TINYINT:
2282 case SQL_SMALLINT:
2283 case SQL_INTEGER:
2284 case SQL_BIT:
2285 pColInf[colNum].dbDataType = DB_DATA_TYPE_INTEGER;
2286 break;
2287 case SQL_DOUBLE:
2288 case SQL_DECIMAL:
2289 case SQL_NUMERIC:
2290 case SQL_FLOAT:
2291 case SQL_REAL:
2292 pColInf[colNum].dbDataType = DB_DATA_TYPE_FLOAT;
2293 break;
2294 case SQL_DATE:
2295 case SQL_TIMESTAMP:
2296 pColInf[colNum].dbDataType = DB_DATA_TYPE_DATE;
2297 break;
2298 case SQL_BINARY:
2299 pColInf[colNum].dbDataType = DB_DATA_TYPE_BLOB;
2300 break;
2301 #ifdef __WXDEBUG__
2302 default:
2303 wxString errMsg;
2304 errMsg.Printf(wxT("SQL Data type %ld currently not supported by wxWidgets"), (long)Sdword);
2305 wxLogDebug(errMsg,wxT("ODBC DEBUG MESSAGE"));
2306 #endif
2307 }
2308 }
2309
2310 *columns = pColInf;
2311 return true;
2312 } // wxDb::ExecSql()
2313
2314 /********** wxDb::GetNext() **********/
2315 bool wxDb::GetNext(void)
2316 {
2317 if (SQLFetch(hstmt) == SQL_SUCCESS)
2318 return true;
2319 else
2320 {
2321 DispAllErrors(henv, hdbc, hstmt);
2322 return false;
2323 }
2324
2325 } // wxDb::GetNext()
2326
2327
2328 /********** wxDb::GetData() **********/
2329 bool wxDb::GetData(UWORD colNo, SWORD cType, PTR pData, SDWORD maxLen, SDWORD FAR *cbReturned)
2330 {
2331 wxASSERT(pData);
2332 wxASSERT(cbReturned);
2333
2334 long bufferSize = maxLen;
2335
2336 if (cType == SQL_C_WXCHAR)
2337 bufferSize = maxLen * sizeof(wxChar);
2338
2339 if (SQLGetData(hstmt, colNo, cType, pData, bufferSize, cbReturned) == SQL_SUCCESS)
2340 return true;
2341 else
2342 {
2343 DispAllErrors(henv, hdbc, hstmt);
2344 return false;
2345 }
2346
2347 } // wxDb::GetData()
2348
2349
2350 /********** wxDb::GetKeyFields() **********/
2351 int wxDb::GetKeyFields(const wxString &tableName, wxDbColInf* colInf, UWORD noCols)
2352 {
2353 wxChar szPkTable[DB_MAX_TABLE_NAME_LEN+1]; /* Primary key table name */
2354 wxChar szFkTable[DB_MAX_TABLE_NAME_LEN+1]; /* Foreign key table name */
2355 SWORD iKeySeq;
2356 wxChar szPkCol[DB_MAX_COLUMN_NAME_LEN+1]; /* Primary key column */
2357 wxChar szFkCol[DB_MAX_COLUMN_NAME_LEN+1]; /* Foreign key column */
2358 SQLRETURN retcode;
2359 SDWORD cb;
2360 SWORD i;
2361 wxString tempStr;
2362 /*
2363 * -----------------------------------------------------------------------
2364 * -- 19991224 : mj10777 : Create ------
2365 * -- : Three things are done and stored here : ------
2366 * -- : 1) which Column(s) is/are Primary Key(s) ------
2367 * -- : 2) which tables use this Key as a Foreign Key ------
2368 * -- : 3) which columns are Foreign Key and the name ------
2369 * -- : of the Table where the Key is the Primary Key -----
2370 * -- : Called from GetColumns(const wxString &tableName, ------
2371 * -- int *numCols,const wxChar *userID ) ------
2372 * -----------------------------------------------------------------------
2373 */
2374
2375 /*---------------------------------------------------------------------*/
2376 /* Get the names of the columns in the primary key. */
2377 /*---------------------------------------------------------------------*/
2378 retcode = SQLPrimaryKeys(hstmt,
2379 NULL, 0, /* Catalog name */
2380 NULL, 0, /* Schema name */
2381 (SQLTCHAR FAR *) tableName.c_str(), SQL_NTS); /* Table name */
2382
2383 /*---------------------------------------------------------------------*/
2384 /* Fetch and display the result set. This will be a list of the */
2385 /* columns in the primary key of the tableName table. */
2386 /*---------------------------------------------------------------------*/
2387 while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
2388 {
2389 retcode = SQLFetch(hstmt);
2390 if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
2391 {
2392 GetData( 4, SQL_C_WXCHAR, szPkCol, DB_MAX_COLUMN_NAME_LEN+1, &cb);
2393 GetData( 5, SQL_C_SSHORT, &iKeySeq, 0, &cb);
2394 //-------
2395 for (i=0;i<noCols;i++) // Find the Column name
2396 if (!wxStrcmp(colInf[i].colName,szPkCol)) // We have found the Column
2397 colInf[i].PkCol = iKeySeq; // Which Primary Key is this (first, second usw.) ?
2398 } // if
2399 } // while
2400 SQLFreeStmt(hstmt, SQL_CLOSE); /* Close the cursor (the hstmt is still allocated). */
2401
2402 /*---------------------------------------------------------------------*/
2403 /* Get all the foreign keys that refer to tableName primary key. */
2404 /*---------------------------------------------------------------------*/
2405 retcode = SQLForeignKeys(hstmt,
2406 NULL, 0, /* Primary catalog */
2407 NULL, 0, /* Primary schema */
2408 (SQLTCHAR FAR *)tableName.c_str(), SQL_NTS,/* Primary table */
2409 NULL, 0, /* Foreign catalog */
2410 NULL, 0, /* Foreign schema */
2411 NULL, 0); /* Foreign table */
2412
2413 /*---------------------------------------------------------------------*/
2414 /* Fetch and display the result set. This will be all of the foreign */
2415 /* keys in other tables that refer to the tableName primary key. */
2416 /*---------------------------------------------------------------------*/
2417 tempStr.Empty();
2418 szPkCol[0] = 0;
2419 while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
2420 {
2421 retcode = SQLFetch(hstmt);
2422 if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
2423 {
2424 GetData( 3, SQL_C_WXCHAR, szPkTable, DB_MAX_TABLE_NAME_LEN+1, &cb);
2425 GetData( 4, SQL_C_WXCHAR, szPkCol, DB_MAX_COLUMN_NAME_LEN+1, &cb);
2426 GetData( 5, SQL_C_SSHORT, &iKeySeq, 0, &cb);
2427 GetData( 7, SQL_C_WXCHAR, szFkTable, DB_MAX_TABLE_NAME_LEN+1, &cb);
2428 GetData( 8, SQL_C_WXCHAR, szFkCol, DB_MAX_COLUMN_NAME_LEN+1, &cb);
2429 tempStr.Printf(wxT("%s[%s] "),tempStr.c_str(),szFkTable); // [ ] in case there is a blank in the Table name
2430 } // if
2431 } // while
2432
2433 tempStr.Trim(); // Get rid of any unneeded blanks
2434 if (!tempStr.empty())
2435 {
2436 for (i=0; i<noCols; i++)
2437 { // Find the Column name
2438 if (!wxStrcmp(colInf[i].colName, szPkCol)) // We have found the Column, store the Information
2439 {
2440 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
2441 colInf[i].PkTableName[DB_MAX_TABLE_NAME_LEN] = 0; // Prevent buffer overrun
2442 }
2443 }
2444 } // if
2445
2446 SQLFreeStmt(hstmt, SQL_CLOSE); /* Close the cursor (the hstmt is still allocated). */
2447
2448 /*---------------------------------------------------------------------*/
2449 /* Get all the foreign keys in the tablename table. */
2450 /*---------------------------------------------------------------------*/
2451 retcode = SQLForeignKeys(hstmt,
2452 NULL, 0, /* Primary catalog */
2453 NULL, 0, /* Primary schema */
2454 NULL, 0, /* Primary table */
2455 NULL, 0, /* Foreign catalog */
2456 NULL, 0, /* Foreign schema */
2457 (SQLTCHAR *)tableName.c_str(), SQL_NTS);/* Foreign table */
2458
2459 /*---------------------------------------------------------------------*/
2460 /* Fetch and display the result set. This will be all of the */
2461 /* primary keys in other tables that are referred to by foreign */
2462 /* keys in the tableName table. */
2463 /*---------------------------------------------------------------------*/
2464 while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
2465 {
2466 retcode = SQLFetch(hstmt);
2467 if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
2468 {
2469 GetData( 3, SQL_C_WXCHAR, szPkTable, DB_MAX_TABLE_NAME_LEN+1, &cb);
2470 GetData( 5, SQL_C_SSHORT, &iKeySeq, 0, &cb);
2471 GetData( 8, SQL_C_WXCHAR, szFkCol, DB_MAX_COLUMN_NAME_LEN+1, &cb);
2472 //-------
2473 for (i=0; i<noCols; i++) // Find the Column name
2474 {
2475 if (!wxStrcmp(colInf[i].colName,szFkCol)) // We have found the (Foreign Key) Column
2476 {
2477 colInf[i].FkCol = iKeySeq; // Which Foreign Key is this (first, second usw.) ?
2478 wxStrncpy(colInf[i].FkTableName, szFkTable, DB_MAX_TABLE_NAME_LEN); // Name of the Table where this Foriegn is the Primary Key
2479 colInf[i].FkTableName[DB_MAX_TABLE_NAME_LEN] = 0; // Prevent buffer overrun
2480 } // if
2481 } // for
2482 } // if
2483 } // while
2484 SQLFreeStmt(hstmt, SQL_CLOSE); /* Close the cursor (the hstmt is still allocated). */
2485
2486 return TRUE;
2487
2488 } // wxDb::GetKeyFields()
2489
2490
2491 #if OLD_GETCOLUMNS
2492 /********** wxDb::GetColumns() **********/
2493 wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const wxChar *userID)
2494 /*
2495 * 1) The last array element of the tableName[] argument must be zero (null).
2496 * This is how the end of the array is detected.
2497 * 2) This function returns an array of wxDbColInf structures. If no columns
2498 * were found, or an error occured, this pointer will be zero (null). THE
2499 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
2500 * IS FINISHED WITH IT. i.e.
2501 *
2502 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
2503 * if (colInf)
2504 * {
2505 * // Use the column inf
2506 * .......
2507 * // Destroy the memory
2508 * delete [] colInf;
2509 * }
2510 *
2511 * userID is evaluated in the following manner:
2512 * userID == NULL ... UserID is ignored
2513 * userID == "" ... UserID set equal to 'this->uid'
2514 * userID != "" ... UserID set equal to 'userID'
2515 *
2516 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2517 * by this function. This function should use its own wxDb instance
2518 * to avoid undesired unbinding of columns.
2519 */
2520 {
2521 UWORD noCols = 0;
2522 UWORD colNo = 0;
2523 wxDbColInf *colInf = 0;
2524
2525 RETCODE retcode;
2526 SDWORD cb;
2527
2528 wxString TableName;
2529
2530 wxString UserID;
2531 convertUserID(userID,UserID);
2532
2533 // Pass 1 - Determine how many columns there are.
2534 // Pass 2 - Allocate the wxDbColInf array and fill in
2535 // the array with the column information.
2536 int pass;
2537 for (pass = 1; pass <= 2; pass++)
2538 {
2539 if (pass == 2)
2540 {
2541 if (noCols == 0) // Probably a bogus table name(s)
2542 break;
2543 // Allocate n wxDbColInf objects to hold the column information
2544 colInf = new wxDbColInf[noCols+1];
2545 if (!colInf)
2546 break;
2547 // Mark the end of the array
2548 wxStrcpy(colInf[noCols].tableName, wxEmptyString);
2549 wxStrcpy(colInf[noCols].colName, wxEmptyString);
2550 colInf[noCols].sqlDataType = 0;
2551 }
2552 // Loop through each table name
2553 int tbl;
2554 for (tbl = 0; tableName[tbl]; tbl++)
2555 {
2556 TableName = tableName[tbl];
2557 // Oracle and Interbase table names are uppercase only, so force
2558 // the name to uppercase just in case programmer forgot to do this
2559 if ((Dbms() == dbmsORACLE) ||
2560 (Dbms() == dbmsFIREBIRD) ||
2561 (Dbms() == dbmsINTERBASE))
2562 TableName = TableName.Upper();
2563
2564 SQLFreeStmt(hstmt, SQL_CLOSE);
2565
2566 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2567 // use the call below that leaves out the user name
2568 if (!UserID.empty() &&
2569 Dbms() != dbmsMY_SQL &&
2570 Dbms() != dbmsACCESS &&
2571 Dbms() != dbmsMS_SQL_SERVER)
2572 {
2573 retcode = SQLColumns(hstmt,
2574 NULL, 0, // All qualifiers
2575 (SQLTCHAR *) UserID.c_str(), SQL_NTS, // Owner
2576 (SQLTCHAR *) TableName.c_str(), SQL_NTS,
2577 NULL, 0); // All columns
2578 }
2579 else
2580 {
2581 retcode = SQLColumns(hstmt,
2582 NULL, 0, // All qualifiers
2583 NULL, 0, // Owner
2584 (SQLTCHAR *) TableName.c_str(), SQL_NTS,
2585 NULL, 0); // All columns
2586 }
2587 if (retcode != SQL_SUCCESS)
2588 { // Error occured, abort
2589 DispAllErrors(henv, hdbc, hstmt);
2590 if (colInf)
2591 delete [] colInf;
2592 SQLFreeStmt(hstmt, SQL_CLOSE);
2593 return(0);
2594 }
2595
2596 while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
2597 {
2598 if (pass == 1) // First pass, just add up the number of columns
2599 noCols++;
2600 else // Pass 2; Fill in the array of structures
2601 {
2602 if (colNo < noCols) // Some extra error checking to prevent memory overwrites
2603 {
2604 // NOTE: Only the ODBC 1.x fields are retrieved
2605 GetData( 1, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].catalog, 128+1, &cb);
2606 GetData( 2, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].schema, 128+1, &cb);
2607 GetData( 3, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].tableName, DB_MAX_TABLE_NAME_LEN+1, &cb);
2608 GetData( 4, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].colName, DB_MAX_COLUMN_NAME_LEN+1, &cb);
2609 GetData( 5, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].sqlDataType, 0, &cb);
2610 GetData( 6, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].typeName, 128+1, &cb);
2611 GetData( 7, SQL_C_SLONG, (UCHAR*) &colInf[colNo].columnLength, 0, &cb);
2612 GetData( 8, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].bufferSize, 0, &cb);
2613 GetData( 9, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].decimalDigits,0, &cb);
2614 GetData(10, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].numPrecRadix, 0, &cb);
2615 GetData(11, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].nullable, 0, &cb);
2616 GetData(12, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].remarks, 254+1, &cb);
2617
2618 // Determine the wxDb data type that is used to represent the native data type of this data source
2619 colInf[colNo].dbDataType = 0;
2620 if (!wxStricmp(typeInfVarchar.TypeName,colInf[colNo].typeName))
2621 {
2622 #ifdef _IODBC_
2623 // IODBC does not return a correct columnLength, so we set
2624 // columnLength = bufferSize if no column length was returned
2625 // IODBC returns the columnLength in bufferSize. (bug)
2626 if (colInf[colNo].columnLength < 1)
2627 {
2628 colInf[colNo].columnLength = colInf[colNo].bufferSize;
2629 }
2630 #endif
2631 colInf[colNo].dbDataType = DB_DATA_TYPE_VARCHAR;
2632 }
2633 else if (!wxStricmp(typeInfInteger.TypeName, colInf[colNo].typeName))
2634 colInf[colNo].dbDataType = DB_DATA_TYPE_INTEGER;
2635 else if (!wxStricmp(typeInfFloat.TypeName, colInf[colNo].typeName))
2636 colInf[colNo].dbDataType = DB_DATA_TYPE_FLOAT;
2637 else if (!wxStricmp(typeInfDate.TypeName, colInf[colNo].typeName))
2638 colInf[colNo].dbDataType = DB_DATA_TYPE_DATE;
2639 else if (!wxStricmp(typeInfBlob.TypeName, colInf[colNo].typeName))
2640 colInf[colNo].dbDataType = DB_DATA_TYPE_BLOB;
2641 colNo++;
2642 }
2643 }
2644 }
2645 if (retcode != SQL_NO_DATA_FOUND)
2646 { // Error occured, abort
2647 DispAllErrors(henv, hdbc, hstmt);
2648 if (colInf)
2649 delete [] colInf;
2650 SQLFreeStmt(hstmt, SQL_CLOSE);
2651 return(0);
2652 }
2653 }
2654 }
2655
2656 SQLFreeStmt(hstmt, SQL_CLOSE);
2657 return colInf;
2658
2659 } // wxDb::GetColumns()
2660
2661
2662 /********** wxDb::GetColumns() **********/
2663
2664 wxDbColInf *wxDb::GetColumns(const wxString &tableName, UWORD *numCols, const wxChar *userID)
2665 //
2666 // Same as the above GetColumns() function except this one gets columns
2667 // only for a single table, and if 'numCols' is not NULL, the number of
2668 // columns stored in the returned wxDbColInf is set in '*numCols'
2669 //
2670 // userID is evaluated in the following manner:
2671 // userID == NULL ... UserID is ignored
2672 // userID == "" ... UserID set equal to 'this->uid'
2673 // userID != "" ... UserID set equal to 'userID'
2674 //
2675 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2676 // by this function. This function should use its own wxDb instance
2677 // to avoid undesired unbinding of columns.
2678
2679 {
2680 UWORD noCols = 0;
2681 UWORD colNo = 0;
2682 wxDbColInf *colInf = 0;
2683
2684 RETCODE retcode;
2685 SDWORD cb;
2686
2687 wxString TableName;
2688
2689 wxString UserID;
2690 convertUserID(userID,UserID);
2691
2692 // Pass 1 - Determine how many columns there are.
2693 // Pass 2 - Allocate the wxDbColInf array and fill in
2694 // the array with the column information.
2695 int pass;
2696 for (pass = 1; pass <= 2; pass++)
2697 {
2698 if (pass == 2)
2699 {
2700 if (noCols == 0) // Probably a bogus table name(s)
2701 break;
2702 // Allocate n wxDbColInf objects to hold the column information
2703 colInf = new wxDbColInf[noCols+1];
2704 if (!colInf)
2705 break;
2706 // Mark the end of the array
2707 wxStrcpy(colInf[noCols].tableName, wxEmptyString);
2708 wxStrcpy(colInf[noCols].colName, wxEmptyString);
2709 colInf[noCols].sqlDataType = 0;
2710 }
2711
2712 TableName = tableName;
2713 // Oracle and Interbase table names are uppercase only, so force
2714 // the name to uppercase just in case programmer forgot to do this
2715 if ((Dbms() == dbmsORACLE) ||
2716 (Dbms() == dbmsFIREBIRD) ||
2717 (Dbms() == dbmsINTERBASE))
2718 TableName = TableName.Upper();
2719
2720 SQLFreeStmt(hstmt, SQL_CLOSE);
2721
2722 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2723 // use the call below that leaves out the user name
2724 if (!UserID.empty() &&
2725 Dbms() != dbmsMY_SQL &&
2726 Dbms() != dbmsACCESS &&
2727 Dbms() != dbmsMS_SQL_SERVER)
2728 {
2729 retcode = SQLColumns(hstmt,
2730 NULL, 0, // All qualifiers
2731 (SQLTCHAR *) UserID.c_str(), SQL_NTS, // Owner
2732 (SQLTCHAR *) TableName.c_str(), SQL_NTS,
2733 NULL, 0); // All columns
2734 }
2735 else
2736 {
2737 retcode = SQLColumns(hstmt,
2738 NULL, 0, // All qualifiers
2739 NULL, 0, // Owner
2740 (SQLTCHAR *) TableName.c_str(), SQL_NTS,
2741 NULL, 0); // All columns
2742 }
2743 if (retcode != SQL_SUCCESS)
2744 { // Error occured, abort
2745 DispAllErrors(henv, hdbc, hstmt);
2746 if (colInf)
2747 delete [] colInf;
2748 SQLFreeStmt(hstmt, SQL_CLOSE);
2749 if (numCols)
2750 *numCols = 0;
2751 return(0);
2752 }
2753
2754 while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
2755 {
2756 if (pass == 1) // First pass, just add up the number of columns
2757 noCols++;
2758 else // Pass 2; Fill in the array of structures
2759 {
2760 if (colNo < noCols) // Some extra error checking to prevent memory overwrites
2761 {
2762 // NOTE: Only the ODBC 1.x fields are retrieved
2763 GetData( 1, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].catalog, 128+1, &cb);
2764 GetData( 2, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].schema, 128+1, &cb);
2765 GetData( 3, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].tableName, DB_MAX_TABLE_NAME_LEN+1, &cb);
2766 GetData( 4, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].colName, DB_MAX_COLUMN_NAME_LEN+1, &cb);
2767 GetData( 5, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].sqlDataType, 0, &cb);
2768 GetData( 6, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].typeName, 128+1, &cb);
2769 GetData( 7, SQL_C_SLONG, (UCHAR*) &colInf[colNo].columnLength, 0, &cb);
2770 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
2771 GetData( 8, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].bufferSize, 0, &cb);
2772 GetData( 9, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].decimalDigits,0, &cb);
2773 GetData(10, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].numPrecRadix, 0, &cb);
2774 GetData(11, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].nullable, 0, &cb);
2775 GetData(12, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].remarks, 254+1, &cb);
2776 // Start Values for Primary/Foriegn Key (=No)
2777 colInf[colNo].PkCol = 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
2778 colInf[colNo].PkTableName[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
2779 colInf[colNo].FkCol = 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
2780 colInf[colNo].FkTableName[0] = 0; // Foreign key table name
2781
2782 // BJO 20000428 : Virtuoso returns type names with upper cases!
2783 if (Dbms() == dbmsVIRTUOSO)
2784 {
2785 wxString s = colInf[colNo].typeName;
2786 s = s.MakeLower();
2787 wxStrcmp(colInf[colNo].typeName, s.c_str());
2788 }
2789
2790 // Determine the wxDb data type that is used to represent the native data type of this data source
2791 colInf[colNo].dbDataType = 0;
2792 if (!wxStricmp(typeInfVarchar.TypeName, colInf[colNo].typeName))
2793 {
2794 #ifdef _IODBC_
2795 // IODBC does not return a correct columnLength, so we set
2796 // columnLength = bufferSize if no column length was returned
2797 // IODBC returns the columnLength in bufferSize. (bug)
2798 if (colInf[colNo].columnLength < 1)
2799 {
2800 colInf[colNo].columnLength = colInf[colNo].bufferSize;
2801 }
2802 #endif
2803
2804 colInf[colNo].dbDataType = DB_DATA_TYPE_VARCHAR;
2805 }
2806 else if (!wxStricmp(typeInfInteger.TypeName, colInf[colNo].typeName))
2807 colInf[colNo].dbDataType = DB_DATA_TYPE_INTEGER;
2808 else if (!wxStricmp(typeInfFloat.TypeName, colInf[colNo].typeName))
2809 colInf[colNo].dbDataType = DB_DATA_TYPE_FLOAT;
2810 else if (!wxStricmp(typeInfDate.TypeName, colInf[colNo].typeName))
2811 colInf[colNo].dbDataType = DB_DATA_TYPE_DATE;
2812 else if (!wxStricmp(typeInfBlob.TypeName, colInf[colNo].typeName))
2813 colInf[colNo].dbDataType = DB_DATA_TYPE_BLOB;
2814
2815 colNo++;
2816 }
2817 }
2818 }
2819 if (retcode != SQL_NO_DATA_FOUND)
2820 { // Error occured, abort
2821 DispAllErrors(henv, hdbc, hstmt);
2822 if (colInf)
2823 delete [] colInf;
2824 SQLFreeStmt(hstmt, SQL_CLOSE);
2825 if (numCols)
2826 *numCols = 0;
2827 return(0);
2828 }
2829 }
2830
2831 SQLFreeStmt(hstmt, SQL_CLOSE);
2832
2833 // Store Primary and Foriegn Keys
2834 GetKeyFields(tableName,colInf,noCols);
2835
2836 if (numCols)
2837 *numCols = noCols;
2838 return colInf;
2839
2840 } // wxDb::GetColumns()
2841
2842
2843 #else // New GetColumns
2844
2845
2846 /*
2847 BJO 20000503
2848 These are tentative new GetColumns members which should be more database
2849 independant and which always returns the columns in the order they were
2850 created.
2851
2852 - The first one (wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const
2853 wxChar* userID)) calls the second implementation for each separate table
2854 before merging the results. This makes the code easier to maintain as
2855 only one member (the second) makes the real work
2856 - wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const
2857 wxChar *userID) is a little bit improved
2858 - It doesn't anymore rely on the type-name to find out which database-type
2859 each column has
2860 - It ends by sorting the columns, so that they are returned in the same
2861 order they were created
2862 */
2863
2864 typedef struct
2865 {
2866 UWORD noCols;
2867 wxDbColInf *colInf;
2868 } _TableColumns;
2869
2870
2871 wxDbColInf *wxDb::GetColumns(wxChar *tableName[], const wxChar *userID)
2872 {
2873 int i, j;
2874 // The last array element of the tableName[] argument must be zero (null).
2875 // This is how the end of the array is detected.
2876
2877 UWORD noCols = 0;
2878
2879 // How many tables ?
2880 int tbl;
2881 for (tbl = 0 ; tableName[tbl]; tbl++);
2882
2883 // Create a table to maintain the columns for each separate table
2884 _TableColumns *TableColumns = new _TableColumns[tbl];
2885
2886 // Fill the table
2887 for (i = 0 ; i < tbl ; i++)
2888
2889 {
2890 TableColumns[i].colInf = GetColumns(tableName[i], &TableColumns[i].noCols, userID);
2891 if (TableColumns[i].colInf == NULL)
2892 return NULL;
2893 noCols += TableColumns[i].noCols;
2894 }
2895
2896 // Now merge all the separate table infos
2897 wxDbColInf *colInf = new wxDbColInf[noCols+1];
2898
2899 // Mark the end of the array
2900 wxStrcpy(colInf[noCols].tableName, wxEmptyString);
2901 wxStrcpy(colInf[noCols].colName, wxEmptyString);
2902 colInf[noCols].sqlDataType = 0;
2903
2904 // Merge ...
2905 int offset = 0;
2906
2907 for (i = 0 ; i < tbl ; i++)
2908 {
2909 for (j = 0 ; j < TableColumns[i].noCols ; j++)
2910 {
2911 colInf[offset++] = TableColumns[i].colInf[j];
2912 }
2913 }
2914
2915 delete [] TableColumns;
2916
2917 return colInf;
2918 } // wxDb::GetColumns() -- NEW
2919
2920
2921 wxDbColInf *wxDb::GetColumns(const wxString &tableName, int *numCols, const wxChar *userID)
2922 //
2923 // Same as the above GetColumns() function except this one gets columns
2924 // only for a single table, and if 'numCols' is not NULL, the number of
2925 // columns stored in the returned wxDbColInf is set in '*numCols'
2926 //
2927 // userID is evaluated in the following manner:
2928 // userID == NULL ... UserID is ignored
2929 // userID == "" ... UserID set equal to 'this->uid'
2930 // userID != "" ... UserID set equal to 'userID'
2931 //
2932 // NOTE: ALL column bindings associated with this wxDb instance are unbound
2933 // by this function. This function should use its own wxDb instance
2934 // to avoid undesired unbinding of columns.
2935 {
2936 UWORD noCols = 0;
2937 UWORD colNo = 0;
2938 wxDbColInf *colInf = 0;
2939
2940 RETCODE retcode;
2941 SDWORD cb;
2942
2943 wxString TableName;
2944
2945 wxString UserID;
2946 convertUserID(userID,UserID);
2947
2948 // Pass 1 - Determine how many columns there are.
2949 // Pass 2 - Allocate the wxDbColInf array and fill in
2950 // the array with the column information.
2951 int pass;
2952 for (pass = 1; pass <= 2; pass++)
2953 {
2954 if (pass == 2)
2955 {
2956 if (noCols == 0) // Probably a bogus table name(s)
2957 break;
2958 // Allocate n wxDbColInf objects to hold the column information
2959 colInf = new wxDbColInf[noCols+1];
2960 if (!colInf)
2961 break;
2962 // Mark the end of the array
2963 wxStrcpy(colInf[noCols].tableName, wxEmptyString);
2964 wxStrcpy(colInf[noCols].colName, wxEmptyString);
2965 colInf[noCols].sqlDataType = 0;
2966 }
2967
2968 TableName = tableName;
2969 // Oracle and Interbase table names are uppercase only, so force
2970 // the name to uppercase just in case programmer forgot to do this
2971 if ((Dbms() == dbmsORACLE) ||
2972 (Dbms() == dbmsFIREBIRD) ||
2973 (Dbms() == dbmsINTERBASE))
2974 TableName = TableName.Upper();
2975
2976 SQLFreeStmt(hstmt, SQL_CLOSE);
2977
2978 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
2979 // use the call below that leaves out the user name
2980 if (!UserID.empty() &&
2981 Dbms() != dbmsMY_SQL &&
2982 Dbms() != dbmsACCESS &&
2983 Dbms() != dbmsMS_SQL_SERVER)
2984 {
2985 retcode = SQLColumns(hstmt,
2986 NULL, 0, // All qualifiers
2987 (UCHAR *) UserID.c_str(), SQL_NTS, // Owner
2988 (UCHAR *) TableName.c_str(), SQL_NTS,
2989 NULL, 0); // All columns
2990 }
2991 else
2992 {
2993 retcode = SQLColumns(hstmt,
2994 NULL, 0, // All qualifiers
2995 NULL, 0, // Owner
2996 (UCHAR *) TableName.c_str(), SQL_NTS,
2997 NULL, 0); // All columns
2998 }
2999 if (retcode != SQL_SUCCESS)
3000 { // Error occured, abort
3001 DispAllErrors(henv, hdbc, hstmt);
3002 if (colInf)
3003 delete [] colInf;
3004 SQLFreeStmt(hstmt, SQL_CLOSE);
3005 if (numCols)
3006 *numCols = 0;
3007 return(0);
3008 }
3009
3010 while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
3011 {
3012 if (pass == 1) // First pass, just add up the number of columns
3013 noCols++;
3014 else // Pass 2; Fill in the array of structures
3015 {
3016 if (colNo < noCols) // Some extra error checking to prevent memory overwrites
3017 {
3018 // NOTE: Only the ODBC 1.x fields are retrieved
3019 GetData( 1, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].catalog, 128+1, &cb);
3020 GetData( 2, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].schema, 128+1, &cb);
3021 GetData( 3, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].tableName, DB_MAX_TABLE_NAME_LEN+1, &cb);
3022 GetData( 4, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].colName, DB_MAX_COLUMN_NAME_LEN+1, &cb);
3023 GetData( 5, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].sqlDataType, 0, &cb);
3024 GetData( 6, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].typeName, 128+1, &cb);
3025 GetData( 7, SQL_C_SLONG, (UCHAR*) &colInf[colNo].columnLength, 0, &cb);
3026 GetData( 8, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].bufferSize, 0, &cb);
3027 GetData( 9, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].decimalDigits,0, &cb);
3028 GetData(10, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].numPrecRadix, 0, &cb);
3029 GetData(11, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].nullable, 0, &cb);
3030 GetData(12, SQL_C_WXCHAR, (UCHAR*) colInf[colNo].remarks, 254+1, &cb);
3031 // Start Values for Primary/Foriegn Key (=No)
3032 colInf[colNo].PkCol = 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
3033 colInf[colNo].PkTableName[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
3034 colInf[colNo].FkCol = 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
3035 colInf[colNo].FkTableName[0] = 0; // Foreign key table name
3036
3037 #ifdef _IODBC_
3038 // IODBC does not return a correct columnLength, so we set
3039 // columnLength = bufferSize if no column length was returned
3040 // IODBC returns the columnLength in bufferSize. (bug)
3041 if (colInf[colNo].columnLength < 1)
3042 {
3043 colInf[colNo].columnLength = colInf[colNo].bufferSize;
3044 }
3045 #endif
3046
3047 // Determine the wxDb data type that is used to represent the native data type of this data source
3048 colInf[colNo].dbDataType = 0;
3049 // Get the intern datatype
3050 switch (colInf[colNo].sqlDataType)
3051 {
3052 case SQL_WCHAR:
3053 case SQL_WVARCHAR:
3054 case SQL_VARCHAR:
3055 case SQL_CHAR:
3056 colInf[colNo].dbDataType = DB_DATA_TYPE_VARCHAR;
3057 break;
3058 case SQL_TINYINT:
3059 case SQL_SMALLINT:
3060 case SQL_INTEGER:
3061 case SQL_BIT:
3062 colInf[colNo].dbDataType = DB_DATA_TYPE_INTEGER;
3063 break;
3064 case SQL_DOUBLE:
3065 case SQL_DECIMAL:
3066 case SQL_NUMERIC:
3067 case SQL_FLOAT:
3068 case SQL_REAL:
3069 colInf[colNo].dbDataType = DB_DATA_TYPE_FLOAT;
3070 break;
3071 case SQL_DATE:
3072 case SQL_TIMESTAMP:
3073 colInf[colNo].dbDataType = DB_DATA_TYPE_DATE;
3074 break;
3075 case SQL_BINARY:
3076 colInf[colNo].dbDataType = DB_DATA_TYPE_BLOB;
3077 break;
3078 #ifdef __WXDEBUG__
3079 default:
3080 wxString errMsg;
3081 errMsg.Printf(wxT("SQL Data type %d currently not supported by wxWidgets"), colInf[colNo].sqlDataType);
3082 wxLogDebug(errMsg,wxT("ODBC DEBUG MESSAGE"));
3083 #endif
3084 }
3085 colNo++;
3086 }
3087 }
3088 }
3089 if (retcode != SQL_NO_DATA_FOUND)
3090 { // Error occured, abort
3091 DispAllErrors(henv, hdbc, hstmt);
3092 if (colInf)
3093 delete [] colInf;
3094 SQLFreeStmt(hstmt, SQL_CLOSE);
3095 if (numCols)
3096 *numCols = 0;
3097 return(0);
3098 }
3099 }
3100
3101 SQLFreeStmt(hstmt, SQL_CLOSE);
3102
3103 // Store Primary and Foreign Keys
3104 GetKeyFields(tableName,colInf,noCols);
3105
3106 ///////////////////////////////////////////////////////////////////////////
3107 // Now sort the the columns in order to make them appear in the right order
3108 ///////////////////////////////////////////////////////////////////////////
3109
3110 // Build a generic SELECT statement which returns 0 rows
3111 wxString Stmt;
3112
3113 Stmt.Printf(wxT("select * from \"%s\" where 0=1"), tableName);
3114
3115 // Execute query
3116 if (SQLExecDirect(hstmt, (UCHAR FAR *) Stmt.c_str(), SQL_NTS) != SQL_SUCCESS)
3117 {
3118 DispAllErrors(henv, hdbc, hstmt);
3119 return NULL;
3120 }
3121
3122 // Get the number of result columns
3123 if (SQLNumResultCols (hstmt, &noCols) != SQL_SUCCESS)
3124 {
3125 DispAllErrors(henv, hdbc, hstmt);
3126 return NULL;
3127 }
3128
3129 if (noCols == 0) // Probably a bogus table name
3130 return NULL;
3131
3132 // Get the name
3133 int i;
3134 short colNum;
3135 UCHAR name[100];
3136 SWORD Sword;
3137 SDWORD Sdword;
3138 for (colNum = 0; colNum < noCols; colNum++)
3139 {
3140 if (SQLColAttributes(hstmt,colNum+1, SQL_COLUMN_NAME,
3141 name, sizeof(name),
3142 &Sword, &Sdword) != SQL_SUCCESS)
3143 {
3144 DispAllErrors(henv, hdbc, hstmt);
3145 return NULL;
3146 }
3147
3148 wxString Name1 = name;
3149 Name1 = Name1.Upper();
3150
3151 // Where is this name in the array ?
3152 for (i = colNum ; i < noCols ; i++)
3153 {
3154 wxString Name2 = colInf[i].colName;
3155 Name2 = Name2.Upper();
3156 if (Name2 == Name1)
3157 {
3158 if (colNum != i) // swap to sort
3159 {
3160 wxDbColInf tmpColInf = colInf[colNum];
3161 colInf[colNum] = colInf[i];
3162 colInf[i] = tmpColInf;
3163 }
3164 break;
3165 }
3166 }
3167 }
3168 SQLFreeStmt(hstmt, SQL_CLOSE);
3169
3170 ///////////////////////////////////////////////////////////////////////////
3171 // End sorting
3172 ///////////////////////////////////////////////////////////////////////////
3173
3174 if (numCols)
3175 *numCols = noCols;
3176 return colInf;
3177
3178 } // wxDb::GetColumns()
3179
3180
3181 #endif // #else OLD_GETCOLUMNS
3182
3183
3184 /********** wxDb::GetColumnCount() **********/
3185 int wxDb::GetColumnCount(const wxString &tableName, const wxChar *userID)
3186 /*
3187 * Returns a count of how many columns are in a table.
3188 * If an error occurs in computing the number of columns
3189 * this function will return a -1 for the count
3190 *
3191 * userID is evaluated in the following manner:
3192 * userID == NULL ... UserID is ignored
3193 * userID == "" ... UserID set equal to 'this->uid'
3194 * userID != "" ... UserID set equal to 'userID'
3195 *
3196 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3197 * by this function. This function should use its own wxDb instance
3198 * to avoid undesired unbinding of columns.
3199 */
3200 {
3201 UWORD noCols = 0;
3202
3203 RETCODE retcode;
3204
3205 wxString TableName;
3206
3207 wxString UserID;
3208 convertUserID(userID,UserID);
3209
3210 TableName = tableName;
3211 // Oracle and Interbase table names are uppercase only, so force
3212 // the name to uppercase just in case programmer forgot to do this
3213 if ((Dbms() == dbmsORACLE) ||
3214 (Dbms() == dbmsFIREBIRD) ||
3215 (Dbms() == dbmsINTERBASE))
3216 TableName = TableName.Upper();
3217
3218 SQLFreeStmt(hstmt, SQL_CLOSE);
3219
3220 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3221 // use the call below that leaves out the user name
3222 if (!UserID.empty() &&
3223 Dbms() != dbmsMY_SQL &&
3224 Dbms() != dbmsACCESS &&
3225 Dbms() != dbmsMS_SQL_SERVER)
3226 {
3227 retcode = SQLColumns(hstmt,
3228 NULL, 0, // All qualifiers
3229 (SQLTCHAR *) UserID.c_str(), SQL_NTS, // Owner
3230 (SQLTCHAR *) TableName.c_str(), SQL_NTS,
3231 NULL, 0); // All columns
3232 }
3233 else
3234 {
3235 retcode = SQLColumns(hstmt,
3236 NULL, 0, // All qualifiers
3237 NULL, 0, // Owner
3238 (SQLTCHAR *) TableName.c_str(), SQL_NTS,
3239 NULL, 0); // All columns
3240 }
3241 if (retcode != SQL_SUCCESS)
3242 { // Error occured, abort
3243 DispAllErrors(henv, hdbc, hstmt);
3244 SQLFreeStmt(hstmt, SQL_CLOSE);
3245 return(-1);
3246 }
3247
3248 // Count the columns
3249 while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
3250 noCols++;
3251
3252 if (retcode != SQL_NO_DATA_FOUND)
3253 { // Error occured, abort
3254 DispAllErrors(henv, hdbc, hstmt);
3255 SQLFreeStmt(hstmt, SQL_CLOSE);
3256 return(-1);
3257 }
3258
3259 SQLFreeStmt(hstmt, SQL_CLOSE);
3260 return noCols;
3261
3262 } // wxDb::GetColumnCount()
3263
3264
3265 /********** wxDb::GetCatalog() *******/
3266 wxDbInf *wxDb::GetCatalog(const wxChar *userID)
3267 /*
3268 * ---------------------------------------------------------------------
3269 * -- 19991203 : mj10777 : Create ------
3270 * -- : Creates a wxDbInf with Tables / Cols Array ------
3271 * -- : uses SQLTables and fills pTableInf; ------
3272 * -- : pColInf is set to NULL and numCols to 0; ------
3273 * -- : returns pDbInf (wxDbInf) ------
3274 * -- - if unsuccesfull (pDbInf == NULL) ------
3275 * -- : pColInf can be filled with GetColumns(..); ------
3276 * -- : numCols can be filled with GetColumnCount(..); ------
3277 * ---------------------------------------------------------------------
3278 *
3279 * userID is evaluated in the following manner:
3280 * userID == NULL ... UserID is ignored
3281 * userID == "" ... UserID set equal to 'this->uid'
3282 * userID != "" ... UserID set equal to 'userID'
3283 *
3284 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3285 * by this function. This function should use its own wxDb instance
3286 * to avoid undesired unbinding of columns.
3287 */
3288 {
3289 int noTab = 0; // Counter while filling table entries
3290 int pass;
3291 RETCODE retcode;
3292 SDWORD cb;
3293 wxString tblNameSave;
3294
3295 wxString UserID;
3296 convertUserID(userID,UserID);
3297
3298 //-------------------------------------------------------------
3299 // Create the Database Array of catalog entries
3300
3301 wxDbInf *pDbInf = new wxDbInf;
3302
3303 //-------------------------------------------------------------
3304 // Table Information
3305 // Pass 1 - Determine how many Tables there are.
3306 // Pass 2 - Create the Table array and fill it
3307 // - Create the Cols array = NULL
3308 //-------------------------------------------------------------
3309
3310 for (pass = 1; pass <= 2; pass++)
3311 {
3312 SQLFreeStmt(hstmt, SQL_CLOSE); // Close if Open
3313 tblNameSave.Empty();
3314
3315 if (!UserID.empty() &&
3316 Dbms() != dbmsMY_SQL &&
3317 Dbms() != dbmsACCESS &&
3318 Dbms() != dbmsMS_SQL_SERVER)
3319 {
3320 retcode = SQLTables(hstmt,
3321 NULL, 0, // All qualifiers
3322 (SQLTCHAR *) UserID.c_str(), SQL_NTS, // User specified
3323 NULL, 0, // All tables
3324 NULL, 0); // All columns
3325 }
3326 else
3327 {
3328 retcode = SQLTables(hstmt,
3329 NULL, 0, // All qualifiers
3330 NULL, 0, // User specified
3331 NULL, 0, // All tables
3332 NULL, 0); // All columns
3333 }
3334
3335 if (retcode != SQL_SUCCESS)
3336 {
3337 DispAllErrors(henv, hdbc, hstmt);
3338 pDbInf = NULL;
3339 SQLFreeStmt(hstmt, SQL_CLOSE);
3340 return pDbInf;
3341 }
3342
3343 while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS) // Table Information
3344 {
3345 if (pass == 1) // First pass, just count the Tables
3346 {
3347 if (pDbInf->numTables == 0)
3348 {
3349 GetData( 1, SQL_C_WXCHAR, (UCHAR*) pDbInf->catalog, 128+1, &cb);
3350 GetData( 2, SQL_C_WXCHAR, (UCHAR*) pDbInf->schema, 128+1, &cb);
3351 }
3352 pDbInf->numTables++; // Counter for Tables
3353 } // if (pass == 1)
3354 if (pass == 2) // Create and fill the Table entries
3355 {
3356 if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3357 { // no, then create the Array
3358 pDbInf->pTableInf = new wxDbTableInf[pDbInf->numTables];
3359 noTab = 0;
3360 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
3361
3362 GetData( 3, SQL_C_WXCHAR, (UCHAR*) (pDbInf->pTableInf+noTab)->tableName, DB_MAX_TABLE_NAME_LEN+1, &cb);
3363 GetData( 4, SQL_C_WXCHAR, (UCHAR*) (pDbInf->pTableInf+noTab)->tableType, 30+1, &cb);
3364 GetData( 5, SQL_C_WXCHAR, (UCHAR*) (pDbInf->pTableInf+noTab)->tableRemarks, 254+1, &cb);
3365
3366 noTab++;
3367 } // if
3368 } // while
3369 } // for
3370 SQLFreeStmt(hstmt, SQL_CLOSE);
3371
3372 // Query how many columns are in each table
3373 for (noTab=0;noTab<pDbInf->numTables;noTab++)
3374 {
3375 (pDbInf->pTableInf+noTab)->numCols = (UWORD)GetColumnCount((pDbInf->pTableInf+noTab)->tableName,UserID);
3376 }
3377
3378 return pDbInf;
3379
3380 } // wxDb::GetCatalog()
3381
3382
3383 /********** wxDb::Catalog() **********/
3384 bool wxDb::Catalog(const wxChar *userID, const wxString &fileName)
3385 /*
3386 * Creates the text file specified in 'filename' which will contain
3387 * a minimal data dictionary of all tables accessible by the user specified
3388 * in 'userID'
3389 *
3390 * userID is evaluated in the following manner:
3391 * userID == NULL ... UserID is ignored
3392 * userID == "" ... UserID set equal to 'this->uid'
3393 * userID != "" ... UserID set equal to 'userID'
3394 *
3395 * NOTE: ALL column bindings associated with this wxDb instance are unbound
3396 * by this function. This function should use its own wxDb instance
3397 * to avoid undesired unbinding of columns.
3398 */
3399 {
3400 wxASSERT(fileName.Length());
3401
3402 RETCODE retcode;
3403 SDWORD cb;
3404 wxChar tblName[DB_MAX_TABLE_NAME_LEN+1];
3405 wxString tblNameSave;
3406 wxChar colName[DB_MAX_COLUMN_NAME_LEN+1];
3407 SWORD sqlDataType;
3408 wxChar typeName[30+1];
3409 SDWORD precision, length;
3410
3411 FILE *fp = wxFopen(fileName.fn_str(),wxT("wt"));
3412 if (fp == NULL)
3413 return false;
3414
3415 SQLFreeStmt(hstmt, SQL_CLOSE);
3416
3417 wxString UserID;
3418 convertUserID(userID,UserID);
3419
3420 if (!UserID.empty() &&
3421 Dbms() != dbmsMY_SQL &&
3422 Dbms() != dbmsACCESS &&
3423 Dbms() != dbmsFIREBIRD &&
3424 Dbms() != dbmsINTERBASE &&
3425 Dbms() != dbmsMS_SQL_SERVER)
3426 {
3427 retcode = SQLColumns(hstmt,
3428 NULL, 0, // All qualifiers
3429 (SQLTCHAR *) UserID.c_str(), SQL_NTS, // User specified
3430 NULL, 0, // All tables
3431 NULL, 0); // All columns
3432 }
3433 else
3434 {
3435 retcode = SQLColumns(hstmt,
3436 NULL, 0, // All qualifiers
3437 NULL, 0, // User specified
3438 NULL, 0, // All tables
3439 NULL, 0); // All columns
3440 }
3441 if (retcode != SQL_SUCCESS)
3442 {
3443 DispAllErrors(henv, hdbc, hstmt);
3444 fclose(fp);
3445 return false;
3446 }
3447
3448 wxString outStr;
3449 tblNameSave.Empty();
3450 int cnt = 0;
3451
3452 while (true)
3453 {
3454 retcode = SQLFetch(hstmt);
3455 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
3456 break;
3457
3458 GetData(3,SQL_C_WXCHAR, (UCHAR *) tblName, DB_MAX_TABLE_NAME_LEN+1, &cb);
3459 GetData(4,SQL_C_WXCHAR, (UCHAR *) colName, DB_MAX_COLUMN_NAME_LEN+1,&cb);
3460 GetData(5,SQL_C_SSHORT, (UCHAR *)&sqlDataType, 0, &cb);
3461 GetData(6,SQL_C_WXCHAR, (UCHAR *) typeName, sizeof(typeName), &cb);
3462 GetData(7,SQL_C_SLONG, (UCHAR *)&precision, 0, &cb);
3463 GetData(8,SQL_C_SLONG, (UCHAR *)&length, 0, &cb);
3464
3465 if (wxStrcmp(tblName, tblNameSave.c_str()))
3466 {
3467 if (cnt)
3468 wxFputs(wxT("\n"), fp);
3469 wxFputs(wxT("================================ "), fp);
3470 wxFputs(wxT("================================ "), fp);
3471 wxFputs(wxT("===================== "), fp);
3472 wxFputs(wxT("========= "), fp);
3473 wxFputs(wxT("=========\n"), fp);
3474 outStr.Printf(wxT("%-32s %-32s %-21s %9s %9s\n"),
3475 wxT("TABLE NAME"), wxT("COLUMN NAME"), wxT("DATA TYPE"), wxT("PRECISION"), wxT("LENGTH"));
3476 wxFputs(outStr.c_str(), fp);
3477 wxFputs(wxT("================================ "), fp);
3478 wxFputs(wxT("================================ "), fp);
3479 wxFputs(wxT("===================== "), fp);
3480 wxFputs(wxT("========= "), fp);
3481 wxFputs(wxT("=========\n"), fp);
3482 tblNameSave = tblName;
3483 }
3484
3485 outStr.Printf(wxT("%-32s %-32s (%04d)%-15s %9ld %9ld\n"),
3486 tblName, colName, sqlDataType, typeName, precision, length);
3487 if (wxFputs(outStr.c_str(), fp) == EOF)
3488 {
3489 SQLFreeStmt(hstmt, SQL_CLOSE);
3490 fclose(fp);
3491 return false;
3492 }
3493 cnt++;
3494 }
3495
3496 if (retcode != SQL_NO_DATA_FOUND)
3497 DispAllErrors(henv, hdbc, hstmt);
3498
3499 SQLFreeStmt(hstmt, SQL_CLOSE);
3500
3501 fclose(fp);
3502 return(retcode == SQL_NO_DATA_FOUND);
3503
3504 } // wxDb::Catalog()
3505
3506
3507 bool wxDb::TableExists(const wxString &tableName, const wxChar *userID, const wxString &tablePath)
3508 /*
3509 * Table name can refer to a table, view, alias or synonym. Returns true
3510 * if the object exists in the database. This function does not indicate
3511 * whether or not the user has privleges to query or perform other functions
3512 * on the table.
3513 *
3514 * userID is evaluated in the following manner:
3515 * userID == NULL ... UserID is ignored
3516 * userID == "" ... UserID set equal to 'this->uid'
3517 * userID != "" ... UserID set equal to 'userID'
3518 */
3519 {
3520 wxASSERT(tableName.Length());
3521
3522 wxString TableName;
3523
3524 if (Dbms() == dbmsDBASE)
3525 {
3526 wxString dbName;
3527 if (tablePath.Length())
3528 dbName.Printf(wxT("%s/%s.dbf"), tablePath.c_str(), tableName.c_str());
3529 else
3530 dbName.Printf(wxT("%s.dbf"), tableName.c_str());
3531
3532 bool exists;
3533 exists = wxFileExists(dbName);
3534 return exists;
3535 }
3536
3537 wxString UserID;
3538 convertUserID(userID,UserID);
3539
3540 TableName = tableName;
3541 // Oracle and Interbase table names are uppercase only, so force
3542 // the name to uppercase just in case programmer forgot to do this
3543 if ((Dbms() == dbmsORACLE) ||
3544 (Dbms() == dbmsFIREBIRD) ||
3545 (Dbms() == dbmsINTERBASE))
3546 TableName = TableName.Upper();
3547
3548 SQLFreeStmt(hstmt, SQL_CLOSE);
3549 RETCODE retcode;
3550
3551 // Some databases cannot accept a user name when looking up table names,
3552 // so we use the call below that leaves out the user name
3553 if (!UserID.empty() &&
3554 Dbms() != dbmsMY_SQL &&
3555 Dbms() != dbmsACCESS &&
3556 Dbms() != dbmsMS_SQL_SERVER &&
3557 Dbms() != dbmsDB2 &&
3558 Dbms() != dbmsFIREBIRD &&
3559 Dbms() != dbmsINTERBASE &&
3560 Dbms() != dbmsPERVASIVE_SQL)
3561 {
3562 retcode = SQLTables(hstmt,
3563 NULL, 0, // All qualifiers
3564 (SQLTCHAR *) UserID.c_str(), SQL_NTS, // Only tables owned by this user
3565 (SQLTCHAR FAR *)TableName.c_str(), SQL_NTS,
3566 NULL, 0); // All table types
3567 }
3568 else
3569 {
3570 retcode = SQLTables(hstmt,
3571 NULL, 0, // All qualifiers
3572 NULL, 0, // All owners
3573 (SQLTCHAR FAR *)TableName.c_str(), SQL_NTS,
3574 NULL, 0); // All table types
3575 }
3576 if (retcode != SQL_SUCCESS)
3577 return(DispAllErrors(henv, hdbc, hstmt));
3578
3579 retcode = SQLFetch(hstmt);
3580 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
3581 {
3582 SQLFreeStmt(hstmt, SQL_CLOSE);
3583 return(DispAllErrors(henv, hdbc, hstmt));
3584 }
3585
3586 SQLFreeStmt(hstmt, SQL_CLOSE);
3587
3588 return true;
3589
3590 } // wxDb::TableExists()
3591
3592
3593 /********** wxDb::TablePrivileges() **********/
3594 bool wxDb::TablePrivileges(const wxString &tableName, const wxString &priv, const wxChar *userID,
3595 const wxChar *schema, const wxString &WXUNUSED(tablePath))
3596 {
3597 wxASSERT(tableName.Length());
3598
3599 wxDbTablePrivilegeInfo result;
3600 SDWORD cbRetVal;
3601 RETCODE retcode;
3602
3603 // We probably need to be able to dynamically set this based on
3604 // the driver type, and state.
3605 wxChar curRole[]=wxT("public");
3606
3607 wxString TableName;
3608
3609 wxString UserID,Schema;
3610 convertUserID(userID,UserID);
3611 convertUserID(schema,Schema);
3612
3613 TableName = tableName;
3614 // Oracle and Interbase table names are uppercase only, so force
3615 // the name to uppercase just in case programmer forgot to do this
3616 if ((Dbms() == dbmsORACLE) ||
3617 (Dbms() == dbmsFIREBIRD) ||
3618 (Dbms() == dbmsINTERBASE))
3619 TableName = TableName.Upper();
3620
3621 SQLFreeStmt(hstmt, SQL_CLOSE);
3622
3623 // Some databases cannot accept a user name when looking up table names,
3624 // so we use the call below that leaves out the user name
3625 if (!Schema.empty() &&
3626 Dbms() != dbmsMY_SQL &&
3627 Dbms() != dbmsACCESS &&
3628 Dbms() != dbmsMS_SQL_SERVER)
3629 {
3630 retcode = SQLTablePrivileges(hstmt,
3631 NULL, 0, // Catalog
3632 (SQLTCHAR FAR *)Schema.c_str(), SQL_NTS, // Schema
3633 (SQLTCHAR FAR *)TableName.c_str(), SQL_NTS);
3634 }
3635 else
3636 {
3637 retcode = SQLTablePrivileges(hstmt,
3638 NULL, 0, // Catalog
3639 NULL, 0, // Schema
3640 (SQLTCHAR FAR *)TableName.c_str(), SQL_NTS);
3641 }
3642
3643 #ifdef DBDEBUG_CONSOLE
3644 wxFprintf(stderr ,wxT("SQLTablePrivileges() returned %i \n"),retcode);
3645 #endif
3646
3647 if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
3648 return (DispAllErrors(henv, hdbc, hstmt));
3649
3650 bool failed = false;
3651 retcode = SQLFetch(hstmt);
3652 while (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
3653 {
3654 if (SQLGetData(hstmt, 1, SQL_C_WXCHAR, (UCHAR*) result.tableQual, sizeof(result.tableQual), &cbRetVal) != SQL_SUCCESS)
3655 failed = true;
3656
3657 if (!failed && SQLGetData(hstmt, 2, SQL_C_WXCHAR, (UCHAR*) result.tableOwner, sizeof(result.tableOwner), &cbRetVal) != SQL_SUCCESS)
3658 failed = true;
3659
3660 if (!failed && SQLGetData(hstmt, 3, SQL_C_WXCHAR, (UCHAR*) result.tableName, sizeof(result.tableName), &cbRetVal) != SQL_SUCCESS)
3661 failed = true;
3662
3663 if (!failed && SQLGetData(hstmt, 4, SQL_C_WXCHAR, (UCHAR*) result.grantor, sizeof(result.grantor), &cbRetVal) != SQL_SUCCESS)
3664 failed = true;
3665
3666 if (!failed && SQLGetData(hstmt, 5, SQL_C_WXCHAR, (UCHAR*) result.grantee, sizeof(result.grantee), &cbRetVal) != SQL_SUCCESS)
3667 failed = true;
3668
3669 if (!failed && SQLGetData(hstmt, 6, SQL_C_WXCHAR, (UCHAR*) result.privilege, sizeof(result.privilege), &cbRetVal) != SQL_SUCCESS)
3670 failed = true;
3671
3672 if (!failed && SQLGetData(hstmt, 7, SQL_C_WXCHAR, (UCHAR*) result.grantable, sizeof(result.grantable), &cbRetVal) != SQL_SUCCESS)
3673 failed = true;
3674
3675 if (failed)
3676 {
3677 return(DispAllErrors(henv, hdbc, hstmt));
3678 }
3679 #ifdef DBDEBUG_CONSOLE
3680 wxFprintf(stderr,wxT("Scanning %s privilege on table %s.%s granted by %s to %s\n"),
3681 result.privilege,result.tableOwner,result.tableName,
3682 result.grantor, result.grantee);
3683 #endif
3684
3685 if (UserID.IsSameAs(result.tableOwner,false))
3686 {
3687 SQLFreeStmt(hstmt, SQL_CLOSE);
3688 return true;
3689 }
3690
3691 if (UserID.IsSameAs(result.grantee,false) &&
3692 !wxStrcmp(result.privilege,priv))
3693 {
3694 SQLFreeStmt(hstmt, SQL_CLOSE);
3695 return true;
3696 }
3697
3698 if (!wxStrcmp(result.grantee,curRole) &&
3699 !wxStrcmp(result.privilege,priv))
3700 {
3701 SQLFreeStmt(hstmt, SQL_CLOSE);
3702 return true;
3703 }
3704
3705 retcode = SQLFetch(hstmt);
3706 }
3707
3708 SQLFreeStmt(hstmt, SQL_CLOSE);
3709 return false;
3710
3711 } // wxDb::TablePrivileges
3712
3713
3714 const wxString wxDb::SQLTableName(const wxChar *tableName)
3715 {
3716 wxString TableName;
3717
3718 if (Dbms() == dbmsACCESS)
3719 TableName = _T("\"");
3720 TableName += tableName;
3721 if (Dbms() == dbmsACCESS)
3722 TableName += _T("\"");
3723
3724 return TableName;
3725 } // wxDb::SQLTableName()
3726
3727
3728 const wxString wxDb::SQLColumnName(const wxChar *colName)
3729 {
3730 wxString ColName;
3731
3732 if (Dbms() == dbmsACCESS)
3733 ColName = _T("\"");
3734 ColName += colName;
3735 if (Dbms() == dbmsACCESS)
3736 ColName += _T("\"");
3737
3738 return ColName;
3739 } // wxDb::SQLColumnName()
3740
3741
3742 /********** wxDb::SetSqlLogging() **********/
3743 bool wxDb::SetSqlLogging(wxDbSqlLogState state, const wxString &filename, bool append)
3744 {
3745 wxASSERT(state == sqlLogON || state == sqlLogOFF);
3746 wxASSERT(state == sqlLogOFF || filename.Length());
3747
3748 if (state == sqlLogON)
3749 {
3750 if (fpSqlLog == 0)
3751 {
3752 fpSqlLog = wxFopen(filename.fn_str(), (append ? wxT("at") : wxT("wt")));
3753 if (fpSqlLog == NULL)
3754 return false;
3755 }
3756 }
3757 else // sqlLogOFF
3758 {
3759 if (fpSqlLog)
3760 {
3761 if (fclose(fpSqlLog))
3762 return false;
3763 fpSqlLog = 0;
3764 }
3765 }
3766
3767 sqlLogState = state;
3768 return true;
3769
3770 } // wxDb::SetSqlLogging()
3771
3772
3773 /********** wxDb::WriteSqlLog() **********/
3774 bool wxDb::WriteSqlLog(const wxString &logMsg)
3775 {
3776 wxASSERT(logMsg.Length());
3777
3778 if (fpSqlLog == 0 || sqlLogState == sqlLogOFF)
3779 return false;
3780
3781 if (wxFputs(wxT("\n"), fpSqlLog) == EOF)
3782 return false;
3783 if (wxFputs(logMsg, fpSqlLog) == EOF)
3784 return false;
3785 if (wxFputs(wxT("\n"), fpSqlLog) == EOF)
3786 return false;
3787
3788 return true;
3789
3790 } // wxDb::WriteSqlLog()
3791
3792
3793 /********** wxDb::Dbms() **********/
3794 wxDBMS wxDb::Dbms(void)
3795 /*
3796 * Be aware that not all database engines use the exact same syntax, and not
3797 * every ODBC compliant database is compliant to the same level of compliancy.
3798 * Some manufacturers support the minimum Level 1 compliancy, and others up
3799 * through Level 3. Others support subsets of features for levels above 1.
3800 *
3801 * If you find an inconsistency between the wxDb class and a specific database
3802 * engine, and an identifier to this section, and special handle the database in
3803 * the area where behavior is non-conforming with the other databases.
3804 *
3805 *
3806 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
3807 * ---------------------------------------------------
3808 *
3809 * ORACLE
3810 * - Currently the only database supported by the class to support VIEWS
3811 *
3812 * DBASE
3813 * - Does not support the SQL_TIMESTAMP structure
3814 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
3815 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
3816 * is true. The user must create ALL indexes from their program.
3817 * - Table names can only be 8 characters long
3818 * - Column names can only be 10 characters long
3819 *
3820 * SYBASE (all)
3821 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
3822 * after every table name involved in the query/join if that tables matching record(s)
3823 * are to be locked
3824 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
3825 *
3826 * SYBASE (Enterprise)
3827 * - If a column is part of the Primary Key, the column cannot be NULL
3828 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
3829 *
3830 * MY_SQL
3831 * - If a column is part of the Primary Key, the column cannot be NULL
3832 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
3833 * - Columns that are part of primary or secondary keys must be defined as being NOT NULL
3834 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3835 * column definition if it is not defined correctly, but it is experimental
3836 * - Does not support sub-queries in SQL statements
3837 *
3838 * POSTGRES
3839 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3840 * - Does not support sub-queries in SQL statements
3841 *
3842 * DB2
3843 * - Primary keys must be declared as NOT NULL
3844 * - Table and index names must not be longer than 13 characters in length (technically
3845 * table names can be up to 18 characters, but the primary index is created using the
3846 * base table name plus "_PIDX", so the limit if the table has a primary index is 13.
3847 *
3848 * PERVASIVE SQL
3849 *
3850 * INTERBASE
3851 * - Columns that are part of primary keys must be defined as being NOT NULL
3852 * when they are created. Some code is added in ::CreateIndex to try to adjust the
3853 * column definition if it is not defined correctly, but it is experimental
3854 */
3855 {
3856 // Should only need to do this once for each new database connection
3857 // so return the value we already determined it to be to save time
3858 // and lots of string comparisons
3859 if (dbmsType != dbmsUNIDENTIFIED)
3860 return(dbmsType);
3861
3862 #ifdef DBDEBUG_CONSOLE
3863 // When run in console mode, use standard out to display errors.
3864 cout << "Database connecting to: " << dbInf.dbmsName << endl;
3865 #endif // DBDEBUG_CONSOLE
3866
3867 wxLogDebug(wxT("Database connecting to: "));
3868 wxLogDebug(dbInf.dbmsName);
3869
3870 wxChar baseName[25+1];
3871 wxStrncpy(baseName, dbInf.dbmsName, 25);
3872 baseName[25] = 0;
3873
3874 // RGG 20001025 : add support for Interbase
3875 // GT : Integrated to base classes on 20001121
3876 if (!wxStricmp(dbInf.dbmsName,wxT("Interbase")))
3877 return((wxDBMS)(dbmsType = dbmsINTERBASE));
3878
3879 // BJO 20000428 : add support for Virtuoso
3880 if (!wxStricmp(dbInf.dbmsName,wxT("OpenLink Virtuoso VDBMS")))
3881 return((wxDBMS)(dbmsType = dbmsVIRTUOSO));
3882
3883 if (!wxStricmp(dbInf.dbmsName,wxT("Adaptive Server Anywhere")))
3884 return((wxDBMS)(dbmsType = dbmsSYBASE_ASA));
3885
3886 // BJO 20000427 : The "SQL Server" string is also returned by SQLServer when
3887 // connected through an OpenLink driver.
3888 // Is it also returned by Sybase Adapatitve server?
3889 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
3890 if (!wxStricmp(dbInf.dbmsName,wxT("SQL Server")))
3891 {
3892 if (!wxStrncmp(dbInf.driverName, wxT("oplodbc"), 7) ||
3893 !wxStrncmp(dbInf.driverName, wxT("OLOD"), 4))
3894 return ((wxDBMS)(dbmsMS_SQL_SERVER));
3895 else
3896 return ((wxDBMS)(dbmsType = dbmsSYBASE_ASE));
3897 }
3898
3899 if (!wxStricmp(dbInf.dbmsName,wxT("Microsoft SQL Server")))
3900 return((wxDBMS)(dbmsType = dbmsMS_SQL_SERVER));
3901
3902 baseName[10] = 0;
3903 if (!wxStricmp(baseName,wxT("PostgreSQL"))) // v6.5.0
3904 return((wxDBMS)(dbmsType = dbmsPOSTGRES));
3905
3906 baseName[9] = 0;
3907 if (!wxStricmp(baseName,wxT("Pervasive")))
3908 return((wxDBMS)(dbmsType = dbmsPERVASIVE_SQL));
3909
3910 baseName[8] = 0;
3911 if (!wxStricmp(baseName,wxT("Informix")))
3912 return((wxDBMS)(dbmsType = dbmsINFORMIX));
3913
3914 if (!wxStricmp(baseName,wxT("Firebird")))
3915 return((wxDBMS)(dbmsType = dbmsFIREBIRD));
3916
3917 baseName[6] = 0;
3918 if (!wxStricmp(baseName,wxT("Oracle")))
3919 return((wxDBMS)(dbmsType = dbmsORACLE));
3920 if (!wxStricmp(baseName,wxT("ACCESS")))
3921 return((wxDBMS)(dbmsType = dbmsACCESS));
3922 if (!wxStricmp(baseName,wxT("Sybase")))
3923 return((wxDBMS)(dbmsType = dbmsSYBASE_ASE));
3924
3925 baseName[5] = 0;
3926 if (!wxStricmp(baseName,wxT("DBASE")))
3927 return((wxDBMS)(dbmsType = dbmsDBASE));
3928 if (!wxStricmp(baseName,wxT("xBase")))
3929 return((wxDBMS)(dbmsType = dbmsXBASE_SEQUITER));
3930 if (!wxStricmp(baseName,wxT("MySQL")))
3931 return((wxDBMS)(dbmsType = dbmsMY_SQL));
3932
3933 baseName[3] = 0;
3934 if (!wxStricmp(baseName,wxT("DB2")))
3935 return((wxDBMS)(dbmsType = dbmsDBASE));
3936
3937 return((wxDBMS)(dbmsType = dbmsUNIDENTIFIED));
3938
3939 } // wxDb::Dbms()
3940
3941
3942 bool wxDb::ModifyColumn(const wxString &tableName, const wxString &columnName,
3943 int dataType, ULONG columnLength,
3944 const wxString &optionalParam)
3945 {
3946 wxASSERT(tableName.Length());
3947 wxASSERT(columnName.Length());
3948 wxASSERT((dataType == DB_DATA_TYPE_VARCHAR && columnLength > 0) ||
3949 dataType != DB_DATA_TYPE_VARCHAR);
3950
3951 // Must specify a columnLength if modifying a VARCHAR type column
3952 if (dataType == DB_DATA_TYPE_VARCHAR && !columnLength)
3953 return false;
3954
3955 wxString dataTypeName;
3956 wxString sqlStmt;
3957 wxString alterSlashModify;
3958
3959 switch(dataType)
3960 {
3961 case DB_DATA_TYPE_VARCHAR :
3962 dataTypeName = typeInfVarchar.TypeName;
3963 break;
3964 case DB_DATA_TYPE_INTEGER :
3965 dataTypeName = typeInfInteger.TypeName;
3966 break;
3967 case DB_DATA_TYPE_FLOAT :
3968 dataTypeName = typeInfFloat.TypeName;
3969 break;
3970 case DB_DATA_TYPE_DATE :
3971 dataTypeName = typeInfDate.TypeName;
3972 break;
3973 case DB_DATA_TYPE_BLOB :
3974 dataTypeName = typeInfBlob.TypeName;
3975 break;
3976 default:
3977 return false;
3978 }
3979
3980 // Set the modify or alter syntax depending on the type of database connected to
3981 switch (Dbms())
3982 {
3983 case dbmsORACLE :
3984 alterSlashModify = _T("MODIFY");
3985 break;
3986 case dbmsMS_SQL_SERVER :
3987 alterSlashModify = _T("ALTER COLUMN");
3988 break;
3989 case dbmsUNIDENTIFIED :
3990 return false;
3991 case dbmsSYBASE_ASA :
3992 case dbmsSYBASE_ASE :
3993 case dbmsMY_SQL :
3994 case dbmsPOSTGRES :
3995 case dbmsACCESS :
3996 case dbmsDBASE :
3997 case dbmsXBASE_SEQUITER :
3998 default :
3999 alterSlashModify = _T("MODIFY");
4000 break;
4001 }
4002
4003 // create the SQL statement
4004 if ( Dbms() == dbmsMY_SQL )
4005 {
4006 sqlStmt.Printf(wxT("ALTER TABLE %s %s %s %s"), tableName.c_str(), alterSlashModify.c_str(),
4007 columnName.c_str(), dataTypeName.c_str());
4008 }
4009 else
4010 {
4011 sqlStmt.Printf(wxT("ALTER TABLE \"%s\" \"%s\" \"%s\" %s"), tableName.c_str(), alterSlashModify.c_str(),
4012 columnName.c_str(), dataTypeName.c_str());
4013 }
4014
4015 // For varchars only, append the size of the column
4016 if (dataType == DB_DATA_TYPE_VARCHAR &&
4017 (Dbms() != dbmsMY_SQL || dataTypeName != _T("text")))
4018 {
4019 wxString s;
4020 s.Printf(wxT("(%lu)"), columnLength);
4021 sqlStmt += s;
4022 }
4023
4024 // for passing things like "NOT NULL"
4025 if (optionalParam.Length())
4026 {
4027 sqlStmt += wxT(" ");
4028 sqlStmt += optionalParam;
4029 }
4030
4031 return ExecSql(sqlStmt);
4032
4033 } // wxDb::ModifyColumn()
4034
4035
4036 /********** wxDbGetConnection() **********/
4037 wxDb WXDLLIMPEXP_ODBC *wxDbGetConnection(wxDbConnectInf *pDbConfig, bool FwdOnlyCursors)
4038 {
4039 wxDbList *pList;
4040
4041 // Used to keep a pointer to a DB connection that matches the requested
4042 // DSN and FwdOnlyCursors settings, even if it is not FREE, so that the
4043 // data types can be copied from it (using the wxDb::Open(wxDb *) function)
4044 // rather than having to re-query the datasource to get all the values
4045 // using the wxDb::Open(Dsn,Uid,AuthStr) function
4046 wxDb *matchingDbConnection = NULL;
4047
4048 // Scan the linked list searching for an available database connection
4049 // that's already been opened but is currently not in use.
4050 for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
4051 {
4052 // The database connection must be for the same datasource
4053 // name and must currently not be in use.
4054 if (pList->Free &&
4055 (pList->PtrDb->FwdOnlyCursors() == FwdOnlyCursors))
4056 {
4057 if (pDbConfig->UseConnectionStr())
4058 {
4059 if (pList->PtrDb->OpenedWithConnectionString() &&
4060 (!wxStrcmp(pDbConfig->GetConnectionStr(), pList->ConnectionStr)))
4061 {
4062 // Found a free connection
4063 pList->Free = false;
4064 return(pList->PtrDb);
4065 }
4066 }
4067 else
4068 {
4069 if (!pList->PtrDb->OpenedWithConnectionString() &&
4070 (!wxStrcmp(pDbConfig->GetDsn(), pList->Dsn)))
4071 {
4072 // Found a free connection
4073 pList->Free = false;
4074 return(pList->PtrDb);
4075 }
4076 }
4077 }
4078
4079 if (pDbConfig->UseConnectionStr())
4080 {
4081 if (!wxStrcmp(pDbConfig->GetConnectionStr(), pList->ConnectionStr))
4082 matchingDbConnection = pList->PtrDb;
4083 }
4084 else
4085 {
4086 if (!wxStrcmp(pDbConfig->GetDsn(), pList->Dsn) &&
4087 !wxStrcmp(pDbConfig->GetUserID(), pList->Uid) &&
4088 !wxStrcmp(pDbConfig->GetPassword(), pList->AuthStr))
4089 matchingDbConnection = pList->PtrDb;
4090 }
4091 }
4092
4093 // No available connections. A new connection must be made and
4094 // appended to the end of the linked list.
4095 if (PtrBegDbList)
4096 {
4097 // Find the end of the list
4098 for (pList = PtrBegDbList; pList->PtrNext; pList = pList->PtrNext);
4099 // Append a new list item
4100 pList->PtrNext = new wxDbList;
4101 pList->PtrNext->PtrPrev = pList;
4102 pList = pList->PtrNext;
4103 }
4104 else // Empty list
4105 {
4106 // Create the first node on the list
4107 pList = PtrBegDbList = new wxDbList;
4108 pList->PtrPrev = 0;
4109 }
4110
4111 // Initialize new node in the linked list
4112 pList->PtrNext = 0;
4113 pList->Free = false;
4114 pList->Dsn = pDbConfig->GetDsn();
4115 pList->Uid = pDbConfig->GetUserID();
4116 pList->AuthStr = pDbConfig->GetPassword();
4117 pList->ConnectionStr = pDbConfig->GetConnectionStr();
4118
4119 pList->PtrDb = new wxDb(pDbConfig->GetHenv(), FwdOnlyCursors);
4120
4121 bool opened;
4122
4123 if (!matchingDbConnection)
4124 {
4125 if (pDbConfig->UseConnectionStr())
4126 {
4127 opened = pList->PtrDb->Open(pDbConfig->GetConnectionStr());
4128 }
4129 else
4130 {
4131 opened = pList->PtrDb->Open(pDbConfig->GetDsn(), pDbConfig->GetUserID(), pDbConfig->GetPassword());
4132 }
4133 }
4134 else
4135 opened = pList->PtrDb->Open(matchingDbConnection);
4136
4137 // Connect to the datasource
4138 if (opened)
4139 {
4140 pList->PtrDb->setCached(true); // Prevent a user from deleting a cached connection
4141 pList->PtrDb->SetSqlLogging(SQLLOGstate, SQLLOGfn, true);
4142 return(pList->PtrDb);
4143 }
4144 else // Unable to connect, destroy list item
4145 {
4146 if (pList->PtrPrev)
4147 pList->PtrPrev->PtrNext = 0;
4148 else
4149 PtrBegDbList = 0; // Empty list again
4150
4151 pList->PtrDb->CommitTrans(); // Commit any open transactions on wxDb object
4152 pList->PtrDb->Close(); // Close the wxDb object
4153 delete pList->PtrDb; // Deletes the wxDb object
4154 delete pList; // Deletes the linked list object
4155 return(0);
4156 }
4157
4158 } // wxDbGetConnection()
4159
4160
4161 /********** wxDbFreeConnection() **********/
4162 bool WXDLLIMPEXP_ODBC wxDbFreeConnection(wxDb *pDb)
4163 {
4164 wxDbList *pList;
4165
4166 // Scan the linked list searching for the database connection
4167 for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
4168 {
4169 if (pList->PtrDb == pDb) // Found it, now free it!!!
4170 return (pList->Free = true);
4171 }
4172
4173 // Never found the database object, return failure
4174 return false;
4175
4176 } // wxDbFreeConnection()
4177
4178
4179 /********** wxDbCloseConnections() **********/
4180 void WXDLLIMPEXP_ODBC wxDbCloseConnections(void)
4181 {
4182 wxDbList *pList, *pNext;
4183
4184 // Traverse the linked list closing database connections and freeing memory as I go.
4185 for (pList = PtrBegDbList; pList; pList = pNext)
4186 {
4187 pNext = pList->PtrNext; // Save the pointer to next
4188 pList->PtrDb->CommitTrans(); // Commit any open transactions on wxDb object
4189 pList->PtrDb->Close(); // Close the wxDb object
4190 pList->PtrDb->setCached(false); // Allows deletion of the wxDb instance
4191 delete pList->PtrDb; // Deletes the wxDb object
4192 delete pList; // Deletes the linked list object
4193 }
4194
4195 // Mark the list as empty
4196 PtrBegDbList = 0;
4197
4198 } // wxDbCloseConnections()
4199
4200
4201 /********** wxDbConnectionsInUse() **********/
4202 int WXDLLIMPEXP_ODBC wxDbConnectionsInUse(void)
4203 {
4204 wxDbList *pList;
4205 int cnt = 0;
4206
4207 // Scan the linked list counting db connections that are currently in use
4208 for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
4209 {
4210 if (pList->Free == false)
4211 cnt++;
4212 }
4213
4214 return(cnt);
4215
4216 } // wxDbConnectionsInUse()
4217
4218
4219
4220 /********** wxDbLogExtendedErrorMsg() **********/
4221 // DEBUG ONLY function
4222 const wxChar WXDLLIMPEXP_ODBC *wxDbLogExtendedErrorMsg(const wxChar *userText,
4223 wxDb *pDb,
4224 const wxChar *ErrFile,
4225 int ErrLine)
4226 {
4227 static wxString msg;
4228 msg = userText;
4229
4230 wxString tStr;
4231
4232 if (ErrFile || ErrLine)
4233 {
4234 msg += wxT("File: ");
4235 msg += ErrFile;
4236 msg += wxT(" Line: ");
4237 tStr.Printf(wxT("%d"),ErrLine);
4238 msg += tStr.c_str();
4239 msg += wxT("\n");
4240 }
4241
4242 msg.Append (wxT("\nODBC errors:\n"));
4243 msg += wxT("\n");
4244
4245 // Display errors for this connection
4246 int i;
4247 for (i = 0; i < DB_MAX_ERROR_HISTORY; i++)
4248 {
4249 if (pDb->errorList[i])
4250 {
4251 msg.Append(pDb->errorList[i]);
4252 if (wxStrcmp(pDb->errorList[i], wxEmptyString) != 0)
4253 msg.Append(wxT("\n"));
4254 // Clear the errmsg buffer so the next error will not
4255 // end up showing the previous error that have occurred
4256 wxStrcpy(pDb->errorList[i], wxEmptyString);
4257 }
4258 }
4259 msg += wxT("\n");
4260
4261 wxLogDebug(msg.c_str());
4262
4263 return msg.c_str();
4264 } // wxDbLogExtendedErrorMsg()
4265
4266
4267 /********** wxDbSqlLog() **********/
4268 bool wxDbSqlLog(wxDbSqlLogState state, const wxChar *filename)
4269 {
4270 bool append = false;
4271 wxDbList *pList;
4272
4273 for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
4274 {
4275 if (!pList->PtrDb->SetSqlLogging(state,filename,append))
4276 return false;
4277 append = true;
4278 }
4279
4280 SQLLOGstate = state;
4281 SQLLOGfn = filename;
4282
4283 return true;
4284
4285 } // wxDbSqlLog()
4286
4287
4288 #if 0
4289 /********** wxDbCreateDataSource() **********/
4290 int wxDbCreateDataSource(const wxString &driverName, const wxString &dsn, const wxString &description,
4291 bool sysDSN, const wxString &defDir, wxWindow *parent)
4292 /*
4293 * !!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4294 * Very rudimentary creation of an ODBC data source.
4295 *
4296 * ODBC driver must be ODBC 3.0 compliant to use this function
4297 */
4298 {
4299 int result = FALSE;
4300
4301 //!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
4302 #ifdef __VISUALC__
4303 int dsnLocation;
4304 wxString setupStr;
4305
4306 if (sysDSN)
4307 dsnLocation = ODBC_ADD_SYS_DSN;
4308 else
4309 dsnLocation = ODBC_ADD_DSN;
4310
4311 // NOTE: The decimal 2 is an invalid character in all keyword pairs
4312 // so that is why I used it, as wxString does not deal well with
4313 // embedded nulls in strings
4314 setupStr.Printf(wxT("DSN=%s%cDescription=%s%cDefaultDir=%s%c"),dsn,2,description,2,defDir,2);
4315
4316 // Replace the separator from above with the '\0' seperator needed
4317 // by the SQLConfigDataSource() function
4318 int k;
4319 do
4320 {
4321 k = setupStr.Find((wxChar)2,true);
4322 if (k != wxNOT_FOUND)
4323 setupStr[(UINT)k] = wxT('\0');
4324 }
4325 while (k != wxNOT_FOUND);
4326
4327 result = SQLConfigDataSource((HWND)parent->GetHWND(), dsnLocation,
4328 driverName, setupStr.c_str());
4329
4330 if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
4331 {
4332 // check for errors caused by ConfigDSN based functions
4333 DWORD retcode = 0;
4334 WORD cb;
4335 wxChar errMsg[SQL_MAX_MESSAGE_LENGTH];
4336 errMsg[0] = wxT('\0');
4337
4338 // This function is only supported in ODBC drivers v3.0 compliant and above
4339 SQLInstallerError(1,&retcode,errMsg,SQL_MAX_MESSAGE_LENGTH-1,&cb);
4340 if (retcode)
4341 {
4342 #ifdef DBDEBUG_CONSOLE
4343 // When run in console mode, use standard out to display errors.
4344 cout << errMsg << endl;
4345 cout << wxT("Press any key to continue...") << endl;
4346 getchar();
4347 #endif // DBDEBUG_CONSOLE
4348
4349 #ifdef __WXDEBUG__
4350 wxLogDebug(errMsg,wxT("ODBC DEBUG MESSAGE"));
4351 #endif // __WXDEBUG__
4352 }
4353 }
4354 else
4355 result = TRUE;
4356 #else
4357 // Using iODBC/unixODBC or some other compiler which does not support the APIs
4358 // necessary to use this function, so this function is not supported
4359 #ifdef __WXDEBUG__
4360 wxLogDebug(wxT("wxDbCreateDataSource() not available except under VC++/MSW"),wxT("ODBC DEBUG MESSAGE"));
4361 #endif
4362 result = FALSE;
4363 #endif // __VISUALC__
4364
4365 return result;
4366
4367 } // wxDbCreateDataSource()
4368 #endif
4369
4370
4371 /********** wxDbGetDataSource() **********/
4372 bool wxDbGetDataSource(HENV henv, wxChar *Dsn, SWORD DsnMaxLength, wxChar *DsDesc,
4373 SWORD DsDescMaxLength, UWORD direction)
4374 /*
4375 * Dsn and DsDesc will contain the data source name and data source
4376 * description upon return
4377 */
4378 {
4379 SWORD cb1,cb2;
4380 SWORD lengthDsn = (SWORD)(DsnMaxLength*sizeof(wxChar));
4381 SWORD lengthDsDesc = (SWORD)(DsDescMaxLength*sizeof(wxChar));
4382
4383 if (SQLDataSources(henv, direction, (SQLTCHAR FAR *) Dsn, lengthDsn, &cb1,
4384 (SQLTCHAR FAR *) DsDesc, lengthDsDesc, &cb2) == SQL_SUCCESS)
4385 return true;
4386 else
4387 return false;
4388
4389 } // wxDbGetDataSource()
4390
4391
4392 // Change this to 0 to remove use of all deprecated functions
4393 #if wxODBC_BACKWARD_COMPATABILITY
4394 /********************************************************************
4395 ********************************************************************
4396 *
4397 * The following functions are all DEPRECATED and are included for
4398 * backward compatability reasons only
4399 *
4400 ********************************************************************
4401 ********************************************************************/
4402 bool SqlLog(sqlLog state, const wxChar *filename)
4403 {
4404 return wxDbSqlLog((enum wxDbSqlLogState)state, filename);
4405 }
4406 /***** DEPRECATED: use wxGetDataSource() *****/
4407 bool GetDataSource(HENV henv, char *Dsn, SWORD DsnMax, char *DsDesc, SWORD DsDescMax,
4408 UWORD direction)
4409 {
4410 return wxDbGetDataSource(henv, Dsn, DsnMax, DsDesc, DsDescMax, direction);
4411 }
4412 /***** DEPRECATED: use wxDbGetConnection() *****/
4413 wxDb WXDLLIMPEXP_ODBC *GetDbConnection(DbStuff *pDbStuff, bool FwdOnlyCursors)
4414 {
4415 return wxDbGetConnection((wxDbConnectInf *)pDbStuff, FwdOnlyCursors);
4416 }
4417 /***** DEPRECATED: use wxDbFreeConnection() *****/
4418 bool WXDLLIMPEXP_ODBC FreeDbConnection(wxDb *pDb)
4419 {
4420 return wxDbFreeConnection(pDb);
4421 }
4422 /***** DEPRECATED: use wxDbCloseConnections() *****/
4423 void WXDLLIMPEXP_ODBC CloseDbConnections(void)
4424 {
4425 wxDbCloseConnections();
4426 }
4427 /***** DEPRECATED: use wxDbConnectionsInUse() *****/
4428 int WXDLLIMPEXP_ODBC NumberDbConnectionsInUse(void)
4429 {
4430 return wxDbConnectionsInUse();
4431 }
4432 #endif
4433
4434
4435 #endif
4436 // wxUSE_ODBC