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