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