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