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