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