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