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