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