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