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