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