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