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