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