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