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