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