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