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