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