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