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