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