]> git.saurik.com Git - wxWidgets.git/blame - 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
108106cf
JS
1///////////////////////////////////////////////////////////////////////////////
2// Name: db.cpp
f6bcfd97
BP
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
108106cf
JS
5// source such as opening and closing the data source.
6// Author: Doug Card
67e9aaa3 7// Modified by: George Tasker
3ca6a5f0 8// Bart Jourquin
0cd121f9 9// Mark Johnson, wxWindows@mj10777.de
23f681ec 10// Mods: Dec, 1998:
a2115c88
GT
11// -Added support for SQL statement logging and database cataloging
12// Mods: April, 1999
1e92909e
GT
13// -Added QUERY_ONLY mode support to reduce default number of cursors
14// -Added additional SQL logging code
a2115c88 15// -Added DEBUG-ONLY tracking of wxTable objects to detect orphaned DB connections
1e92909e 16// -Set ODBC option to only read committed writes to the DB so all
a2115c88 17// databases operate the same in that respect
108106cf
JS
18// Created: 9.96
19// RCS-ID: $Id$
20// Copyright: (c) 1996 Remstar International, Inc.
21// Licence: wxWindows licence, plus:
1e92909e 22// Notice: This class library and its intellectual design are free of charge for use,
108106cf
JS
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
01dba85a
JS
38#include "wx/wxprec.h"
39
f6bcfd97 40
a2115c88
GT
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"
a2115c88
GT
45
46#if wxMAJOR_VERSION == 2
1e92909e 47 #ifdef __GNUG__
3ca6a5f0 48 #pragma implementation "db.h"
1e92909e 49 #endif
a2115c88
GT
50#endif
51
1fc5dd6f 52#ifdef DBDEBUG_CONSOLE
52a17fe5 53 #include "wx/ioswrap.h"
108106cf 54#endif
108106cf
JS
55
56#ifdef __BORLANDC__
1e92909e 57 #pragma hdrstop
108106cf
JS
58#endif //__BORLANDC__
59
a2115c88 60#if wxMAJOR_VERSION == 2
1e92909e
GT
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"
0efe4a98 67 #include "wx/log.h"
1e92909e
GT
68 #endif
69 #include "wx/filefn.h"
70 #include "wx/wxchar.h"
a2115c88
GT
71#endif
72
f6bcfd97 73
a2115c88 74#if wxMAJOR_VERSION == 1
1e92909e
GT
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
a2115c88 83#endif
108106cf 84
47d67540 85#if wxUSE_ODBC
108106cf 86
108106cf
JS
87#include <stdio.h>
88#include <string.h>
89#include <assert.h>
7e616b10
RR
90#include <stdlib.h>
91#include <ctype.h>
67e9aaa3 92
a2115c88 93#if wxMAJOR_VERSION == 1
1e92909e 94 #include "db.h"
a2115c88 95#elif wxMAJOR_VERSION == 2
1e92909e 96 #include "wx/db.h"
a2115c88 97#endif
108106cf 98
d50af714 99WXDLLEXPORT_DATA(wxDbList*) PtrBegDbList = 0;
108106cf 100
f6bcfd97 101
9453a61b
BJ
102char const *SQL_LOG_FILENAME = "sqllog.txt";
103char const *SQL_CATALOG_FILENAME = "catalog.txt";
104
e041ce57 105#ifdef __WXDEBUG__
1e92909e 106 extern wxList TablesInUse;
a2115c88
GT
107#endif
108
109// SQL Log defaults to be used by GetDbConnection
f6bcfd97 110wxDbSqlLogState SQLLOGstate = sqlLogOFF;
a2115c88 111
f6bcfd97 112//char SQLLOGfn[wxDB_PATH_MAX+1] = SQL_LOG_FILENAME;
3ca6a5f0
BP
113//wxChar *SQLLOGfn = (wxChar*) SQL_LOG_FILENAME;
114static wxString SQLLOGfn = SQL_LOG_FILENAME;
a2115c88 115
f6bcfd97 116// The wxDb::errorList is copied to this variable when the wxDb object
a2115c88 117// is closed. This way, the error list is still available after the
9453a61b 118// database object is closed. This is necessary if the database
a2115c88 119// connection fails so the calling application can show the operator
f6bcfd97
BP
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
3ca6a5f0
BP
122// this variable. NOTE: This occurs during a CLOSE, not a FREEing of the
123// connection
a2115c88
GT
124char DBerrorList[DB_MAX_ERROR_HISTORY][DB_MAX_ERROR_MSG_LEN];
125
3ca6a5f0
BP
126// This type defines the return row-struct form
127// SQLTablePrivileges, and is used by wxDB::TablePrivileges.
128typedef struct
129{
a8aa2258
GT
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];
3ca6a5f0 137} wxDbTablePrivilegeInfo;
3ca6a5f0 138
67e9aaa3 139
f6bcfd97
BP
140/********** wxDbColFor Constructor **********/
141wxDbColFor::wxDbColFor()
67e9aaa3 142{
3ca6a5f0 143 s_Field = "";
1e92909e
GT
144 int i;
145 for (i=0;i<7;i++)
67e9aaa3
GT
146 {
147 s_Format[i] = "";
3ca6a5f0
BP
148 s_Amount[i] = "";
149 i_Amount[i] = 0;
67e9aaa3 150 }
3ca6a5f0
BP
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
f6bcfd97 155} // wxDbColFor::wxDbColFor()
67e9aaa3
GT
156
157
f6bcfd97 158wxDbColFor::~wxDbColFor()
67e9aaa3 159{
f6bcfd97 160} // wxDbColFor::~wxDbColFor()
67e9aaa3
GT
161
162
3ca6a5f0
BP
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{
0cd121f9
MJ
228 if (pTableInf)
229 delete [] pTableInf;
230 pTableInf = NULL;
3ca6a5f0
BP
231} // wxDbInf::~wxDbInf()
232
233
234/*************************************************/
235
236
f6bcfd97 237int wxDbColFor::Format(int Nation,int dbDataType,SWORD sqlDataType,short columnSize,short decimalDigits)
67e9aaa3
GT
238{
239 // ----------------------------------------------------------------------------------------
0cd121f9 240 // -- 19991224 : mj10777 : Create
67e9aaa3
GT
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
3ca6a5f0
BP
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,
67e9aaa3
GT
247 // they should be used here.
248 // ----------------------------------------------------------------------------------------
3ca6a5f0 249 // There should also be a function to scan in a string to fill the variable
67e9aaa3
GT
250 // ----------------------------------------------------------------------------------------
251 wxString Temp0;
3ca6a5f0 252 i_Nation = Nation; // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
1e92909e 253 i_dbDataType = dbDataType;
67e9aaa3 254 i_sqlDataType = sqlDataType;
3ca6a5f0
BP
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
67e9aaa3
GT
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:
3ca6a5f0 276 s_Field = "%s";
67e9aaa3
GT
277 break;
278 case DB_DATA_TYPE_INTEGER:
3ca6a5f0 279 s_Field = "%d";
67e9aaa3
GT
280 break;
281 case DB_DATA_TYPE_FLOAT:
282 if (decimalDigits == 0)
283 decimalDigits = 2;
284 Temp0 = "%";
fbdcff4a 285 Temp0.Printf(wxT("%s%d.%d"),Temp0.c_str(),columnSize,decimalDigits);
3ca6a5f0 286 s_Field.Printf(wxT("%sf"),Temp0.c_str());
67e9aaa3
GT
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:
3ca6a5f0 311 s_Field.Printf(wxT("Unknown Format(%d)-SQL(%d)"),dbDataType,sqlDataType); //
67e9aaa3
GT
312 break;
313 };
314 return TRUE;
f6bcfd97 315} // wxDbColFor::Format()
67e9aaa3
GT
316
317
a8aa2258 318/********** wxDb Constructors **********/
f6bcfd97 319wxDb::wxDb(HENV &aHenv, bool FwdOnlyCursors)
a8aa2258
GT
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 */
108106cf 333{
1e92909e
GT
334 int i;
335
336 fpSqlLog = 0; // Sql Log file pointer
337 sqlLogState = sqlLogOFF; // By default, logging is turned off
338 nTables = 0;
a8aa2258 339 dbmsType = dbmsUNIDENTIFIED;
23f681ec 340
fbdcff4a
JS
341 wxStrcpy(sqlState,wxT(""));
342 wxStrcpy(errorMsg,wxT(""));
1e92909e
GT
343 nativeError = cbErrorMsg = 0;
344 for (i = 0; i < DB_MAX_ERROR_HISTORY; i++)
fbdcff4a 345 wxStrcpy(errorList[i], wxT(""));
1e92909e
GT
346
347 // Init typeInf structures
fbdcff4a 348 wxStrcpy(typeInfVarchar.TypeName,wxT(""));
1e92909e
GT
349 typeInfVarchar.FsqlType = 0;
350 typeInfVarchar.Precision = 0;
351 typeInfVarchar.CaseSensitive = 0;
352 typeInfVarchar.MaximumScale = 0;
353
fbdcff4a 354 wxStrcpy(typeInfInteger.TypeName,wxT(""));
1e92909e
GT
355 typeInfInteger.FsqlType = 0;
356 typeInfInteger.Precision = 0;
357 typeInfInteger.CaseSensitive = 0;
358 typeInfInteger.MaximumScale = 0;
359
fbdcff4a 360 wxStrcpy(typeInfFloat.TypeName,wxT(""));
1e92909e
GT
361 typeInfFloat.FsqlType = 0;
362 typeInfFloat.Precision = 0;
363 typeInfFloat.CaseSensitive = 0;
364 typeInfFloat.MaximumScale = 0;
365
fbdcff4a 366 wxStrcpy(typeInfDate.TypeName,wxT(""));
1e92909e
GT
367 typeInfDate.FsqlType = 0;
368 typeInfDate.Precision = 0;
369 typeInfDate.CaseSensitive = 0;
370 typeInfDate.MaximumScale = 0;
23f681ec 371
1e92909e
GT
372 // Error reporting is turned OFF by default
373 silent = TRUE;
23f681ec 374
1e92909e
GT
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;
a8aa2258 384} // wxDb::initialize()
108106cf 385
67e9aaa3 386
f6bcfd97
BP
387/********** wxDb::Open() **********/
388bool wxDb::Open(char *Dsn, char *Uid, char *AuthStr)
108106cf 389{
1e92909e
GT
390 assert(Dsn && wxStrlen(Dsn));
391 dsn = Dsn;
392 uid = Uid;
393 authStr = AuthStr;
108106cf 394
1e92909e 395 RETCODE retcode;
108106cf 396
1e92909e
GT
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);
108106cf 402
6919c53f 403#ifdef DBDEBUG_CONSOLE
1e92909e
GT
404 if (retcode == SQL_SUCCESS)
405 cout << "SQLSetConnectOption(CURSOR_LIB) successful" << endl;
406 else
407 cout << "SQLSetConnectOption(CURSOR_LIB) failed" << endl;
6919c53f 408#endif
1e92909e 409 }
108106cf 410
1e92909e 411 // Connect to the data source
a8aa2258
GT
412 retcode = SQLConnect(hdbc, (UCHAR FAR *) dsn, SQL_NTS,
413 (UCHAR FAR *) uid, SQL_NTS,
414 (UCHAR FAR *) authStr,SQL_NTS);
f6bcfd97 415
a8aa2258 416/*
1e92909e
GT
417 if (retcode == SQL_SUCCESS_WITH_INFO)
418 DispAllErrors(henv, hdbc);
419 else if (retcode != SQL_SUCCESS)
420 return(DispAllErrors(henv, hdbc));
108106cf 421
a8aa2258
GT
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
a2115c88 429/*
1e92909e
GT
430 If using Intersolv branded ODBC drivers, this is the place where you would substitute
431 your branded driver license information
a2115c88 432
1e92909e
GT
433 SQLSetConnectOption(hdbc, 1041, (UDWORD) "");
434 SQLSetConnectOption(hdbc, 1042, (UDWORD) "");
a2115c88 435*/
67e9aaa3 436
1e92909e
GT
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
a8aa2258 445 if (!setConnectionOptions())
1e92909e
GT
446 return(FALSE);
447
448 // Query the data source for inf. about itself
a8aa2258 449 if (!getDbInfo())
1e92909e
GT
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
3ca6a5f0
BP
494 if (!getDataTypeInfo(SQL_VARCHAR, typeInfVarchar))
495 if (!getDataTypeInfo(SQL_CHAR, typeInfVarchar))
1e92909e
GT
496 return(FALSE);
497 else
498 typeInfVarchar.FsqlType = SQL_CHAR;
499 else
500 typeInfVarchar.FsqlType = SQL_VARCHAR;
501
502 // Float
3ca6a5f0 503 if (!getDataTypeInfo(SQL_DOUBLE,typeInfFloat))
f6bcfd97 504
3ca6a5f0
BP
505 if (!getDataTypeInfo(SQL_REAL,typeInfFloat))
506 if (!getDataTypeInfo(SQL_FLOAT,typeInfFloat))
507 if (!getDataTypeInfo(SQL_DECIMAL,typeInfFloat))
508 if (!getDataTypeInfo(SQL_NUMERIC,typeInfFloat))
1e92909e
GT
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
3ca6a5f0
BP
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))
1e92909e
GT
527 return(FALSE);
528 else
529 typeInfInteger.FsqlType = typeInfFloat.FsqlType;
3ca6a5f0 530 }
1e92909e
GT
531 else
532 typeInfInteger.FsqlType = SQL_INTEGER;
533
534 // Date/Time
535 if (Dbms() != dbmsDBASE)
3ca6a5f0
BP
536 {
537 if (! getDataTypeInfo(SQL_TIMESTAMP,typeInfDate))
1e92909e
GT
538 return(FALSE);
539 else
540 typeInfDate.FsqlType = SQL_TIMESTAMP;
541 }
542 else
543 {
3ca6a5f0 544 if (!getDataTypeInfo(SQL_DATE,typeInfDate))
1e92909e
GT
545 return(FALSE);
546 else
547 typeInfDate.FsqlType = SQL_DATE;
548 }
108106cf 549
1fc5dd6f 550#ifdef DBDEBUG_CONSOLE
1e92909e
GT
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;
108106cf
JS
556#endif
557
1e92909e
GT
558 // Completed Successfully
559 return(TRUE);
108106cf 560
f6bcfd97 561} // wxDb::Open()
108106cf 562
108106cf 563
a8aa2258
GT
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
f6bcfd97
BP
672/********** wxDb::setConnectionOptions() **********/
673bool wxDb::setConnectionOptions(void)
67e9aaa3
GT
674/*
675 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
676 */
108106cf 677{
a8aa2258
GT
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
1e92909e
GT
685 SQLSetConnectOption(hdbc, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF);
686 SQLSetConnectOption(hdbc, SQL_OPT_TRACE, SQL_OPT_TRACE_OFF);
a8aa2258
GT
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 }
108106cf 700
1e92909e 701 // Display the connection options to verify them
1fc5dd6f 702#ifdef DBDEBUG_CONSOLE
1e92909e 703 long l;
23f681ec
VZ
704 cout << "****** CONNECTION OPTIONS ******" << endl;
705
1e92909e
GT
706 if (SQLGetConnectOption(hdbc, SQL_AUTOCOMMIT, &l) != SQL_SUCCESS)
707 return(DispAllErrors(henv, hdbc));
708 cout << "AUTOCOMMIT: " << (l == SQL_AUTOCOMMIT_OFF ? "OFF" : "ON") << endl;
23f681ec 709
1e92909e
GT
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;
23f681ec 726
1e92909e
GT
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;
108106cf
JS
732#endif
733
1e92909e
GT
734 // Completed Successfully
735 return(TRUE);
108106cf 736
f6bcfd97 737} // wxDb::setConnectionOptions()
108106cf 738
67e9aaa3 739
f6bcfd97
BP
740/********** wxDb::getDbInfo() **********/
741bool wxDb::getDbInfo(void)
108106cf 742{
1e92909e
GT
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));
108106cf 748
1e92909e
GT
749 if (SQLGetInfo(hdbc, SQL_DATABASE_NAME, (UCHAR*) dbInf.databaseName, 128, &cb) != SQL_SUCCESS)
750 return(DispAllErrors(henv, hdbc));
108106cf 751
1e92909e
GT
752 if (SQLGetInfo(hdbc, SQL_DBMS_NAME, (UCHAR*) dbInf.dbmsName, 40, &cb) != SQL_SUCCESS)
753 return(DispAllErrors(henv, hdbc));
108106cf 754
1e92909e
GT
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);
108106cf 759
1e92909e
GT
760 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
761 return(DispAllErrors(henv, hdbc));
108106cf 762
1e92909e
GT
763 if (SQLGetInfo(hdbc, SQL_ACTIVE_CONNECTIONS, (UCHAR*) &dbInf.maxConnections, sizeof(dbInf.maxConnections), &cb) != SQL_SUCCESS)
764 return(DispAllErrors(henv, hdbc));
108106cf 765
1e92909e
GT
766 if (SQLGetInfo(hdbc, SQL_ACTIVE_STATEMENTS, (UCHAR*) &dbInf.maxStmts, sizeof(dbInf.maxStmts), &cb) != SQL_SUCCESS)
767 return(DispAllErrors(henv, hdbc));
108106cf 768
1e92909e
GT
769 if (SQLGetInfo(hdbc, SQL_DRIVER_NAME, (UCHAR*) dbInf.driverName, 40, &cb) != SQL_SUCCESS)
770 return(DispAllErrors(henv, hdbc));
108106cf 771
1e92909e
GT
772 if (SQLGetInfo(hdbc, SQL_DRIVER_ODBC_VER, (UCHAR*) dbInf.odbcVer, 60, &cb) == SQL_ERROR)
773 return(DispAllErrors(henv, hdbc));
108106cf 774
1e92909e
GT
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));
108106cf 778
1e92909e
GT
779 if (SQLGetInfo(hdbc, SQL_DRIVER_VER, (UCHAR*) dbInf.driverVer, 60, &cb) == SQL_ERROR)
780 return(DispAllErrors(henv, hdbc));
108106cf 781
1e92909e
GT
782 if (SQLGetInfo(hdbc, SQL_ODBC_API_CONFORMANCE, (UCHAR*) &dbInf.apiConfLvl, sizeof(dbInf.apiConfLvl), &cb) != SQL_SUCCESS)
783 return(DispAllErrors(henv, hdbc));
108106cf 784
1e92909e 785 if (SQLGetInfo(hdbc, SQL_ODBC_SAG_CLI_CONFORMANCE, (UCHAR*) &dbInf.cliConfLvl, sizeof(dbInf.cliConfLvl), &cb) != SQL_SUCCESS)
a8aa2258
GT
786// return(DispAllErrors(henv, hdbc));
787 {
788 // Not all drivers support this call - Nick Gorham(unixODBC)
789 dbInf.cliConfLvl = 0;
790 }
108106cf 791
1e92909e
GT
792 if (SQLGetInfo(hdbc, SQL_ODBC_SQL_CONFORMANCE, (UCHAR*) &dbInf.sqlConfLvl, sizeof(dbInf.sqlConfLvl), &cb) != SQL_SUCCESS)
793 return(DispAllErrors(henv, hdbc));
108106cf 794
1e92909e
GT
795 if (SQLGetInfo(hdbc, SQL_OUTER_JOINS, (UCHAR*) dbInf.outerJoins, 2, &cb) != SQL_SUCCESS)
796 return(DispAllErrors(henv, hdbc));
108106cf 797
1e92909e
GT
798 if (SQLGetInfo(hdbc, SQL_PROCEDURES, (UCHAR*) dbInf.procedureSupport, 2, &cb) != SQL_SUCCESS)
799 return(DispAllErrors(henv, hdbc));
a8aa2258 800
3ca6a5f0
BP
801 if (SQLGetInfo(hdbc, SQL_ACCESSIBLE_TABLES, (UCHAR*) dbInf.accessibleTables, 2, &cb) != SQL_SUCCESS)
802 return(DispAllErrors(henv, hdbc));
a8aa2258 803
1e92909e
GT
804 if (SQLGetInfo(hdbc, SQL_CURSOR_COMMIT_BEHAVIOR, (UCHAR*) &dbInf.cursorCommitBehavior, sizeof(dbInf.cursorCommitBehavior), &cb) != SQL_SUCCESS)
805 return(DispAllErrors(henv, hdbc));
108106cf 806
1e92909e
GT
807 if (SQLGetInfo(hdbc, SQL_CURSOR_ROLLBACK_BEHAVIOR, (UCHAR*) &dbInf.cursorRollbackBehavior, sizeof(dbInf.cursorRollbackBehavior), &cb) != SQL_SUCCESS)
808 return(DispAllErrors(henv, hdbc));
108106cf 809
1e92909e
GT
810 if (SQLGetInfo(hdbc, SQL_NON_NULLABLE_COLUMNS, (UCHAR*) &dbInf.supportNotNullClause, sizeof(dbInf.supportNotNullClause), &cb) != SQL_SUCCESS)
811 return(DispAllErrors(henv, hdbc));
108106cf 812
1e92909e
GT
813 if (SQLGetInfo(hdbc, SQL_ODBC_SQL_OPT_IEF, (UCHAR*) dbInf.supportIEF, 2, &cb) != SQL_SUCCESS)
814 return(DispAllErrors(henv, hdbc));
108106cf 815
1e92909e
GT
816 if (SQLGetInfo(hdbc, SQL_DEFAULT_TXN_ISOLATION, (UCHAR*) &dbInf.txnIsolation, sizeof(dbInf.txnIsolation), &cb) != SQL_SUCCESS)
817 return(DispAllErrors(henv, hdbc));
108106cf 818
1e92909e
GT
819 if (SQLGetInfo(hdbc, SQL_TXN_ISOLATION_OPTION, (UCHAR*) &dbInf.txnIsolationOptions, sizeof(dbInf.txnIsolationOptions), &cb) != SQL_SUCCESS)
820 return(DispAllErrors(henv, hdbc));
108106cf 821
1e92909e
GT
822 if (SQLGetInfo(hdbc, SQL_FETCH_DIRECTION, (UCHAR*) &dbInf.fetchDirections, sizeof(dbInf.fetchDirections), &cb) != SQL_SUCCESS)
823 return(DispAllErrors(henv, hdbc));
108106cf 824
1e92909e
GT
825 if (SQLGetInfo(hdbc, SQL_LOCK_TYPES, (UCHAR*) &dbInf.lockTypes, sizeof(dbInf.lockTypes), &cb) != SQL_SUCCESS)
826 return(DispAllErrors(henv, hdbc));
108106cf 827
1e92909e
GT
828 if (SQLGetInfo(hdbc, SQL_POS_OPERATIONS, (UCHAR*) &dbInf.posOperations, sizeof(dbInf.posOperations), &cb) != SQL_SUCCESS)
829 return(DispAllErrors(henv, hdbc));
108106cf 830
1e92909e
GT
831 if (SQLGetInfo(hdbc, SQL_POSITIONED_STATEMENTS, (UCHAR*) &dbInf.posStmts, sizeof(dbInf.posStmts), &cb) != SQL_SUCCESS)
832 return(DispAllErrors(henv, hdbc));
108106cf 833
1e92909e
GT
834 if (SQLGetInfo(hdbc, SQL_SCROLL_CONCURRENCY, (UCHAR*) &dbInf.scrollConcurrency, sizeof(dbInf.scrollConcurrency), &cb) != SQL_SUCCESS)
835 return(DispAllErrors(henv, hdbc));
108106cf 836
1e92909e
GT
837 if (SQLGetInfo(hdbc, SQL_SCROLL_OPTIONS, (UCHAR*) &dbInf.scrollOptions, sizeof(dbInf.scrollOptions), &cb) != SQL_SUCCESS)
838 return(DispAllErrors(henv, hdbc));
108106cf 839
1e92909e
GT
840 if (SQLGetInfo(hdbc, SQL_STATIC_SENSITIVITY, (UCHAR*) &dbInf.staticSensitivity, sizeof(dbInf.staticSensitivity), &cb) != SQL_SUCCESS)
841 return(DispAllErrors(henv, hdbc));
108106cf 842
1e92909e
GT
843 if (SQLGetInfo(hdbc, SQL_TXN_CAPABLE, (UCHAR*) &dbInf.txnCapable, sizeof(dbInf.txnCapable), &cb) != SQL_SUCCESS)
844 return(DispAllErrors(henv, hdbc));
108106cf 845
1e92909e
GT
846 if (SQLGetInfo(hdbc, SQL_LOGIN_TIMEOUT, (UCHAR*) &dbInf.loginTimeout, sizeof(dbInf.loginTimeout), &cb) != SQL_SUCCESS)
847 return(DispAllErrors(henv, hdbc));
108106cf 848
1fc5dd6f 849#ifdef DBDEBUG_CONSOLE
23f681ec 850 cout << "***** DATA SOURCE INFORMATION *****" << endl;
1e92909e
GT
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 {
3ca6a5f0
BP
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;
1e92909e
GT
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;
3ca6a5f0 884 cout << "All tables accessible : " << dbInf.accessibleTables << endl;
1e92909e
GT
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;
108106cf 924#ifdef ODBC_V20
1e92909e 925 case SQL_TXN_VERSIONING: cout << "Versioning"; break;
108106cf 926#endif
1e92909e
GT
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, ";
108106cf 939#ifdef ODBC_V20
1e92909e
GT
940 if (dbInf.txnIsolationOptions & SQL_TXN_VERSIONING)
941 cout << "Versioning";
108106cf 942#endif
1e92909e
GT
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, ";
108106cf 958#ifdef ODBC_V20
1e92909e
GT
959 if (dbInf.fetchDirections & SQL_FD_FETCH_RESUME)
960 cout << "Resume, ";
108106cf 961#endif
1e92909e
GT
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;
108106cf
JS
1042#endif
1043
1e92909e
GT
1044 // Completed Successfully
1045 return(TRUE);
108106cf 1046
f6bcfd97 1047} // wxDb::getDbInfo()
108106cf 1048
67e9aaa3 1049
f6bcfd97
BP
1050/********** wxDb::getDataTypeInfo() **********/
1051bool wxDb::getDataTypeInfo(SWORD fSqlType, wxDbSqlTypeInfo &structSQLTypeInfo)
108106cf 1052{
67e9aaa3
GT
1053/*
1054 * fSqlType will be something like SQL_VARCHAR. This parameter determines
1055 * the data type inf. is gathered for.
1056 *
f6bcfd97 1057 * wxDbSqlTypeInfo is a structure that is filled in with data type information,
67e9aaa3 1058 */
1e92909e 1059 RETCODE retcode;
f6bcfd97
BP
1060 SDWORD cbRet;
1061
1e92909e
GT
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)
3ca6a5f0 1067 {
1fc5dd6f 1068#ifdef DBDEBUG_CONSOLE
3ca6a5f0
BP
1069 if (retcode == SQL_NO_DATA_FOUND)
1070 cout << "SQL_NO_DATA_FOUND fetching inf. about data type." << endl;
108106cf 1071#endif
3ca6a5f0
BP
1072 DispAllErrors(henv, hdbc, hstmt);
1073 SQLFreeStmt(hstmt, SQL_CLOSE);
1074 return(FALSE);
1075 }
1e92909e
GT
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));
67e9aaa3 1079
f6bcfd97
BP
1080 // BJO 20000503: no more needed with new GetColumns...
1081#if OLD_GETCOLUMNS
67e9aaa3
GT
1082 // BJO 991209
1083 if (Dbms() == dbmsMY_SQL)
3ca6a5f0 1084 {
f6bcfd97
BP
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");
3ca6a5f0 1091 }
f6bcfd97
BP
1092
1093 // BJO 20000427 : OpenLink driver
1094 if (!wxStrncmp(dbInf.driverName, "oplodbc", 7) ||
3ca6a5f0
BP
1095 !wxStrncmp(dbInf.driverName, "OLOD", 4))
1096 {
1097 if (!wxStrcmp(structSQLTypeInfo.TypeName, "double precision"))
1098 wxStrcpy(structSQLTypeInfo.TypeName, "real");
1099 }
f6bcfd97 1100#endif
67e9aaa3 1101
1e92909e
GT
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));
a2115c88 1108
1e92909e
GT
1109 if (SQLGetData(hstmt, 15, SQL_C_SHORT,(UCHAR*) &structSQLTypeInfo.MaximumScale, 0, &cbRet) != SQL_SUCCESS)
1110 return(DispAllErrors(henv, hdbc, hstmt));
108106cf 1111
1e92909e
GT
1112 if (structSQLTypeInfo.MaximumScale < 0)
1113 structSQLTypeInfo.MaximumScale = 0;
108106cf 1114
1e92909e
GT
1115 // Close the statement handle which closes open cursors
1116 if (SQLFreeStmt(hstmt, SQL_CLOSE) != SQL_SUCCESS)
1117 return(DispAllErrors(henv, hdbc, hstmt));
108106cf 1118
1e92909e
GT
1119 // Completed Successfully
1120 return(TRUE);
108106cf 1121
f6bcfd97 1122} // wxDb::getDataTypeInfo()
108106cf 1123
67e9aaa3 1124
f6bcfd97
BP
1125/********** wxDb::Close() **********/
1126void wxDb::Close(void)
108106cf 1127{
1e92909e
GT
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);
a2115c88 1152
e041ce57 1153#ifdef __WXDEBUG__
fdc03678 1154 wxTablesInUse *tiu;
1e92909e
GT
1155 wxNode *pNode;
1156 pNode = TablesInUse.First();
1157 wxString s,s2;
1158 while (pNode)
1159 {
fdc03678 1160 tiu = (wxTablesInUse *)pNode->Data();
1e92909e
GT
1161 if (tiu->pDb == this)
1162 {
fbdcff4a
JS
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);
0efe4a98 1165 wxLogDebug (s.c_str(),s2.c_str());
1e92909e
GT
1166 }
1167 pNode = pNode->Next();
1168 }
a2115c88
GT
1169#endif
1170
1e92909e
GT
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]);
a2115c88 1175
a8aa2258
GT
1176 dbmsType = dbmsUNIDENTIFIED;
1177 dbIsOpen = FALSE;
1178
f6bcfd97 1179} // wxDb::Close()
108106cf 1180
67e9aaa3 1181
f6bcfd97
BP
1182/********** wxDb::CommitTrans() **********/
1183bool wxDb::CommitTrans(void)
108106cf 1184{
1e92909e
GT
1185 if (this)
1186 {
1187 // Commit the transaction
1188 if (SQLTransact(henv, hdbc, SQL_COMMIT) != SQL_SUCCESS)
1189 return(DispAllErrors(henv, hdbc));
1190 }
108106cf 1191
1e92909e
GT
1192 // Completed successfully
1193 return(TRUE);
108106cf 1194
f6bcfd97 1195} // wxDb::CommitTrans()
108106cf 1196
67e9aaa3 1197
f6bcfd97
BP
1198/********** wxDb::RollbackTrans() **********/
1199bool wxDb::RollbackTrans(void)
108106cf 1200{
1e92909e
GT
1201 // Rollback the transaction
1202 if (SQLTransact(henv, hdbc, SQL_ROLLBACK) != SQL_SUCCESS)
1203 return(DispAllErrors(henv, hdbc));
108106cf 1204
1e92909e
GT
1205 // Completed successfully
1206 return(TRUE);
108106cf 1207
f6bcfd97 1208} // wxDb::RollbackTrans()
108106cf 1209
67e9aaa3 1210
f6bcfd97
BP
1211/********** wxDb::DispAllErrors() **********/
1212bool wxDb::DispAllErrors(HENV aHenv, HDBC aHdbc, HSTMT aHstmt)
a8aa2258
GT
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 */
108106cf 1227{
1e92909e
GT
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);
f6bcfd97 1233 logError(odbcErrMsg.c_str(), sqlState);
1e92909e
GT
1234 if (!silent)
1235 {
1fc5dd6f 1236#ifdef DBDEBUG_CONSOLE
1e92909e 1237 // When run in console mode, use standard out to display errors.
f6bcfd97 1238 cout << odbcErrMsg.c_str() << endl;
1e92909e
GT
1239 cout << "Press any key to continue..." << endl;
1240 getchar();
108106cf 1241#endif
a2115c88
GT
1242
1243#ifdef __WXDEBUG__
f6bcfd97 1244 wxLogDebug(odbcErrMsg.c_str(),wxT("ODBC DEBUG MESSAGE from DispAllErrors()"));
a2115c88 1245#endif
f6bcfd97 1246 }
1e92909e 1247 }
108106cf 1248
1e92909e 1249 return(FALSE); // This function always returns false.
108106cf 1250
f6bcfd97 1251} // wxDb::DispAllErrors()
108106cf 1252
67e9aaa3 1253
f6bcfd97
BP
1254/********** wxDb::GetNextError() **********/
1255bool wxDb::GetNextError(HENV aHenv, HDBC aHdbc, HSTMT aHstmt)
108106cf 1256{
1e92909e
GT
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);
108106cf 1261
f6bcfd97 1262} // wxDb::GetNextError()
108106cf 1263
67e9aaa3 1264
f6bcfd97
BP
1265/********** wxDb::DispNextError() **********/
1266void wxDb::DispNextError(void)
108106cf 1267{
1e92909e 1268 wxString odbcErrMsg;
108106cf 1269
1e92909e 1270 odbcErrMsg.sprintf("SQL State = %s\nNative Error Code = %li\nError Message = %s\n", sqlState, nativeError, errorMsg);
f6bcfd97 1271 logError(odbcErrMsg.c_str(), sqlState);
108106cf 1272
1e92909e
GT
1273 if (silent)
1274 return;
108106cf 1275
1fc5dd6f 1276#ifdef DBDEBUG_CONSOLE
1e92909e 1277 // When run in console mode, use standard out to display errors.
f6bcfd97 1278 cout << odbcErrMsg.c_str() << endl;
1e92909e
GT
1279 cout << "Press any key to continue..." << endl;
1280 getchar();
108106cf
JS
1281#endif
1282
f6bcfd97
BP
1283#ifdef __WXDEBUG__
1284 wxLogDebug(odbcErrMsg,wxT("ODBC DEBUG MESSAGE"));
1285#endif // __WXDEBUG__
108106cf 1286
f6bcfd97
BP
1287} // wxDb::DispNextError()
1288
1289
1290/********** wxDb::logError() **********/
1291void wxDb::logError(const char *errMsg, const char *SQLState)
108106cf 1292{
1e92909e 1293 assert(errMsg && wxStrlen(errMsg));
108106cf 1294
1e92909e
GT
1295 static int pLast = -1;
1296 int dbStatus;
108106cf 1297
1e92909e
GT
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 }
108106cf 1305
1e92909e 1306 wxStrcpy(errorList[pLast], errMsg);
108106cf 1307
1e92909e
GT
1308 if (SQLState && wxStrlen(SQLState))
1309 if ((dbStatus = TranslateSqlState(SQLState)) != DB_ERR_FUNCTION_SEQUENCE_ERROR)
1310 DB_STATUS = dbStatus;
108106cf 1311
1e92909e
GT
1312 // Add the errmsg to the sql log
1313 WriteSqlLog(errMsg);
a2115c88 1314
f6bcfd97 1315} // wxDb::logError()
108106cf 1316
67e9aaa3 1317
f6bcfd97
BP
1318/**********wxDb::TranslateSqlState() **********/
1319int wxDb::TranslateSqlState(const wxChar *SQLState)
108106cf 1320{
f6bcfd97 1321 if (!wxStrcmp(SQLState, wxT("01000")))
1e92909e 1322 return(DB_ERR_GENERAL_WARNING);
f6bcfd97 1323 if (!wxStrcmp(SQLState, wxT("01002")))
1e92909e 1324 return(DB_ERR_DISCONNECT_ERROR);
f6bcfd97 1325 if (!wxStrcmp(SQLState, wxT("01004")))
1e92909e 1326 return(DB_ERR_DATA_TRUNCATED);
f6bcfd97 1327 if (!wxStrcmp(SQLState, wxT("01006")))
1e92909e 1328 return(DB_ERR_PRIV_NOT_REVOKED);
f6bcfd97 1329 if (!wxStrcmp(SQLState, wxT("01S00")))
1e92909e 1330 return(DB_ERR_INVALID_CONN_STR_ATTR);
f6bcfd97 1331 if (!wxStrcmp(SQLState, wxT("01S01")))
1e92909e 1332 return(DB_ERR_ERROR_IN_ROW);
f6bcfd97 1333 if (!wxStrcmp(SQLState, wxT("01S02")))
1e92909e 1334 return(DB_ERR_OPTION_VALUE_CHANGED);
f6bcfd97 1335 if (!wxStrcmp(SQLState, wxT("01S03")))
1e92909e 1336 return(DB_ERR_NO_ROWS_UPD_OR_DEL);
f6bcfd97 1337 if (!wxStrcmp(SQLState, wxT("01S04")))
1e92909e 1338 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL);
f6bcfd97 1339 if (!wxStrcmp(SQLState, wxT("07001")))
1e92909e 1340 return(DB_ERR_WRONG_NO_OF_PARAMS);
f6bcfd97 1341 if (!wxStrcmp(SQLState, wxT("07006")))
1e92909e 1342 return(DB_ERR_DATA_TYPE_ATTR_VIOL);
f6bcfd97 1343 if (!wxStrcmp(SQLState, wxT("08001")))
1e92909e 1344 return(DB_ERR_UNABLE_TO_CONNECT);
f6bcfd97 1345 if (!wxStrcmp(SQLState, wxT("08002")))
1e92909e 1346 return(DB_ERR_CONNECTION_IN_USE);
f6bcfd97 1347 if (!wxStrcmp(SQLState, wxT("08003")))
1e92909e 1348 return(DB_ERR_CONNECTION_NOT_OPEN);
f6bcfd97 1349 if (!wxStrcmp(SQLState, wxT("08004")))
1e92909e 1350 return(DB_ERR_REJECTED_CONNECTION);
f6bcfd97 1351 if (!wxStrcmp(SQLState, wxT("08007")))
1e92909e 1352 return(DB_ERR_CONN_FAIL_IN_TRANS);
f6bcfd97 1353 if (!wxStrcmp(SQLState, wxT("08S01")))
1e92909e 1354 return(DB_ERR_COMM_LINK_FAILURE);
f6bcfd97 1355 if (!wxStrcmp(SQLState, wxT("21S01")))
1e92909e 1356 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH);
f6bcfd97 1357 if (!wxStrcmp(SQLState, wxT("21S02")))
1e92909e 1358 return(DB_ERR_DERIVED_TABLE_MISMATCH);
f6bcfd97 1359 if (!wxStrcmp(SQLState, wxT("22001")))
1e92909e 1360 return(DB_ERR_STRING_RIGHT_TRUNC);
f6bcfd97 1361 if (!wxStrcmp(SQLState, wxT("22003")))
1e92909e 1362 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG);
f6bcfd97 1363 if (!wxStrcmp(SQLState, wxT("22005")))
1e92909e 1364 return(DB_ERR_ERROR_IN_ASSIGNMENT);
f6bcfd97 1365 if (!wxStrcmp(SQLState, wxT("22008")))
1e92909e 1366 return(DB_ERR_DATETIME_FLD_OVERFLOW);
f6bcfd97 1367 if (!wxStrcmp(SQLState, wxT("22012")))
1e92909e 1368 return(DB_ERR_DIVIDE_BY_ZERO);
f6bcfd97 1369 if (!wxStrcmp(SQLState, wxT("22026")))
1e92909e 1370 return(DB_ERR_STR_DATA_LENGTH_MISMATCH);
f6bcfd97 1371 if (!wxStrcmp(SQLState, wxT("23000")))
1e92909e 1372 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL);
f6bcfd97 1373 if (!wxStrcmp(SQLState, wxT("24000")))
1e92909e 1374 return(DB_ERR_INVALID_CURSOR_STATE);
f6bcfd97 1375 if (!wxStrcmp(SQLState, wxT("25000")))
1e92909e 1376 return(DB_ERR_INVALID_TRANS_STATE);
f6bcfd97 1377 if (!wxStrcmp(SQLState, wxT("28000")))
1e92909e 1378 return(DB_ERR_INVALID_AUTH_SPEC);
f6bcfd97 1379 if (!wxStrcmp(SQLState, wxT("34000")))
1e92909e 1380 return(DB_ERR_INVALID_CURSOR_NAME);
f6bcfd97 1381 if (!wxStrcmp(SQLState, wxT("37000")))
1e92909e 1382 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL);
f6bcfd97 1383 if (!wxStrcmp(SQLState, wxT("3C000")))
1e92909e 1384 return(DB_ERR_DUPLICATE_CURSOR_NAME);
f6bcfd97 1385 if (!wxStrcmp(SQLState, wxT("40001")))
1e92909e 1386 return(DB_ERR_SERIALIZATION_FAILURE);
f6bcfd97 1387 if (!wxStrcmp(SQLState, wxT("42000")))
1e92909e 1388 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2);
f6bcfd97 1389 if (!wxStrcmp(SQLState, wxT("70100")))
1e92909e 1390 return(DB_ERR_OPERATION_ABORTED);
f6bcfd97 1391 if (!wxStrcmp(SQLState, wxT("IM001")))
1e92909e 1392 return(DB_ERR_UNSUPPORTED_FUNCTION);
f6bcfd97 1393 if (!wxStrcmp(SQLState, wxT("IM002")))
1e92909e 1394 return(DB_ERR_NO_DATA_SOURCE);
f6bcfd97 1395 if (!wxStrcmp(SQLState, wxT("IM003")))
1e92909e 1396 return(DB_ERR_DRIVER_LOAD_ERROR);
f6bcfd97 1397 if (!wxStrcmp(SQLState, wxT("IM004")))
1e92909e 1398 return(DB_ERR_SQLALLOCENV_FAILED);
f6bcfd97 1399 if (!wxStrcmp(SQLState, wxT("IM005")))
1e92909e 1400 return(DB_ERR_SQLALLOCCONNECT_FAILED);
f6bcfd97 1401 if (!wxStrcmp(SQLState, wxT("IM006")))
1e92909e 1402 return(DB_ERR_SQLSETCONNECTOPTION_FAILED);
f6bcfd97 1403 if (!wxStrcmp(SQLState, wxT("IM007")))
1e92909e 1404 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB);
f6bcfd97 1405 if (!wxStrcmp(SQLState, wxT("IM008")))
1e92909e 1406 return(DB_ERR_DIALOG_FAILED);
f6bcfd97 1407 if (!wxStrcmp(SQLState, wxT("IM009")))
1e92909e 1408 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL);
f6bcfd97 1409 if (!wxStrcmp(SQLState, wxT("IM010")))
1e92909e 1410 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG);
f6bcfd97 1411 if (!wxStrcmp(SQLState, wxT("IM011")))
1e92909e 1412 return(DB_ERR_DRIVER_NAME_TOO_LONG);
f6bcfd97 1413 if (!wxStrcmp(SQLState, wxT("IM012")))
1e92909e 1414 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR);
f6bcfd97 1415 if (!wxStrcmp(SQLState, wxT("IM013")))
1e92909e 1416 return(DB_ERR_TRACE_FILE_ERROR);
f6bcfd97 1417 if (!wxStrcmp(SQLState, wxT("S0001")))
1e92909e 1418 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS);
f6bcfd97 1419 if (!wxStrcmp(SQLState, wxT("S0002")))
1e92909e 1420 return(DB_ERR_TABLE_NOT_FOUND);
f6bcfd97 1421 if (!wxStrcmp(SQLState, wxT("S0011")))
1e92909e 1422 return(DB_ERR_INDEX_ALREADY_EXISTS);
f6bcfd97 1423 if (!wxStrcmp(SQLState, wxT("S0012")))
1e92909e 1424 return(DB_ERR_INDEX_NOT_FOUND);
f6bcfd97 1425 if (!wxStrcmp(SQLState, wxT("S0021")))
1e92909e 1426 return(DB_ERR_COLUMN_ALREADY_EXISTS);
f6bcfd97 1427 if (!wxStrcmp(SQLState, wxT("S0022")))
1e92909e 1428 return(DB_ERR_COLUMN_NOT_FOUND);
f6bcfd97 1429 if (!wxStrcmp(SQLState, wxT("S0023")))
1e92909e 1430 return(DB_ERR_NO_DEFAULT_FOR_COLUMN);
f6bcfd97 1431 if (!wxStrcmp(SQLState, wxT("S1000")))
1e92909e 1432 return(DB_ERR_GENERAL_ERROR);
f6bcfd97 1433 if (!wxStrcmp(SQLState, wxT("S1001")))
1e92909e 1434 return(DB_ERR_MEMORY_ALLOCATION_FAILURE);
f6bcfd97 1435 if (!wxStrcmp(SQLState, wxT("S1002")))
1e92909e 1436 return(DB_ERR_INVALID_COLUMN_NUMBER);
f6bcfd97 1437 if (!wxStrcmp(SQLState, wxT("S1003")))
1e92909e 1438 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE);
f6bcfd97 1439 if (!wxStrcmp(SQLState, wxT("S1004")))
1e92909e 1440 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE);
f6bcfd97 1441 if (!wxStrcmp(SQLState, wxT("S1008")))
1e92909e 1442 return(DB_ERR_OPERATION_CANCELLED);
f6bcfd97 1443 if (!wxStrcmp(SQLState, wxT("S1009")))
1e92909e 1444 return(DB_ERR_INVALID_ARGUMENT_VALUE);
f6bcfd97 1445 if (!wxStrcmp(SQLState, wxT("S1010")))
1e92909e 1446 return(DB_ERR_FUNCTION_SEQUENCE_ERROR);
f6bcfd97 1447 if (!wxStrcmp(SQLState, wxT("S1011")))
1e92909e 1448 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME);
f6bcfd97 1449 if (!wxStrcmp(SQLState, wxT("S1012")))
1e92909e 1450 return(DB_ERR_INVALID_TRANS_OPERATION_CODE);
f6bcfd97 1451 if (!wxStrcmp(SQLState, wxT("S1015")))
1e92909e 1452 return(DB_ERR_NO_CURSOR_NAME_AVAIL);
f6bcfd97 1453 if (!wxStrcmp(SQLState, wxT("S1090")))
1e92909e 1454 return(DB_ERR_INVALID_STR_OR_BUF_LEN);
f6bcfd97 1455 if (!wxStrcmp(SQLState, wxT("S1091")))
1e92909e 1456 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE);
f6bcfd97 1457 if (!wxStrcmp(SQLState, wxT("S1092")))
1e92909e 1458 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE);
f6bcfd97 1459 if (!wxStrcmp(SQLState, wxT("S1093")))
1e92909e 1460 return(DB_ERR_INVALID_PARAM_NO);
f6bcfd97 1461 if (!wxStrcmp(SQLState, wxT("S1094")))
1e92909e 1462 return(DB_ERR_INVALID_SCALE_VALUE);
f6bcfd97 1463 if (!wxStrcmp(SQLState, wxT("S1095")))
1e92909e 1464 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE);
f6bcfd97 1465 if (!wxStrcmp(SQLState, wxT("S1096")))
1e92909e 1466 return(DB_ERR_INF_TYPE_OUT_OF_RANGE);
f6bcfd97 1467 if (!wxStrcmp(SQLState, wxT("S1097")))
1e92909e 1468 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE);
f6bcfd97 1469 if (!wxStrcmp(SQLState, wxT("S1098")))
1e92909e 1470 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE);
f6bcfd97 1471 if (!wxStrcmp(SQLState, wxT("S1099")))
1e92909e 1472 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE);
f6bcfd97 1473 if (!wxStrcmp(SQLState, wxT("S1100")))
1e92909e 1474 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE);
f6bcfd97 1475 if (!wxStrcmp(SQLState, wxT("S1101")))
1e92909e 1476 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE);
f6bcfd97 1477 if (!wxStrcmp(SQLState, wxT("S1103")))
1e92909e 1478 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE);
f6bcfd97 1479 if (!wxStrcmp(SQLState, wxT("S1104")))
1e92909e 1480 return(DB_ERR_INVALID_PRECISION_VALUE);
f6bcfd97 1481 if (!wxStrcmp(SQLState, wxT("S1105")))
1e92909e 1482 return(DB_ERR_INVALID_PARAM_TYPE);
f6bcfd97 1483 if (!wxStrcmp(SQLState, wxT("S1106")))
1e92909e 1484 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE);
f6bcfd97 1485 if (!wxStrcmp(SQLState, wxT("S1107")))
1e92909e 1486 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE);
f6bcfd97 1487 if (!wxStrcmp(SQLState, wxT("S1108")))
1e92909e 1488 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE);
f6bcfd97 1489 if (!wxStrcmp(SQLState, wxT("S1109")))
1e92909e 1490 return(DB_ERR_INVALID_CURSOR_POSITION);
f6bcfd97 1491 if (!wxStrcmp(SQLState, wxT("S1110")))
1e92909e 1492 return(DB_ERR_INVALID_DRIVER_COMPLETION);
f6bcfd97 1493 if (!wxStrcmp(SQLState, wxT("S1111")))
1e92909e 1494 return(DB_ERR_INVALID_BOOKMARK_VALUE);
f6bcfd97 1495 if (!wxStrcmp(SQLState, wxT("S1C00")))
1e92909e 1496 return(DB_ERR_DRIVER_NOT_CAPABLE);
f6bcfd97 1497 if (!wxStrcmp(SQLState, wxT("S1T00")))
1e92909e
GT
1498 return(DB_ERR_TIMEOUT_EXPIRED);
1499
1500 // No match
1501 return(0);
108106cf 1502
f6bcfd97 1503} // wxDb::TranslateSqlState()
67e9aaa3 1504
23f681ec 1505
f6bcfd97
BP
1506/********** wxDb::Grant() **********/
1507bool wxDb::Grant(int privileges, const char *tableName, const char *userList)
108106cf 1508{
1e92909e
GT
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;
108106cf 1547
1fc5dd6f 1548#ifdef DBDEBUG_CONSOLE
f6bcfd97 1549 cout << endl << sqlStmt.c_str() << endl;
108106cf
JS
1550#endif
1551
f6bcfd97 1552 WriteSqlLog(sqlStmt.c_str());
1fc5dd6f 1553
f6bcfd97 1554 return(ExecSql(sqlStmt.c_str()));
108106cf 1555
f6bcfd97 1556} // wxDb::Grant()
108106cf 1557
67e9aaa3 1558
f6bcfd97
BP
1559/********** wxDb::CreateView() **********/
1560bool wxDb::CreateView(const char *viewName, const char *colList, const char *pSqlStmt, bool attemptDrop)
108106cf 1561{
1e92909e
GT
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;
23f681ec 1571
1e92909e
GT
1572 if (wxStrlen(colList))
1573 {
1574 sqlStmt += " (";
1575 sqlStmt += colList;
1576 sqlStmt += ")";
1577 }
108106cf 1578
1e92909e
GT
1579 sqlStmt += " AS ";
1580 sqlStmt += pSqlStmt;
108106cf 1581
f6bcfd97 1582 WriteSqlLog(sqlStmt.c_str());
1fc5dd6f
JS
1583
1584#ifdef DBDEBUG_CONSOLE
f6bcfd97 1585 cout << sqlStmt.c_str() << endl;
108106cf
JS
1586#endif
1587
f6bcfd97 1588 return(ExecSql(sqlStmt.c_str()));
108106cf 1589
f6bcfd97 1590} // wxDb::CreateView()
108106cf 1591
67e9aaa3 1592
f6bcfd97
BP
1593/********** wxDb::DropView() **********/
1594bool wxDb::DropView(const char *viewName)
a2115c88 1595{
67e9aaa3
GT
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
1e92909e 1599 * below for any other databases when those databases are defined
67e9aaa3
GT
1600 * to handle this situation consistently
1601 */
1e92909e
GT
1602// char sqlStmt[DB_MAX_STATEMENT_LEN];
1603 wxString sqlStmt;
a2115c88 1604
1e92909e 1605 sqlStmt.sprintf("DROP VIEW %s", viewName);
a2115c88 1606
f6bcfd97 1607 WriteSqlLog(sqlStmt.c_str());
a2115c88
GT
1608
1609#ifdef DBDEBUG_CONSOLE
f6bcfd97 1610 cout << endl << sqlStmt.c_str() << endl;
a2115c88
GT
1611#endif
1612
f6bcfd97 1613 if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
1e92909e
GT
1614 {
1615 // Check for "Base table not found" error and ignore
1616 GetNextError(henv, hdbc, hstmt);
f6bcfd97 1617 if (wxStrcmp(sqlState,wxT("S0002"))) // "Base table not found"
1e92909e
GT
1618 {
1619 // Check for product specific error codes
f6bcfd97 1620 if (!((Dbms() == dbmsSYBASE_ASA && !wxStrcmp(sqlState,wxT("42000"))))) // 5.x (and lower?)
1e92909e
GT
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;
a2115c88 1635
f6bcfd97 1636} // wxDb::DropView()
a2115c88
GT
1637
1638
f6bcfd97
BP
1639/********** wxDb::ExecSql() **********/
1640bool wxDb::ExecSql(const char *pSqlStmt)
108106cf 1641{
1e92909e
GT
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 }
108106cf 1650
f6bcfd97 1651} // wxDb::ExecSql()
108106cf 1652
67e9aaa3 1653
f6bcfd97
BP
1654/********** wxDb::GetNext() **********/
1655bool wxDb::GetNext(void)
a2115c88 1656{
1e92909e
GT
1657 if (SQLFetch(hstmt) == SQL_SUCCESS)
1658 return(TRUE);
1659 else
1660 {
1661 DispAllErrors(henv, hdbc, hstmt);
1662 return(FALSE);
1663 }
a2115c88 1664
f6bcfd97 1665} // wxDb::GetNext()
a2115c88 1666
67e9aaa3 1667
f6bcfd97
BP
1668/********** wxDb::GetData() **********/
1669bool wxDb::GetData(UWORD colNo, SWORD cType, PTR pData, SDWORD maxLen, SDWORD FAR *cbReturned)
a2115c88 1670{
1e92909e
GT
1671 assert(pData);
1672 assert(cbReturned);
a2115c88 1673
1e92909e
GT
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 }
a2115c88 1681
f6bcfd97 1682} // wxDb::GetData()
a2115c88 1683
67e9aaa3 1684
f6bcfd97
BP
1685/********** wxDb::GetKeyFields() **********/
1686int wxDb::GetKeyFields(char *tableName, wxDbColInf* colInf, int noCols)
67e9aaa3
GT
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 * ---------------------------------------------------------------------
0cd121f9 1700 * -- 19991224 : mj10777 : Create ------
67e9aaa3
GT
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.) ?
3ca6a5f0
BP
1734 } // if
1735 } // while
67e9aaa3
GT
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,
1e92909e
GT
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 */
67e9aaa3
GT
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);
f6bcfd97 1765 Temp0.Printf(wxT("%s[%s] "),Temp0.c_str(),szFkTable); // [ ] in case there is a blank in the Table name
3ca6a5f0
BP
1766 } // if
1767 } // while
67e9aaa3 1768 Temp0.Trim(); // Get rid of any unneeded blanks
f6bcfd97 1769 if (Temp0 != wxT(""))
67e9aaa3 1770 {
3ca6a5f0
BP
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
67e9aaa3
GT
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.) ?
3ca6a5f0
BP
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
67e9aaa3
GT
1815 SQLFreeStmt(hstmt, SQL_CLOSE); /* Close the cursor (the hstmt is still allocated). */
1816
67e9aaa3 1817 return TRUE;
3ca6a5f0 1818
f6bcfd97 1819} // wxDb::GetKeyFields()
67e9aaa3
GT
1820
1821
f6bcfd97
BP
1822#if OLD_GETCOLUMNS
1823/********** wxDb::GetColumns() **********/
1824wxDbColInf *wxDb::GetColumns(char *tableName[], const char *userID)
108106cf 1825/*
1e92909e
GT
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.
f6bcfd97 1828 * 2) This function returns an array of wxDbColInf structures. If no columns
1e92909e
GT
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.
108106cf 1832 *
f6bcfd97 1833 * wxDbColInf *colInf = pDb->GetColumns(tableList, userID);
1e92909e
GT
1834 * if (colInf)
1835 * {
1836 * // Use the column inf
1837 * .......
1838 * // Destroy the memory
1839 * delete [] colInf;
1840 * }
67e9aaa3
GT
1841 *
1842 * userID is evaluated in the following manner:
1e92909e
GT
1843 * userID == NULL ... UserID is ignored
1844 * userID == "" ... UserID set equal to 'this->uid'
1845 * userID != "" ... UserID set equal to 'userID'
67e9aaa3 1846 *
f6bcfd97
BP
1847 * NOTE: ALL column bindings associated with this wxDb instance are unbound
1848 * by this function. This function should use its own wxDb instance
ea5d599d 1849 * to avoid undesired unbinding of columns.
108106cf 1850 */
108106cf 1851{
1e92909e
GT
1852 int noCols = 0;
1853 int colNo = 0;
f6bcfd97 1854 wxDbColInf *colInf = 0;
1e92909e
GT
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
23f681ec 1876 // Oracle user names may only be in uppercase, so force
1e92909e
GT
1877 // the name to uppercase
1878 if (Dbms() == dbmsORACLE)
1879 UserID = UserID.Upper();
1880
1881 // Pass 1 - Determine how many columns there are.
f6bcfd97 1882 // Pass 2 - Allocate the wxDbColInf array and fill in
1e92909e
GT
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;
f6bcfd97
BP
1891 // Allocate n wxDbColInf objects to hold the column information
1892 colInf = new wxDbColInf[noCols+1];
1e92909e
GT
1893 if (!colInf)
1894 break;
1895 // Mark the end of the array
f6bcfd97
BP
1896 wxStrcpy(colInf[noCols].tableName, wxT(""));
1897 wxStrcpy(colInf[noCols].colName, wxT(""));
1e92909e
GT
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];
23f681ec 1905 // Oracle table names are uppercase only, so force
1e92909e
GT
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
a8aa2258 1912 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
1e92909e 1913 // use the call below that leaves out the user name
f6bcfd97 1914 if (wxStrcmp(UserID.c_str(),wxT("")) &&
1e92909e 1915 Dbms() != dbmsMY_SQL &&
a8aa2258
GT
1916 Dbms() != dbmsACCESS &&
1917 Dbms() != dbmsMS_SQL_SERVER)
1e92909e
GT
1918 {
1919 retcode = SQLColumns(hstmt,
1920 NULL, 0, // All qualifiers
f6bcfd97
BP
1921 (UCHAR *) UserID.c_str(), SQL_NTS, // Owner
1922 (UCHAR *) TableName.c_str(), SQL_NTS,
1e92909e
GT
1923 NULL, 0); // All columns
1924 }
1925 else
1926 {
1927 retcode = SQLColumns(hstmt,
1928 NULL, 0, // All qualifiers
1929 NULL, 0, // Owner
f6bcfd97 1930 (UCHAR *) TableName.c_str(), SQL_NTS,
1e92909e
GT
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);
52a17fe5 1962 GetData(12, SQL_C_CHAR, (UCHAR*) colInf[colNo].remarks, 254+1, &cb);
3ca6a5f0 1963
f6bcfd97 1964 // Determine the wxDb data type that is used to represent the native data type of this data source
1e92909e
GT
1965 colInf[colNo].dbDataType = 0;
1966 if (!wxStricmp(typeInfVarchar.TypeName,colInf[colNo].typeName))
52a17fe5 1967 {
a8aa2258
GT
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)
52a17fe5
GT
1972 if (colInf[colNo].columnSize < 1)
1973 {
52a17fe5
GT
1974 colInf[colNo].columnSize = colInf[colNo].bufferLength;
1975 }
a8aa2258 1976#endif
1e92909e 1977 colInf[colNo].dbDataType = DB_DATA_TYPE_VARCHAR;
52a17fe5 1978 }
1e92909e
GT
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;
108106cf 2003
f6bcfd97 2004} // wxDb::GetColumns()
108106cf
JS
2005
2006
f6bcfd97
BP
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
67e9aaa3 2024{
1e92909e
GT
2025 int noCols = 0;
2026 int colNo = 0;
f6bcfd97 2027 wxDbColInf *colInf = 0;
1e92909e
GT
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
23f681ec 2049 // Oracle user names may only be in uppercase, so force
1e92909e
GT
2050 // the name to uppercase
2051 if (Dbms() == dbmsORACLE)
2052 UserID = UserID.Upper();
2053
2054 // Pass 1 - Determine how many columns there are.
f6bcfd97 2055 // Pass 2 - Allocate the wxDbColInf array and fill in
1e92909e
GT
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;
f6bcfd97
BP
2064 // Allocate n wxDbColInf objects to hold the column information
2065 colInf = new wxDbColInf[noCols+1];
1e92909e
GT
2066 if (!colInf)
2067 break;
2068 // Mark the end of the array
f6bcfd97
BP
2069 wxStrcpy(colInf[noCols].tableName, wxT(""));
2070 wxStrcpy(colInf[noCols].colName, wxT(""));
1e92909e
GT
2071 colInf[noCols].sqlDataType = 0;
2072 }
2073
2074 TableName = tableName;
23f681ec 2075 // Oracle table names are uppercase only, so force
1e92909e
GT
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
a8aa2258 2082 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
1e92909e 2083 // use the call below that leaves out the user name
f6bcfd97 2084 if (wxStrcmp(UserID.c_str(),wxT("")) &&
1e92909e 2085 Dbms() != dbmsMY_SQL &&
a8aa2258
GT
2086 Dbms() != dbmsACCESS &&
2087 Dbms() != dbmsMS_SQL_SERVER)
1e92909e
GT
2088 {
2089 retcode = SQLColumns(hstmt,
2090 NULL, 0, // All qualifiers
f6bcfd97
BP
2091 (UCHAR *) UserID.c_str(), SQL_NTS, // Owner
2092 (UCHAR *) TableName.c_str(), SQL_NTS,
1e92909e
GT
2093 NULL, 0); // All columns
2094 }
2095 else
2096 {
2097 retcode = SQLColumns(hstmt,
2098 NULL, 0, // All qualifiers
2099 NULL, 0, // Owner
f6bcfd97 2100 (UCHAR *) TableName.c_str(), SQL_NTS,
1e92909e
GT
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);
67e9aaa3 2130 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
23f681ec 2131 GetData( 8, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].bufferLength, 0, &cb);
1e92909e
GT
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);
67e9aaa3
GT
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
3ca6a5f0
BP
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 {
a8aa2258
GT
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)
3ca6a5f0
BP
2158 if (colInf[colNo].columnSize < 1)
2159 {
a8aa2258 2160 colInf[colNo].columnSize = colInf[colNo].bufferLength;
3ca6a5f0 2161 }
a8aa2258
GT
2162#endif
2163
3ca6a5f0
BP
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
1e92909e
GT
2173 colNo++;
2174 }
2175 }
2176 }
2177 if (retcode != SQL_NO_DATA_FOUND)
3ca6a5f0 2178 { // Error occured, abort
1e92909e
GT
2179 DispAllErrors(henv, hdbc, hstmt);
2180 if (colInf)
3ca6a5f0 2181 delete [] colInf;
1e92909e
GT
2182 SQLFreeStmt(hstmt, SQL_CLOSE);
2183 if (numCols)
3ca6a5f0 2184 *numCols = 0;
1e92909e 2185 return(0);
3ca6a5f0 2186 }
1e92909e 2187 }
67e9aaa3
GT
2188
2189 SQLFreeStmt(hstmt, SQL_CLOSE);
2190
2191 // Store Primary and Foriegn Keys
2192 GetKeyFields(tableName,colInf,noCols);
2193
2194 if (numCols)
1e92909e
GT
2195 *numCols = noCols;
2196 return colInf;
67e9aaa3 2197
f6bcfd97
BP
2198} // wxDb::GetColumns()
2199
f6bcfd97 2200
3ca6a5f0 2201#else // New GetColumns
67e9aaa3
GT
2202
2203
f6bcfd97 2204/*
3ca6a5f0
BP
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
f6bcfd97
BP
2220*/
2221
2222typedef struct
2223{
3ca6a5f0
BP
2224 int noCols;
2225 wxDbColInf *colInf;
f6bcfd97
BP
2226} _TableColumns;
2227
3ca6a5f0 2228
f6bcfd97
BP
2229wxDbColInf *wxDb::GetColumns(char *tableName[], const char* userID)
2230{
3ca6a5f0
BP
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
f6bcfd97
BP
2277
2278
2279wxDbColInf *wxDb::GetColumns(char *tableName, int *numCols, const char *userID)
3ca6a5f0
BP
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.
f6bcfd97 2293{
3ca6a5f0
BP
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)
f6bcfd97 2330 {
3ca6a5f0
BP
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;
f6bcfd97 2341 }
3ca6a5f0
BP
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
a8aa2258 2351 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
3ca6a5f0
BP
2352 // use the call below that leaves out the user name
2353 if (wxStrcmp(UserID.c_str(),wxT("")) &&
2354 Dbms() != dbmsMY_SQL &&
a8aa2258
GT
2355 Dbms() != dbmsACCESS &&
2356 Dbms() != dbmsMS_SQL_SERVER)
f6bcfd97 2357 {
3ca6a5f0
BP
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
f6bcfd97 2363 }
3ca6a5f0 2364 else
f6bcfd97 2365 {
3ca6a5f0
BP
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
f6bcfd97 2371 }
3ca6a5f0 2372 if (retcode != SQL_SUCCESS)
f6bcfd97 2373 { // Error occured, abort
3ca6a5f0
BP
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);
f6bcfd97 2381 }
3ca6a5f0 2382
f6bcfd97
BP
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
3ca6a5f0
BP
2409
2410#ifdef _IODBC_
a8aa2258
GT
2411 // IODBC does not return a correct columnSize, so we set
2412 // columnSize = bufferLength if no column size was returned
3ca6a5f0 2413 // IODBC returns the columnSize in bufferLength.. (bug)
a8aa2258
GT
2414 if (colInf[colNo].columnSize < 1)
2415 {
2416 colInf[colNo].columnSize = colInf[colNo].bufferLength;
2417 }
f6bcfd97 2418#endif
3ca6a5f0
BP
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;
f6bcfd97 2445#ifdef __WXDEBUG__
3ca6a5f0
BP
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 }
f6bcfd97
BP
2452 colNo++;
2453 }
2454 }
2455 }
2456 if (retcode != SQL_NO_DATA_FOUND)
3ca6a5f0 2457 { // Error occured, abort
f6bcfd97
BP
2458 DispAllErrors(henv, hdbc, hstmt);
2459 if (colInf)
3ca6a5f0 2460 delete [] colInf;
f6bcfd97
BP
2461 SQLFreeStmt(hstmt, SQL_CLOSE);
2462 if (numCols)
3ca6a5f0 2463 *numCols = 0;
f6bcfd97 2464 return(0);
3ca6a5f0
BP
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;
f6bcfd97 2487 }
3ca6a5f0
BP
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++)
f6bcfd97 2506 {
3ca6a5f0
BP
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 }
f6bcfd97 2534 }
3ca6a5f0 2535 SQLFreeStmt(hstmt, SQL_CLOSE);
f6bcfd97 2536
3ca6a5f0
BP
2537 ///////////////////////////////////////////////////////////////////////////
2538 // End sorting
2539 ///////////////////////////////////////////////////////////////////////////
f6bcfd97 2540
3ca6a5f0
BP
2541 if (numCols)
2542 *numCols = noCols;
2543 return colInf;
2544
f6bcfd97
BP
2545} // wxDb::GetColumns()
2546
2547
3ca6a5f0 2548#endif // #else OLD_GETCOLUMNS
f6bcfd97
BP
2549
2550
2551/********** wxDb::GetColumnCount() **********/
2552int wxDb::GetColumnCount(char *tableName, const char *userID)
67e9aaa3
GT
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:
1e92909e
GT
2559 * userID == NULL ... UserID is ignored
2560 * userID == "" ... UserID set equal to 'this->uid'
2561 * userID != "" ... UserID set equal to 'userID'
67e9aaa3 2562 *
f6bcfd97
BP
2563 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2564 * by this function. This function should use its own wxDb instance
67e9aaa3
GT
2565 * to avoid undesired unbinding of columns.
2566 */
2567{
1e92909e
GT
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
f6bcfd97 2583 UserID = wxT("");
1e92909e
GT
2584
2585 // dBase does not use user names, and some drivers fail if you try to pass one
2586 if (Dbms() == dbmsDBASE)
f6bcfd97 2587 UserID = wxT("");
1e92909e 2588
23f681ec 2589 // Oracle user names may only be in uppercase, so force
1e92909e
GT
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;
23f681ec 2598 // Oracle table names are uppercase only, so force
1e92909e
GT
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
a8aa2258 2605 // MySQL, SQLServer, and Access cannot accept a user name when looking up column names, so we
1e92909e 2606 // use the call below that leaves out the user name
f6bcfd97 2607 if (wxStrcmp(UserID.c_str(),wxT("")) &&
1e92909e 2608 Dbms() != dbmsMY_SQL &&
a8aa2258
GT
2609 Dbms() != dbmsACCESS &&
2610 Dbms() != dbmsMS_SQL_SERVER)
1e92909e
GT
2611 {
2612 retcode = SQLColumns(hstmt,
2613 NULL, 0, // All qualifiers
3ca6a5f0 2614 (UCHAR *) UserID.c_str(), SQL_NTS, // Owner
f6bcfd97 2615 (UCHAR *) TableName.c_str(), SQL_NTS,
1e92909e
GT
2616 NULL, 0); // All columns
2617 }
2618 else
2619 {
2620 retcode = SQLColumns(hstmt,
2621 NULL, 0, // All qualifiers
2622 NULL, 0, // Owner
f6bcfd97 2623 (UCHAR *) TableName.c_str(), SQL_NTS,
1e92909e
GT
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;
67e9aaa3 2648
f6bcfd97 2649} // wxDb::GetColumnCount()
67e9aaa3
GT
2650
2651
f6bcfd97
BP
2652/********** wxDb::GetCatalog() *******/
2653wxDbInf *wxDb::GetCatalog(char *userID)
67e9aaa3
GT
2654/*
2655 * ---------------------------------------------------------------------
0cd121f9 2656 * -- 19991203 : mj10777 : Create ------
67e9aaa3
GT
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:
1e92909e
GT
2667 * userID == NULL ... UserID is ignored
2668 * userID == "" ... UserID set equal to 'this->uid'
2669 * userID != "" ... UserID set equal to 'userID'
67e9aaa3 2670 *
f6bcfd97
BP
2671 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2672 * by this function. This function should use its own wxDb instance
67e9aaa3
GT
2673 * to avoid undesired unbinding of columns.
2674 */
2675{
1e92909e
GT
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;
1e92909e
GT
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
f6bcfd97 2693 UserID = wxT("");
1e92909e
GT
2694
2695 // dBase does not use user names, and some drivers fail if you try to pass one
2696 if (Dbms() == dbmsDBASE)
f6bcfd97 2697 UserID = wxT("");
1e92909e 2698
23f681ec 2699 // Oracle user names may only be in uppercase, so force
1e92909e
GT
2700 // the name to uppercase
2701 if (Dbms() == dbmsORACLE)
2702 UserID = UserID.Upper();
2703
2704 //-------------------------------------------------------------
2705 pDbInf = new wxDbInf; // Create the Database Arrray
1e92909e
GT
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 //-------------------------------------------------------------
f6bcfd97 2712
1e92909e
GT
2713 for (pass = 1; pass <= 2; pass++)
2714 {
2715 SQLFreeStmt(hstmt, SQL_CLOSE); // Close if Open
f6bcfd97 2716 tblNameSave = wxT("");
1e92909e 2717
f6bcfd97 2718 if (wxStrcmp(UserID.c_str(),wxT("")) &&
3ca6a5f0
BP
2719 Dbms() != dbmsMY_SQL &&
2720 Dbms() != dbmsACCESS)
1e92909e
GT
2721 {
2722 retcode = SQLTables(hstmt,
2723 NULL, 0, // All qualifiers
3ca6a5f0 2724 (UCHAR *) UserID.c_str(), SQL_NTS, // User specified
1e92909e
GT
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 }
f6bcfd97 2736
1e92909e
GT
2737 if (retcode != SQL_SUCCESS)
2738 {
2739 DispAllErrors(henv, hdbc, hstmt);
2740 pDbInf = NULL;
2741 SQLFreeStmt(hstmt, SQL_CLOSE);
2742 return pDbInf;
2743 }
3ca6a5f0 2744
1e92909e
GT
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
f6bcfd97 2760 pDbInf->pTableInf = new wxDbTableInf[pDbInf->numTables];
1e92909e 2761 noTab = 0;
3ca6a5f0 2762 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
f6bcfd97 2763
1e92909e
GT
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);
3ca6a5f0 2767
1e92909e 2768 noTab++;
3ca6a5f0
BP
2769 } // if
2770 } // while
2771 } // for
1e92909e
GT
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 }
3ca6a5f0 2779
1e92909e 2780 return pDbInf;
3ca6a5f0 2781
f6bcfd97 2782} // wxDb::GetCatalog()
67e9aaa3
GT
2783
2784
f6bcfd97
BP
2785/********** wxDb::Catalog() **********/
2786bool wxDb::Catalog(const char *userID, const char *fileName)
67e9aaa3
GT
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:
1e92909e
GT
2793 * userID == NULL ... UserID is ignored
2794 * userID == "" ... UserID set equal to 'this->uid'
2795 * userID != "" ... UserID set equal to 'userID'
67e9aaa3 2796 *
f6bcfd97
BP
2797 * NOTE: ALL column bindings associated with this wxDb instance are unbound
2798 * by this function. This function should use its own wxDb instance
67e9aaa3
GT
2799 * to avoid undesired unbinding of columns.
2800 */
1fc5dd6f 2801{
1e92909e
GT
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
f6bcfd97 2829 UserID = wxT("");
1e92909e
GT
2830
2831 // dBase does not use user names, and some drivers fail if you try to pass one
2832 if (Dbms() == dbmsDBASE)
f6bcfd97 2833 UserID = wxT("");
1e92909e 2834
23f681ec 2835 // Oracle user names may only be in uppercase, so force
1e92909e
GT
2836 // the name to uppercase
2837 if (Dbms() == dbmsORACLE)
2838 UserID = UserID.Upper();
2839
f6bcfd97 2840 if (wxStrcmp(UserID.c_str(),wxT("")) &&
1e92909e
GT
2841 Dbms() != dbmsMY_SQL &&
2842 Dbms() != dbmsACCESS)
2843 {
2844 retcode = SQLColumns(hstmt,
2845 NULL, 0, // All qualifiers
3ca6a5f0 2846 (UCHAR *) UserID.c_str(), SQL_NTS, // User specified
1e92909e
GT
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;
f6bcfd97 2866 tblNameSave = wxT("");
1e92909e
GT
2867 int cnt = 0;
2868
2869 while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
2870 {
f6bcfd97 2871 if (wxStrcmp(tblName,tblNameSave.c_str()))
1e92909e
GT
2872 {
2873 if (cnt)
2874 fputs("\n", fp);
2875 fputs("================================ ", fp);
2876 fputs("================================ ", fp);
2877 fputs("===================== ", fp);
2878 fputs("========= ", fp);
2879 fputs("=========\n", fp);
f6bcfd97
BP
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);
1e92909e
GT
2883 fputs("================================ ", fp);
2884 fputs("================================ ", fp);
2885 fputs("===================== ", fp);
2886 fputs("========= ", fp);
2887 fputs("=========\n", fp);
2888 tblNameSave = tblName;
2889 }
67e9aaa3
GT
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
1e92909e
GT
2898 outStr.sprintf("%-32s %-32s (%04d)%-15s %9d %9d\n",
2899 tblName, colName, sqlDataType, typeName, precision, length);
f6bcfd97 2900 if (fputs(outStr.c_str(), fp) == EOF)
1e92909e
GT
2901 {
2902 SQLFreeStmt(hstmt, SQL_CLOSE);
2903 fclose(fp);
2904 return(FALSE);
2905 }
2906 cnt++;
2907 }
1fc5dd6f 2908
1e92909e
GT
2909 if (retcode != SQL_NO_DATA_FOUND)
2910 DispAllErrors(henv, hdbc, hstmt);
1fc5dd6f 2911
1e92909e 2912 SQLFreeStmt(hstmt, SQL_CLOSE);
a2115c88 2913
1e92909e
GT
2914 fclose(fp);
2915 return(retcode == SQL_NO_DATA_FOUND);
1fc5dd6f 2916
f6bcfd97 2917} // wxDb::Catalog()
1fc5dd6f
JS
2918
2919
f6bcfd97 2920bool wxDb::TableExists(const char *tableName, const char *userID, const char *tablePath)
67e9aaa3
GT
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:
1e92909e
GT
2928 * userID == NULL ... UserID is ignored
2929 * userID == "" ... UserID set equal to 'this->uid'
2930 * userID != "" ... UserID set equal to 'userID'
67e9aaa3 2931 */
108106cf 2932{
1e92909e
GT
2933 wxString UserID;
2934 wxString TableName;
3ca6a5f0 2935
1e92909e 2936 assert(tableName && wxStrlen(tableName));
3ca6a5f0 2937
f6bcfd97 2938 if (Dbms() == dbmsDBASE)
1e92909e 2939 {
3ca6a5f0
BP
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;
1e92909e 2949 }
3ca6a5f0 2950
1e92909e
GT
2951 if (userID)
2952 {
2953 if (!wxStrlen(userID))
2954 UserID = uid;
2955 else
2956 UserID = userID;
2957 }
2958 else
2959 UserID = "";
3ca6a5f0 2960
23f681ec 2961 // Oracle user names may only be in uppercase, so force
1e92909e
GT
2962 // the name to uppercase
2963 if (Dbms() == dbmsORACLE)
2964 UserID = UserID.Upper();
3ca6a5f0 2965
1e92909e 2966 TableName = tableName;
23f681ec 2967 // Oracle table names are uppercase only, so force
1e92909e
GT
2968 // the name to uppercase just in case programmer forgot to do this
2969 if (Dbms() == dbmsORACLE)
2970 TableName = TableName.Upper();
3ca6a5f0 2971
1e92909e
GT
2972 SQLFreeStmt(hstmt, SQL_CLOSE);
2973 RETCODE retcode;
3ca6a5f0 2974
a8aa2258
GT
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
1e92909e 2977 if (wxStrcmp(UserID,"") &&
3ca6a5f0 2978 Dbms() != dbmsMY_SQL &&
a8aa2258
GT
2979 Dbms() != dbmsACCESS &&
2980 Dbms() != dbmsMS_SQL_SERVER)
1e92909e
GT
2981 {
2982 retcode = SQLTables(hstmt,
3ca6a5f0 2983 NULL, 0, // All qualifiers
a8aa2258 2984 (UCHAR *) UserID.c_str(), SQL_NTS, // Only tables owned by this user
f6bcfd97 2985 (UCHAR FAR *)TableName.c_str(), SQL_NTS,
3ca6a5f0 2986 NULL, 0); // All table types
1e92909e
GT
2987 }
2988 else
2989 {
2990 retcode = SQLTables(hstmt,
3ca6a5f0
BP
2991 NULL, 0, // All qualifiers
2992 NULL, 0, // All owners
f6bcfd97 2993 (UCHAR FAR *)TableName.c_str(), SQL_NTS,
3ca6a5f0 2994 NULL, 0); // All table types
1e92909e
GT
2995 }
2996 if (retcode != SQL_SUCCESS)
2997 return(DispAllErrors(henv, hdbc, hstmt));
3ca6a5f0 2998
1e92909e 2999 retcode = SQLFetch(hstmt);
a8aa2258 3000 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
1e92909e
GT
3001 {
3002 SQLFreeStmt(hstmt, SQL_CLOSE);
3003 return(DispAllErrors(henv, hdbc, hstmt));
3004 }
3ca6a5f0 3005
1e92909e 3006 SQLFreeStmt(hstmt, SQL_CLOSE);
108106cf 3007
3ca6a5f0
BP
3008 return(TRUE);
3009
f6bcfd97 3010} // wxDb::TableExists()
108106cf
JS
3011
3012
a8aa2258
GT
3013/********** wxDb::TablePrivileges() **********/
3014bool wxDb::TablePrivileges(const char *tableName, const char* priv,
3ca6a5f0
BP
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,
a8aa2258
GT
3055 NULL, 0, // Catalog
3056 NULL, 0, // Schema
3ca6a5f0 3057 (UCHAR FAR *)TableName.GetData(), SQL_NTS);
a8aa2258 3058
3ca6a5f0
BP
3059#ifdef DBDEBUG_CONSOLE
3060 fprintf(stderr ,"SQLTablePrivileges() returned %i \n",retcode);
3061#endif
3ca6a5f0 3062
a8aa2258
GT
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));
3ca6a5f0 3068
a8aa2258
GT
3069 if (SQLGetData(hstmt, 2, SQL_C_CHAR, (UCHAR*) result.tableOwner, 128, &cbRetVal) != SQL_SUCCESS)
3070 return(DispAllErrors(henv, hdbc, hstmt));
3ca6a5f0 3071
a8aa2258
GT
3072 if (SQLGetData(hstmt, 3, SQL_C_CHAR, (UCHAR*) result.tableName, 128, &cbRetVal) != SQL_SUCCESS)
3073 return(DispAllErrors(henv, hdbc, hstmt));
3ca6a5f0 3074
a8aa2258
GT
3075 if (SQLGetData(hstmt, 4, SQL_C_CHAR, (UCHAR*) result.grantor, 128, &cbRetVal) != SQL_SUCCESS)
3076 return(DispAllErrors(henv, hdbc, hstmt));
3ca6a5f0 3077
a8aa2258
GT
3078 if (SQLGetData(hstmt, 5, SQL_C_CHAR, (UCHAR*) result.grantee, 128, &cbRetVal) != SQL_SUCCESS)
3079 return(DispAllErrors(henv, hdbc, hstmt));
3ca6a5f0 3080
a8aa2258
GT
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));
3ca6a5f0
BP
3086
3087 retcode = SQLFetch(hstmt);
3088 while (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
3089 {
3090#ifdef DBDEBUG_CONSOLE
a8aa2258
GT
3091 fprintf(stderr,"Scanning %s privilege on table %s.%s granted by %s to %s\n",
3092 result.privilege,result.tableOwner,result.tableName,
3ca6a5f0
BP
3093 result.grantor, result.grantee);
3094#endif
a8aa2258
GT
3095 if (UserID.IsSameAs(result.tableOwner,false))
3096 {
3097 SQLFreeStmt(hstmt, SQL_CLOSE);
3ca6a5f0 3098 return TRUE;
a8aa2258 3099 }
3ca6a5f0
BP
3100
3101 if (UserID.IsSameAs(result.grantee,false) &&
a8aa2258
GT
3102 !wxStrcmp(result.privilege,priv))
3103 {
3104 SQLFreeStmt(hstmt, SQL_CLOSE);
3ca6a5f0 3105 return TRUE;
a8aa2258 3106 }
3ca6a5f0 3107
a8aa2258
GT
3108 if (!wxStrcmp(result.grantee,curRole) &&
3109 !wxStrcmp(result.privilege,priv))
3110 {
3111 SQLFreeStmt(hstmt, SQL_CLOSE);
3ca6a5f0 3112 return TRUE;
a8aa2258 3113 }
3ca6a5f0
BP
3114
3115 retcode = SQLFetch(hstmt);
3116 }
3117
a8aa2258 3118 SQLFreeStmt(hstmt, SQL_CLOSE);
3ca6a5f0
BP
3119 return FALSE;
3120
a8aa2258 3121} // wxDb::TablePrivileges
3ca6a5f0
BP
3122
3123
f6bcfd97
BP
3124/********** wxDb::SetSqlLogging() **********/
3125bool wxDb::SetSqlLogging(wxDbSqlLogState state, const char *filename, bool append)
1fc5dd6f 3126{
ea5d599d
GT
3127 assert(state == sqlLogON || state == sqlLogOFF);
3128 assert(state == sqlLogOFF || filename);
1e92909e 3129
ea5d599d
GT
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 }
1e92909e 3148
ea5d599d
GT
3149 sqlLogState = state;
3150 return(TRUE);
1fc5dd6f 3151
f6bcfd97 3152} // wxDb::SetSqlLogging()
1fc5dd6f
JS
3153
3154
f6bcfd97
BP
3155/********** wxDb::WriteSqlLog() **********/
3156bool wxDb::WriteSqlLog(const wxChar *logMsg)
1fc5dd6f 3157{
1e92909e 3158 assert(logMsg);
1fc5dd6f 3159
1e92909e
GT
3160 if (fpSqlLog == 0 || sqlLogState == sqlLogOFF)
3161 return(FALSE);
1fc5dd6f 3162
1e92909e
GT
3163 if (fputs("\n", fpSqlLog) == EOF) return(FALSE);
3164 if (fputs(logMsg, fpSqlLog) == EOF) return(FALSE);
3165 if (fputs("\n", fpSqlLog) == EOF) return(FALSE);
1fc5dd6f 3166
1e92909e 3167 return(TRUE);
1fc5dd6f 3168
f6bcfd97 3169} // wxDb::WriteSqlLog()
1fc5dd6f
JS
3170
3171
f6bcfd97
BP
3172/********** wxDb::Dbms() **********/
3173wxDBMS wxDb::Dbms(void)
a2115c88
GT
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 *
f6bcfd97 3180 * If you find an inconsistency between the wxDb class and a specific database
a2115c88
GT
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
1e92909e 3189 * - Currently the only database supported by the class to support VIEWS
a2115c88
GT
3190 *
3191 * DBASE
1e92909e
GT
3192 * - Does not support the SQL_TIMESTAMP structure
3193 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
fdc03678
GT
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.
1e92909e
GT
3196 * - Table names can only be 8 characters long
3197 * - Column names can only be 10 characters long
a2115c88
GT
3198 *
3199 * SYBASE (all)
1e92909e
GT
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
a2115c88
GT
3204 *
3205 * SYBASE (Enterprise)
1e92909e 3206 * - If a column is part of the Primary Key, the column cannot be NULL
3ca6a5f0 3207 * - Maximum row size is somewhere in the neighborhood of 1920 bytes
a2115c88
GT
3208 *
3209 * MY_SQL
1e92909e
GT
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
3ca6a5f0
BP
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
a2115c88
GT
3216 *
3217 * POSTGRES
23f681ec 3218 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
3ca6a5f0 3219 * - Does not support sub-queries in SQL statements
a2115c88 3220 *
a8aa2258
GT
3221 * DB2
3222 * - Primary keys must be declared as NOT NULL
3223 *
a2115c88 3224 */
a2115c88 3225{
a8aa2258
GT
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
1e92909e 3232 wxChar baseName[25+1];
1e92909e 3233 wxStrncpy(baseName,dbInf.dbmsName,25);
3ca6a5f0 3234 baseName[25] = 0;
f6bcfd97
BP
3235
3236 // BJO 20000428 : add support for Virtuoso
3237 if (!wxStricmp(dbInf.dbmsName,"OpenLink Virtuoso VDBMS"))
a8aa2258 3238 return((wxDBMS)(dbmsType = dbmsVIRTUOSO));
f6bcfd97 3239
1e92909e 3240 if (!wxStricmp(dbInf.dbmsName,"Adaptive Server Anywhere"))
a8aa2258
GT
3241 return((wxDBMS)(dbmsType = dbmsSYBASE_ASA));
3242
f6bcfd97
BP
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?
3ca6a5f0 3246 // OpenLink driver name is OLOD3032.DLL for msw and oplodbc.so for unix
f6bcfd97 3247 if (!wxStricmp(dbInf.dbmsName,"SQL Server"))
3ca6a5f0 3248 {
f6bcfd97
BP
3249 if (!wxStrncmp(dbInf.driverName, "oplodbc", 7) ||
3250 !wxStrncmp(dbInf.driverName, "OLOD", 4))
a8aa2258
GT
3251 return ((wxDBMS)(dbmsMS_SQL_SERVER));
3252 else
3253 return ((wxDBMS)(dbmsType = dbmsSYBASE_ASE));
3ca6a5f0 3254 }
f6bcfd97 3255
1e92909e 3256 if (!wxStricmp(dbInf.dbmsName,"Microsoft SQL Server"))
a8aa2258 3257 return((wxDBMS)(dbmsType = dbmsMS_SQL_SERVER));
1e92909e 3258 if (!wxStricmp(dbInf.dbmsName,"MySQL"))
a8aa2258 3259 return((wxDBMS)(dbmsType = dbmsMY_SQL));
1e92909e 3260 if (!wxStricmp(dbInf.dbmsName,"PostgreSQL")) // v6.5.0
a8aa2258 3261 return((wxDBMS)(dbmsType = dbmsPOSTGRES));
1e92909e
GT
3262
3263 baseName[8] = 0;
3264 if (!wxStricmp(baseName,"Informix"))
a8aa2258 3265 return((wxDBMS)(dbmsType = dbmsINFORMIX));
1e92909e
GT
3266
3267 baseName[6] = 0;
3268 if (!wxStricmp(baseName,"Oracle"))
a8aa2258 3269 return((wxDBMS)(dbmsType = dbmsORACLE));
1e92909e 3270 if (!wxStricmp(dbInf.dbmsName,"ACCESS"))
a8aa2258 3271 return((wxDBMS)(dbmsType = dbmsACCESS));
1e92909e 3272 if (!wxStricmp(dbInf.dbmsName,"MySQL"))
a8aa2258 3273 return((wxDBMS)(dbmsType = dbmsMY_SQL));
f6bcfd97 3274 if (!wxStricmp(baseName,"Sybase"))
a8aa2258 3275 return((wxDBMS)(dbmsType = dbmsSYBASE_ASE));
1e92909e
GT
3276
3277 baseName[5] = 0;
3278 if (!wxStricmp(baseName,"DBASE"))
a8aa2258 3279 return((wxDBMS)(dbmsType = dbmsDBASE));
1e92909e 3280
a8aa2258
GT
3281 baseName[3] = 0;
3282 if (!wxStricmp(baseName,"DB2"))
3283 return((wxDBMS)(dbmsType = dbmsDBASE));
3284
3285 return((wxDBMS)(dbmsType = dbmsUNIDENTIFIED));
3ca6a5f0 3286
f6bcfd97 3287} // wxDb::Dbms()
a2115c88
GT
3288
3289
fdc03678 3290/********** wxDbGetConnection() **********/
f6bcfd97 3291wxDb WXDLLEXPORT *wxDbGetConnection(wxDbConnectInf *pDbConfig, bool FwdOnlyCursors)
108106cf 3292{
fdc03678 3293 wxDbList *pList;
1e92909e 3294
a8aa2258
GT
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
1e92909e
GT
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.
3ca6a5f0
BP
3308 if (pList->Free &&
3309 (pList->PtrDb->FwdOnlyCursors() == FwdOnlyCursors) &&
3310 (!wxStrcmp(pDbConfig->Dsn, pList->Dsn))) // Found a free connection
1e92909e
GT
3311 {
3312 pList->Free = FALSE;
3313 return(pList->PtrDb);
3314 }
a8aa2258
GT
3315
3316 if (!wxStrcmp(pDbConfig->Dsn, pList->Dsn) &&
3317 !wxStrcmp(pDbConfig->Uid, pList->Uid) &&
3318 !wxStrcmp(pDbConfig->AuthStr, pList->AuthStr))
3319 matchingDbConnection = pList->PtrDb;
1e92909e
GT
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
fdc03678 3329 pList->PtrNext = new wxDbList;
1e92909e
GT
3330 pList->PtrNext->PtrPrev = pList;
3331 pList = pList->PtrNext;
3332 }
3ca6a5f0 3333 else // Empty list
1e92909e
GT
3334 {
3335 // Create the first node on the list
fdc03678 3336 pList = PtrBegDbList = new wxDbList;
1e92909e
GT
3337 pList->PtrPrev = 0;
3338 }
3339
3340 // Initialize new node in the linked list
3341 pList->PtrNext = 0;
3342 pList->Free = FALSE;
fdc03678 3343 wxStrcpy(pList->Dsn, pDbConfig->Dsn);
a8aa2258
GT
3344 wxStrcpy(pList->Uid, pDbConfig->Uid);
3345 wxStrcpy(pList->AuthStr, pDbConfig->AuthStr);
3346
f6bcfd97 3347 pList->PtrDb = new wxDb(pDbConfig->Henv,FwdOnlyCursors);
1e92909e 3348
a8aa2258
GT
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
1e92909e 3356 // Connect to the datasource
a8aa2258 3357 if (opened)
1e92909e 3358 {
f6bcfd97 3359 pList->PtrDb->SetSqlLogging(SQLLOGstate,SQLLOGfn,TRUE);
1e92909e
GT
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
f6bcfd97
BP
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
1e92909e
GT
3371 delete pList; // Deletes the linked list object
3372 return(0);
3373 }
108106cf 3374
fdc03678 3375} // wxDbGetConnection()
108106cf 3376
67e9aaa3 3377
fdc03678 3378/********** wxDbFreeConnection() **********/
f6bcfd97 3379bool WXDLLEXPORT wxDbFreeConnection(wxDb *pDb)
108106cf 3380{
fdc03678 3381 wxDbList *pList;
108106cf 3382
1e92909e
GT
3383 // Scan the linked list searching for the database connection
3384 for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
3385 {
3ca6a5f0
BP
3386 if (pList->PtrDb == pDb) // Found it, now free it!!!
3387 return (pList->Free = TRUE);
1e92909e 3388 }
108106cf 3389
1e92909e
GT
3390 // Never found the database object, return failure
3391 return(FALSE);
108106cf 3392
fdc03678 3393} // wxDbFreeConnection()
108106cf 3394
67e9aaa3 3395
fdc03678
GT
3396/********** wxDbCloseConnections() **********/
3397void WXDLLEXPORT wxDbCloseConnections(void)
108106cf 3398{
fdc03678 3399 wxDbList *pList, *pNext;
23f681ec 3400
1e92909e
GT
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
f6bcfd97
BP
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
1e92909e
GT
3408 delete pList; // Deletes the linked list object
3409 }
3410
3411 // Mark the list as empty
3412 PtrBegDbList = 0;
108106cf 3413
fdc03678 3414} // wxDbCloseConnections()
108106cf 3415
67e9aaa3 3416
fdc03678
GT
3417/********** wxDbNumberConnectionsInUse() **********/
3418int WXDLLEXPORT wxDbConnectionsInUse(void)
108106cf 3419{
fdc03678 3420 wxDbList *pList;
1e92909e 3421 int cnt = 0;
108106cf 3422
1e92909e
GT
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 }
108106cf 3429
1e92909e 3430 return(cnt);
108106cf 3431
fdc03678 3432} // wxDbConnectionsInUse()
108106cf 3433
67e9aaa3 3434
fdc03678 3435/********** wxDbSqlLog() **********/
f6bcfd97 3436bool wxDbSqlLog(wxDbSqlLogState state, const wxChar *filename)
a2115c88 3437{
1e92909e 3438 bool append = FALSE;
fdc03678 3439 wxDbList *pList;
a2115c88 3440
1e92909e
GT
3441 for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
3442 {
f6bcfd97 3443 if (!pList->PtrDb->SetSqlLogging(state,filename,append))
1e92909e
GT
3444 return(FALSE);
3445 append = TRUE;
3446 }
a2115c88 3447
1e92909e 3448 SQLLOGstate = state;
3ca6a5f0 3449 SQLLOGfn = filename;
a2115c88 3450
1e92909e 3451 return(TRUE);
a2115c88 3452
fdc03678 3453} // wxDbSqlLog()
a2115c88 3454
67e9aaa3 3455
a8aa2258 3456#if 0
fdc03678
GT
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.
a8aa2258
GT
3463 *
3464 * ODBC driver must be ODBC 3.0 compliant to use this function
fdc03678
GT
3465 */
3466{
3467 int result = FALSE;
3468
a8aa2258
GT
3469//!!!! ONLY FUNCTIONAL UNDER MSW with VC6 !!!!
3470#ifdef __VISUALC__
fdc03678
GT
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,
f6bcfd97 3496 driverName, setupStr.c_str());
fdc03678 3497
a8aa2258 3498 if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
fdc03678
GT
3499 {
3500 // check for errors caused by ConfigDSN based functions
3501 DWORD retcode = 0;
3502 WORD cb;
a8aa2258 3503 wxChar errMsg[SQL_MAX_MESSAGE_LENGTH];
fdc03678
GT
3504 errMsg[0] = '\0';
3505
a8aa2258
GT
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);
fdc03678
GT
3508 if (retcode)
3509 {
fdc03678 3510#ifdef DBDEBUG_CONSOLE
f6bcfd97
BP
3511 // When run in console mode, use standard out to display errors.
3512 cout << errMsg << endl;
a8aa2258 3513 cout << wxT("Press any key to continue...") << endl;
f6bcfd97 3514 getchar();
fdc03678 3515#endif // DBDEBUG_CONSOLE
fdc03678
GT
3516
3517#ifdef __WXDEBUG__
f6bcfd97 3518 wxLogDebug(errMsg,wxT("ODBC DEBUG MESSAGE"));
fdc03678
GT
3519#endif // __WXDEBUG__
3520 }
3521 }
3522 else
3523 result = TRUE;
a8aa2258
GT
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
fdc03678 3527#ifdef __WXDEBUG__
a8aa2258 3528 wxLogDebug("wxDbCreateDataSource() not available except under VC++/MSW",wxT("ODBC DEBUG MESSAGE"));
fdc03678 3529#endif
a8aa2258
GT
3530 result = FALSE;
3531#endif // __VISUALC__
fdc03678
GT
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,
1e92909e 3541 UWORD direction)
67e9aaa3
GT
3542/*
3543 * Dsn and DsDesc will contain the data source name and data source
3544 * description upon return
3545 */
108106cf 3546{
1e92909e 3547 SWORD cb1,cb2;
108106cf 3548
1e92909e
GT
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);
108106cf 3554
fdc03678
GT
3555} // wxDbGetDataSource()
3556
3557
3558// Change this to 0 to remove use of all deprecated functions
f6bcfd97 3559#if wxODBC_BACKWARD_COMPATABILITY
fdc03678
GT
3560/********************************************************************
3561 ********************************************************************
3562 *
3563 * The following functions are all DEPRECATED and are included for
3564 * backward compatability reasons only
3565 *
3566 ********************************************************************
3567 ********************************************************************/
f6bcfd97 3568bool SqlLog(sqlLog state, const wxChar *filename)
fdc03678 3569{
f6bcfd97 3570 return wxDbSqlLog((enum wxDbSqlLogState)state, filename);
fdc03678
GT
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() *****/
f6bcfd97 3579wxDb WXDLLEXPORT *GetDbConnection(DbStuff *pDbStuff, bool FwdOnlyCursors)
fdc03678
GT
3580{
3581 return wxDbGetConnection((wxDbConnectInf *)pDbStuff, FwdOnlyCursors);
3582}
3583/***** DEPRECATED: use wxDbFreeConnection() *****/
f6bcfd97 3584bool WXDLLEXPORT FreeDbConnection(wxDb *pDb)
fdc03678
GT
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
108106cf 3601#endif
1fc5dd6f 3602 // wxUSE_ODBC