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