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