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