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