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