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