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