]> git.saurik.com Git - wxWidgets.git/blob - src/common/db.cpp
::bindInsertParams() was leaving some column bindings uninitialized if the column...
[wxWidgets.git] / src / common / db.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: db.cpp
3 // Purpose: Implementation of the wxDB class. The wxDB class represents a connection
4 // to an ODBC data source. The wxDB class allows operations on the data
5 // source such as opening and closing the data source.
6 // Author: Doug Card
7 // Modified by: George Tasker
8 // Mods: Dec, 1998:
9 // -Added support for SQL statement logging and database cataloging
10 // Mods: April, 1999
11 // -Added QUERY_ONLY mode support to reduce default number of cursors
12 // -Added additional SQL logging code
13 // -Added DEBUG-ONLY tracking of wxTable objects to detect orphaned DB connections
14 // -Set ODBC option to only read committed writes to the DB so all
15 // databases operate the same in that respect
16 // Created: 9.96
17 // RCS-ID: $Id$
18 // Copyright: (c) 1996 Remstar International, Inc.
19 // Licence: wxWindows licence, plus:
20 // Notice: This class library and its intellectual design are free of charge for use,
21 // modification, enhancement, debugging under the following conditions:
22 // 1) These classes may only be used as part of the implementation of a
23 // wxWindows-based application
24 // 2) All enhancements and bug fixes are to be submitted back to the wxWindows
25 // user groups free of all charges for use with the wxWindows library.
26 // 3) These classes may not be distributed as part of any other class library,
27 // DLL, text (written or electronic), other than a complete distribution of
28 // the wxWindows GUI development toolkit.
29 ///////////////////////////////////////////////////////////////////////////////
30
31 /*
32 // SYNOPSIS START
33 // SYNOPSIS STOP
34 */
35
36 #include "wx/wxprec.h"
37
38 // Use this line for wxWindows v1.x
39 //#include "wx_ver.h"
40 // Use this line for wxWindows v2.x
41 #include "wx/version.h"
42
43 #if wxMAJOR_VERSION == 2
44 #ifdef __GNUG__
45 #pragma implementation "db.h"
46 #endif
47 #endif
48
49 #ifdef DBDEBUG_CONSOLE
50 #include <iostream.h>
51 #endif
52
53 #ifdef __BORLANDC__
54 #pragma hdrstop
55 #endif //__BORLANDC__
56
57 #if wxMAJOR_VERSION == 2
58 #ifndef WX_PRECOMP
59 #include "wx/string.h"
60 #include "wx/object.h"
61 #include "wx/list.h"
62 #include "wx/utils.h"
63 #include "wx/msgdlg.h"
64 #endif
65 #include "wx/filefn.h"
66 #include "wx/wxchar.h"
67 #endif
68
69 #if wxMAJOR_VERSION == 1
70 # if defined(wx_msw) || defined(wx_x)
71 # ifdef WX_PRECOMP
72 # include "wx_prec.h"
73 # else
74 # include "wx.h"
75 # endif
76 # endif
77 # define wxUSE_ODBC 1
78 #endif
79
80 #if wxUSE_ODBC
81
82 #include <stdio.h>
83 #include <string.h>
84 #include <assert.h>
85 #include <stdlib.h>
86 #include <ctype.h>
87
88 #if wxMAJOR_VERSION == 1
89 #include "db.h"
90 #elif wxMAJOR_VERSION == 2
91 #include "wx/db.h"
92 #endif
93
94 DbList WXDLLEXPORT *PtrBegDbList = 0;
95
96 #ifdef __WXDEBUG__
97 extern wxList TablesInUse;
98 #endif
99
100 // SQL Log defaults to be used by GetDbConnection
101 enum sqlLog SQLLOGstate = sqlLogOFF;
102
103 char SQLLOGfn[DB_PATH_MAX+1] = "sqllog.txt";
104
105 // The wxDB::errorList is copied to this variable when the wxDB object
106 // is closed. This way, the error list is still available after the
107 // database object is closed. This is necessary if the database
108 // connection fails so the calling application can show the operator
109 // why the connection failed. Note: as each wxDB object is closed, it
110 // will overwrite the errors of the previously destroyed wxDB object in
111 // this variable.
112 char DBerrorList[DB_MAX_ERROR_HISTORY][DB_MAX_ERROR_MSG_LEN];
113
114
115 /********** wxColFor Constructor **********/
116 wxColFor::wxColFor()
117 {
118 i_Nation = 0; // 0=EU, 1=UK, 2=International, 3=US
119 s_Field = "";
120 for (int i=0;i<7;i++)
121 {
122 s_Format[i] = "";
123 s_Menge[i] = "";
124 i_Menge[i] = 0;
125 }
126 Format(1,DB_DATA_TYPE_VARCHAR,0,0,0); // the Function that does the work
127 } // wxColFor::wxColFor()
128
129
130 wxColFor::~wxColFor()
131 {
132 } // wxColFor::~wxColFor()
133
134
135 int wxColFor::Format(int Nation,int dbDataType,SWORD sqlDataType,short columnSize,short decimalDigits)
136 {
137 // ----------------------------------------------------------------------------------------
138 // -- 19991224 : mj10777@gmx.net : Create
139 // There is still a lot of work to do here, but it is a start
140 // It handles all the basic data-types that I have run into up to now
141 // The main work will have be with Dates and float Formatting (US 1,000.00 ; EU 1.000,00)
142 // There are wxWindow plans for locale support and the new wxDateTime.
143 // - if they define some constants (wxEUROPEAN) that can be gloably used,
144 // they should be used here.
145 // ----------------------------------------------------------------------------------------
146 // There should also be a Function to scan in a string to fill the variable
147 // ----------------------------------------------------------------------------------------
148 wxString Temp0;
149 i_Nation = Nation; // 0 = timestamp , 1=EU, 2=UK, 3=International, 4=US
150 i_dbDataType = dbDataType;
151 i_sqlDataType = sqlDataType;
152 s_Field.Printf("%s%d",s_Menge[1].c_str(),i_Menge[1]); // OK for VARCHAR, INTEGER and FLOAT
153 if (i_dbDataType == 0) // Filter unsupported dbDataTypes
154 {
155 if ((i_sqlDataType == SQL_VARCHAR) || (i_sqlDataType == SQL_LONGVARCHAR))
156 i_dbDataType = DB_DATA_TYPE_VARCHAR;
157 if (i_sqlDataType == SQL_C_DATE)
158 i_dbDataType = DB_DATA_TYPE_DATE;
159 if (i_sqlDataType == SQL_C_BIT)
160 i_dbDataType = DB_DATA_TYPE_INTEGER;
161 if (i_sqlDataType == SQL_NUMERIC)
162 i_dbDataType = DB_DATA_TYPE_VARCHAR;
163 if (i_sqlDataType == SQL_REAL)
164 i_dbDataType = DB_DATA_TYPE_FLOAT;
165 }
166 if ((i_dbDataType == DB_DATA_TYPE_INTEGER) && (i_sqlDataType == SQL_C_DOUBLE))
167 { // DBASE Numeric
168 i_dbDataType = DB_DATA_TYPE_FLOAT;
169 }
170 switch(i_dbDataType) // -A-> Still a lot of proper formatting to do
171 {
172 case DB_DATA_TYPE_VARCHAR:
173 s_Field = "%s"; //
174 break;
175 case DB_DATA_TYPE_INTEGER:
176 s_Field = "%d"; //
177 break;
178 case DB_DATA_TYPE_FLOAT:
179 if (decimalDigits == 0)
180 decimalDigits = 2;
181 Temp0 = "%";
182 Temp0.Printf("%s%d.%d",Temp0.c_str(),columnSize,decimalDigits);
183 s_Field.Printf("%sf",Temp0.c_str()); //
184 break;
185 case DB_DATA_TYPE_DATE:
186 if (i_Nation == 0) // timestamp YYYY-MM-DD HH:MM:SS.SSS (tested for SYBASE)
187 {
188 s_Field = "%04d-%02d-%02d %02d:%02d:%02d.%03d";
189 }
190 if (i_Nation == 1) // European DD.MM.YYYY HH:MM:SS.SSS
191 {
192 s_Field = "%02d.%02d.%04d %02d:%02d:%02d.%03d";
193 }
194 if (i_Nation == 2) // UK DD/MM/YYYY HH:MM:SS.SSS
195 {
196 s_Field = "%02d/%02d/%04d %02d:%02d:%02d.%03d";
197 }
198 if (i_Nation == 3) // International YYYY-MM-DD HH:MM:SS.SSS
199 {
200 s_Field = "%04d-%02d-%02d %02d:%02d:%02d.%03d";
201 }
202 if (i_Nation == 4) // US MM/DD/YYYY HH:MM:SS.SSS
203 {
204 s_Field = "%02d/%02d/%04d %02d:%02d:%02d.%03d";
205 }
206 break;
207 default:
208 s_Field.Printf("-E-> unknown Format(%d)-sql(%d)",dbDataType,sqlDataType); //
209 break;
210 };
211 return TRUE;
212 } // wxColFor::Format()
213
214
215 /********** wxDB Constructor **********/
216 wxDB::wxDB(HENV &aHenv, bool FwdOnlyCursors)
217 {
218 int i;
219
220 fpSqlLog = 0; // Sql Log file pointer
221 sqlLogState = sqlLogOFF; // By default, logging is turned off
222 nTables = 0;
223
224 wxStrcpy(sqlState,"");
225 wxStrcpy(errorMsg,"");
226 nativeError = cbErrorMsg = 0;
227 for (i = 0; i < DB_MAX_ERROR_HISTORY; i++)
228 wxStrcpy(errorList[i], "");
229
230 // Init typeInf structures
231 wxStrcpy(typeInfVarchar.TypeName,"");
232 typeInfVarchar.FsqlType = 0;
233 typeInfVarchar.Precision = 0;
234 typeInfVarchar.CaseSensitive = 0;
235 typeInfVarchar.MaximumScale = 0;
236
237 wxStrcpy(typeInfInteger.TypeName,"");
238 typeInfInteger.FsqlType = 0;
239 typeInfInteger.Precision = 0;
240 typeInfInteger.CaseSensitive = 0;
241 typeInfInteger.MaximumScale = 0;
242
243 wxStrcpy(typeInfFloat.TypeName,"");
244 typeInfFloat.FsqlType = 0;
245 typeInfFloat.Precision = 0;
246 typeInfFloat.CaseSensitive = 0;
247 typeInfFloat.MaximumScale = 0;
248
249 wxStrcpy(typeInfDate.TypeName,"");
250 typeInfDate.FsqlType = 0;
251 typeInfDate.Precision = 0;
252 typeInfDate.CaseSensitive = 0;
253 typeInfDate.MaximumScale = 0;
254
255 // Error reporting is turned OFF by default
256 silent = TRUE;
257
258 // Copy the HENV into the db class
259 henv = aHenv;
260 fwdOnlyCursors = FwdOnlyCursors;
261
262 // Allocate a data source connection handle
263 if (SQLAllocConnect(henv, &hdbc) != SQL_SUCCESS)
264 DispAllErrors(henv);
265
266 // Initialize the db status flag
267 DB_STATUS = 0;
268
269 // Mark database as not open as of yet
270 dbIsOpen = FALSE;
271
272 } // wxDB::wxDB()
273
274
275 /********** wxDB::Open() **********/
276 bool wxDB::Open(char *Dsn, char *Uid, char *AuthStr)
277 {
278 assert(Dsn && wxStrlen(Dsn));
279 dsn = Dsn;
280 uid = Uid;
281 authStr = AuthStr;
282
283 RETCODE retcode;
284
285 if (!FwdOnlyCursors())
286 {
287 // Specify that the ODBC cursor library be used, if needed. This must be
288 // specified before the connection is made.
289 retcode = SQLSetConnectOption(hdbc, SQL_ODBC_CURSORS, SQL_CUR_USE_IF_NEEDED);
290
291 #ifdef DBDEBUG_CONSOLE
292 if (retcode == SQL_SUCCESS)
293 cout << "SQLSetConnectOption(CURSOR_LIB) successful" << endl;
294 else
295 cout << "SQLSetConnectOption(CURSOR_LIB) failed" << endl;
296 #endif
297 }
298
299 // Connect to the data source
300 retcode = SQLConnect(hdbc, (UCHAR FAR *) Dsn, SQL_NTS,
301 (UCHAR FAR *) Uid, SQL_NTS,
302 (UCHAR FAR *) AuthStr,SQL_NTS);
303 if (retcode == SQL_SUCCESS_WITH_INFO)
304 DispAllErrors(henv, hdbc);
305 else if (retcode != SQL_SUCCESS)
306 return(DispAllErrors(henv, hdbc));
307
308 /*
309 If using Intersolv branded ODBC drivers, this is the place where you would substitute
310 your branded driver license information
311
312 SQLSetConnectOption(hdbc, 1041, (UDWORD) "");
313 SQLSetConnectOption(hdbc, 1042, (UDWORD) "");
314 */
315
316 // Mark database as open
317 dbIsOpen = TRUE;
318
319 // Allocate a statement handle for the database connection
320 if (SQLAllocStmt(hdbc, &hstmt) != SQL_SUCCESS)
321 return(DispAllErrors(henv, hdbc));
322
323 // Set Connection Options
324 if (! setConnectionOptions())
325 return(FALSE);
326
327 // Query the data source for inf. about itself
328 if (! getDbInfo())
329 return(FALSE);
330
331 // Query the data source regarding data type information
332
333 //
334 // The way I determined which SQL data types to use was by calling SQLGetInfo
335 // for all of the possible SQL data types to see which ones were supported. If
336 // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
337 // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
338 // types I've selected below will not alway's be what we want. These are just
339 // what happened to work against an Oracle 7/Intersolv combination. The following is
340 // a complete list of the results I got back against the Oracle 7 database:
341 //
342 // SQL_BIGINT SQL_NO_DATA_FOUND
343 // SQL_BINARY SQL_NO_DATA_FOUND
344 // SQL_BIT SQL_NO_DATA_FOUND
345 // SQL_CHAR type name = 'CHAR', Precision = 255
346 // SQL_DATE SQL_NO_DATA_FOUND
347 // SQL_DECIMAL type name = 'NUMBER', Precision = 38
348 // SQL_DOUBLE type name = 'NUMBER', Precision = 15
349 // SQL_FLOAT SQL_NO_DATA_FOUND
350 // SQL_INTEGER SQL_NO_DATA_FOUND
351 // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
352 // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
353 // SQL_NUMERIC SQL_NO_DATA_FOUND
354 // SQL_REAL SQL_NO_DATA_FOUND
355 // SQL_SMALLINT SQL_NO_DATA_FOUND
356 // SQL_TIME SQL_NO_DATA_FOUND
357 // SQL_TIMESTAMP type name = 'DATE', Precision = 19
358 // SQL_VARBINARY type name = 'RAW', Precision = 255
359 // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
360 // =====================================================================
361 // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
362 //
363 // SQL_VARCHAR type name = 'TEXT', Precision = 255
364 // SQL_TIMESTAMP type name = 'DATETIME'
365 // SQL_DECIMAL SQL_NO_DATA_FOUND
366 // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
367 // SQL_FLOAT SQL_NO_DATA_FOUND
368 // SQL_REAL type name = 'SINGLE', Precision = 7
369 // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
370 // SQL_INTEGER type name = 'LONG', Precision = 10
371
372 // VARCHAR = Variable length character string
373 if (! getDataTypeInfo(SQL_VARCHAR, typeInfVarchar))
374 if (! getDataTypeInfo(SQL_CHAR, typeInfVarchar))
375 return(FALSE);
376 else
377 typeInfVarchar.FsqlType = SQL_CHAR;
378 else
379 typeInfVarchar.FsqlType = SQL_VARCHAR;
380
381 // Float
382 if (! getDataTypeInfo(SQL_DOUBLE, typeInfFloat))
383 if (! getDataTypeInfo(SQL_REAL, typeInfFloat))
384 if (! getDataTypeInfo(SQL_FLOAT, typeInfFloat))
385 if (! getDataTypeInfo(SQL_DECIMAL, typeInfFloat))
386 if (! getDataTypeInfo(SQL_NUMERIC, typeInfFloat))
387 return(FALSE);
388 else
389 typeInfFloat.FsqlType = SQL_NUMERIC;
390 else
391 typeInfFloat.FsqlType = SQL_DECIMAL;
392 else
393 typeInfFloat.FsqlType = SQL_FLOAT;
394 else
395 typeInfFloat.FsqlType = SQL_REAL;
396 else
397 typeInfFloat.FsqlType = SQL_DOUBLE;
398
399 // Integer
400 if (! getDataTypeInfo(SQL_INTEGER, typeInfInteger))
401 // If SQL_INTEGER is not supported, use the floating point
402 // data type to store integers as well as floats
403 if (! getDataTypeInfo(typeInfFloat.FsqlType, typeInfInteger))
404 return(FALSE);
405 else
406 typeInfInteger.FsqlType = typeInfFloat.FsqlType;
407 else
408 typeInfInteger.FsqlType = SQL_INTEGER;
409
410 // Date/Time
411 if (Dbms() != dbmsDBASE)
412 {
413 if (! getDataTypeInfo(SQL_TIMESTAMP, typeInfDate))
414 return(FALSE);
415 else
416 typeInfDate.FsqlType = SQL_TIMESTAMP;
417 }
418 else
419 {
420 if (! getDataTypeInfo(SQL_DATE, typeInfDate))
421 return(FALSE);
422 else
423 typeInfDate.FsqlType = SQL_DATE;
424 }
425
426 #ifdef DBDEBUG_CONSOLE
427 cout << "VARCHAR DATA TYPE: " << typeInfVarchar.TypeName << endl;
428 cout << "INTEGER DATA TYPE: " << typeInfInteger.TypeName << endl;
429 cout << "FLOAT DATA TYPE: " << typeInfFloat.TypeName << endl;
430 cout << "DATE DATA TYPE: " << typeInfDate.TypeName << endl;
431 cout << endl;
432 #endif
433
434 // Completed Successfully
435 return(TRUE);
436
437 } // wxDB::Open()
438
439
440 /********** wxDB::setConnectionOptions() **********/
441 bool wxDB::setConnectionOptions(void)
442 /*
443 * NOTE: The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
444 */
445 {
446 SQLSetConnectOption(hdbc, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF);
447 SQLSetConnectOption(hdbc, SQL_OPT_TRACE, SQL_OPT_TRACE_OFF);
448
449 // Display the connection options to verify them
450 #ifdef DBDEBUG_CONSOLE
451 long l;
452 cout << ">>>>> CONNECTION OPTIONS <<<<<<" << endl;
453
454 if (SQLGetConnectOption(hdbc, SQL_AUTOCOMMIT, &l) != SQL_SUCCESS)
455 return(DispAllErrors(henv, hdbc));
456 cout << "AUTOCOMMIT: " << (l == SQL_AUTOCOMMIT_OFF ? "OFF" : "ON") << endl;
457
458 if (SQLGetConnectOption(hdbc, SQL_ODBC_CURSORS, &l) != SQL_SUCCESS)
459 return(DispAllErrors(henv, hdbc));
460 cout << "ODBC CURSORS: ";
461 switch(l)
462 {
463 case(SQL_CUR_USE_IF_NEEDED):
464 cout << "SQL_CUR_USE_IF_NEEDED";
465 break;
466 case(SQL_CUR_USE_ODBC):
467 cout << "SQL_CUR_USE_ODBC";
468 break;
469 case(SQL_CUR_USE_DRIVER):
470 cout << "SQL_CUR_USE_DRIVER";
471 break;
472 }
473 cout << endl;
474
475 if (SQLGetConnectOption(hdbc, SQL_OPT_TRACE, &l) != SQL_SUCCESS)
476 return(DispAllErrors(henv, hdbc));
477 cout << "TRACING: " << (l == SQL_OPT_TRACE_OFF ? "OFF" : "ON") << endl;
478
479 cout << endl;
480 #endif
481
482 // Completed Successfully
483 return(TRUE);
484
485 } // wxDB::setConnectionOptions()
486
487
488 /********** wxDB::getDbInfo() **********/
489 bool wxDB::getDbInfo(void)
490 {
491 SWORD cb;
492 RETCODE retcode;
493
494 if (SQLGetInfo(hdbc, SQL_SERVER_NAME, (UCHAR*) dbInf.serverName, 80, &cb) != SQL_SUCCESS)
495 return(DispAllErrors(henv, hdbc));
496
497 if (SQLGetInfo(hdbc, SQL_DATABASE_NAME, (UCHAR*) dbInf.databaseName, 128, &cb) != SQL_SUCCESS)
498 return(DispAllErrors(henv, hdbc));
499
500 if (SQLGetInfo(hdbc, SQL_DBMS_NAME, (UCHAR*) dbInf.dbmsName, 40, &cb) != SQL_SUCCESS)
501 return(DispAllErrors(henv, hdbc));
502
503 // 16-Mar-1999
504 // After upgrading to MSVC6, the original 20 char buffer below was insufficient,
505 // causing database connectivity to fail in some cases.
506 retcode = SQLGetInfo(hdbc, SQL_DBMS_VER, (UCHAR*) dbInf.dbmsVer, 64, &cb);
507 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO )
508 return(DispAllErrors(henv, hdbc));
509
510 if (SQLGetInfo(hdbc, SQL_ACTIVE_CONNECTIONS, (UCHAR*) &dbInf.maxConnections, sizeof(dbInf.maxConnections), &cb) != SQL_SUCCESS)
511 return(DispAllErrors(henv, hdbc));
512
513 if (SQLGetInfo(hdbc, SQL_ACTIVE_STATEMENTS, (UCHAR*) &dbInf.maxStmts, sizeof(dbInf.maxStmts), &cb) != SQL_SUCCESS)
514 return(DispAllErrors(henv, hdbc));
515
516 if (SQLGetInfo(hdbc, SQL_DRIVER_NAME, (UCHAR*) dbInf.driverName, 40, &cb) != SQL_SUCCESS)
517 return(DispAllErrors(henv, hdbc));
518
519 if (SQLGetInfo(hdbc, SQL_DRIVER_ODBC_VER, (UCHAR*) dbInf.odbcVer, 60, &cb) == SQL_ERROR)
520 return(DispAllErrors(henv, hdbc));
521
522 retcode = SQLGetInfo(hdbc, SQL_ODBC_VER, (UCHAR*) dbInf.drvMgrOdbcVer, 60, &cb);
523 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
524 return(DispAllErrors(henv, hdbc));
525
526 if (SQLGetInfo(hdbc, SQL_DRIVER_VER, (UCHAR*) dbInf.driverVer, 60, &cb) == SQL_ERROR)
527 return(DispAllErrors(henv, hdbc));
528
529 if (SQLGetInfo(hdbc, SQL_ODBC_API_CONFORMANCE, (UCHAR*) &dbInf.apiConfLvl, sizeof(dbInf.apiConfLvl), &cb) != SQL_SUCCESS)
530 return(DispAllErrors(henv, hdbc));
531
532 if (SQLGetInfo(hdbc, SQL_ODBC_SAG_CLI_CONFORMANCE, (UCHAR*) &dbInf.cliConfLvl, sizeof(dbInf.cliConfLvl), &cb) != SQL_SUCCESS)
533 return(DispAllErrors(henv, hdbc));
534
535 if (SQLGetInfo(hdbc, SQL_ODBC_SQL_CONFORMANCE, (UCHAR*) &dbInf.sqlConfLvl, sizeof(dbInf.sqlConfLvl), &cb) != SQL_SUCCESS)
536 return(DispAllErrors(henv, hdbc));
537
538 if (SQLGetInfo(hdbc, SQL_OUTER_JOINS, (UCHAR*) dbInf.outerJoins, 2, &cb) != SQL_SUCCESS)
539 return(DispAllErrors(henv, hdbc));
540
541 if (SQLGetInfo(hdbc, SQL_PROCEDURES, (UCHAR*) dbInf.procedureSupport, 2, &cb) != SQL_SUCCESS)
542 return(DispAllErrors(henv, hdbc));
543
544 if (SQLGetInfo(hdbc, SQL_CURSOR_COMMIT_BEHAVIOR, (UCHAR*) &dbInf.cursorCommitBehavior, sizeof(dbInf.cursorCommitBehavior), &cb) != SQL_SUCCESS)
545 return(DispAllErrors(henv, hdbc));
546
547 if (SQLGetInfo(hdbc, SQL_CURSOR_ROLLBACK_BEHAVIOR, (UCHAR*) &dbInf.cursorRollbackBehavior, sizeof(dbInf.cursorRollbackBehavior), &cb) != SQL_SUCCESS)
548 return(DispAllErrors(henv, hdbc));
549
550 if (SQLGetInfo(hdbc, SQL_NON_NULLABLE_COLUMNS, (UCHAR*) &dbInf.supportNotNullClause, sizeof(dbInf.supportNotNullClause), &cb) != SQL_SUCCESS)
551 return(DispAllErrors(henv, hdbc));
552
553 if (SQLGetInfo(hdbc, SQL_ODBC_SQL_OPT_IEF, (UCHAR*) dbInf.supportIEF, 2, &cb) != SQL_SUCCESS)
554 return(DispAllErrors(henv, hdbc));
555
556 if (SQLGetInfo(hdbc, SQL_DEFAULT_TXN_ISOLATION, (UCHAR*) &dbInf.txnIsolation, sizeof(dbInf.txnIsolation), &cb) != SQL_SUCCESS)
557 return(DispAllErrors(henv, hdbc));
558
559 if (SQLGetInfo(hdbc, SQL_TXN_ISOLATION_OPTION, (UCHAR*) &dbInf.txnIsolationOptions, sizeof(dbInf.txnIsolationOptions), &cb) != SQL_SUCCESS)
560 return(DispAllErrors(henv, hdbc));
561
562 if (SQLGetInfo(hdbc, SQL_FETCH_DIRECTION, (UCHAR*) &dbInf.fetchDirections, sizeof(dbInf.fetchDirections), &cb) != SQL_SUCCESS)
563 return(DispAllErrors(henv, hdbc));
564
565 if (SQLGetInfo(hdbc, SQL_LOCK_TYPES, (UCHAR*) &dbInf.lockTypes, sizeof(dbInf.lockTypes), &cb) != SQL_SUCCESS)
566 return(DispAllErrors(henv, hdbc));
567
568 if (SQLGetInfo(hdbc, SQL_POS_OPERATIONS, (UCHAR*) &dbInf.posOperations, sizeof(dbInf.posOperations), &cb) != SQL_SUCCESS)
569 return(DispAllErrors(henv, hdbc));
570
571 if (SQLGetInfo(hdbc, SQL_POSITIONED_STATEMENTS, (UCHAR*) &dbInf.posStmts, sizeof(dbInf.posStmts), &cb) != SQL_SUCCESS)
572 return(DispAllErrors(henv, hdbc));
573
574 if (SQLGetInfo(hdbc, SQL_SCROLL_CONCURRENCY, (UCHAR*) &dbInf.scrollConcurrency, sizeof(dbInf.scrollConcurrency), &cb) != SQL_SUCCESS)
575 return(DispAllErrors(henv, hdbc));
576
577 if (SQLGetInfo(hdbc, SQL_SCROLL_OPTIONS, (UCHAR*) &dbInf.scrollOptions, sizeof(dbInf.scrollOptions), &cb) != SQL_SUCCESS)
578 return(DispAllErrors(henv, hdbc));
579
580 if (SQLGetInfo(hdbc, SQL_STATIC_SENSITIVITY, (UCHAR*) &dbInf.staticSensitivity, sizeof(dbInf.staticSensitivity), &cb) != SQL_SUCCESS)
581 return(DispAllErrors(henv, hdbc));
582
583 if (SQLGetInfo(hdbc, SQL_TXN_CAPABLE, (UCHAR*) &dbInf.txnCapable, sizeof(dbInf.txnCapable), &cb) != SQL_SUCCESS)
584 return(DispAllErrors(henv, hdbc));
585
586 if (SQLGetInfo(hdbc, SQL_LOGIN_TIMEOUT, (UCHAR*) &dbInf.loginTimeout, sizeof(dbInf.loginTimeout), &cb) != SQL_SUCCESS)
587 return(DispAllErrors(henv, hdbc));
588
589 #ifdef DBDEBUG_CONSOLE
590 cout << ">>>>> DATA SOURCE INFORMATION <<<<<" << endl;
591 cout << "SERVER Name: " << dbInf.serverName << endl;
592 cout << "DBMS Name: " << dbInf.dbmsName << "; DBMS Version: " << dbInf.dbmsVer << endl;
593 cout << "ODBC Version: " << dbInf.odbcVer << "; Driver Version: " << dbInf.driverVer << endl;
594
595 cout << "API Conf. Level: ";
596 switch(dbInf.apiConfLvl)
597 {
598 case SQL_OAC_NONE: cout << "None"; break;
599 case SQL_OAC_LEVEL1: cout << "Level 1"; break;
600 case SQL_OAC_LEVEL2: cout << "Level 2"; break;
601 }
602 cout << endl;
603
604 cout << "SAG CLI Conf. Level: ";
605 switch(dbInf.cliConfLvl)
606 {
607 case SQL_OSCC_NOT_COMPLIANT: cout << "Not Compliant"; break;
608 case SQL_OSCC_COMPLIANT: cout << "Compliant"; break;
609 }
610 cout << endl;
611
612 cout << "SQL Conf. Level: ";
613 switch(dbInf.sqlConfLvl)
614 {
615 case SQL_OSC_MINIMUM: cout << "Minimum Grammer"; break;
616 case SQL_OSC_CORE: cout << "Core Grammer"; break;
617 case SQL_OSC_EXTENDED: cout << "Extended Grammer"; break;
618 }
619 cout << endl;
620
621 cout << "Max. Connections: " << dbInf.maxConnections << endl;
622 cout << "Outer Joins: " << dbInf.outerJoins << endl;
623 cout << "Support for Procedures: " << dbInf.procedureSupport << endl;
624
625 cout << "Cursor COMMIT Behavior: ";
626 switch(dbInf.cursorCommitBehavior)
627 {
628 case SQL_CB_DELETE: cout << "Delete cursors"; break;
629 case SQL_CB_CLOSE: cout << "Close cursors"; break;
630 case SQL_CB_PRESERVE: cout << "Preserve cursors"; break;
631 }
632 cout << endl;
633
634 cout << "Cursor ROLLBACK Behavior: ";
635 switch(dbInf.cursorRollbackBehavior)
636 {
637 case SQL_CB_DELETE: cout << "Delete cursors"; break;
638 case SQL_CB_CLOSE: cout << "Close cursors"; break;
639 case SQL_CB_PRESERVE: cout << "Preserve cursors"; break;
640 }
641 cout << endl;
642
643 cout << "Support NOT NULL clause: ";
644 switch(dbInf.supportNotNullClause)
645 {
646 case SQL_NNC_NULL: cout << "No"; break;
647 case SQL_NNC_NON_NULL: cout << "Yes"; break;
648 }
649 cout << endl;
650
651 cout << "Support IEF (Ref. Integrity): " << dbInf.supportIEF << endl;
652 cout << "Login Timeout: " << dbInf.loginTimeout << endl;
653
654 cout << endl << endl << "more ..." << endl;
655 getchar();
656
657 cout << "Default Transaction Isolation: ";
658 switch(dbInf.txnIsolation)
659 {
660 case SQL_TXN_READ_UNCOMMITTED: cout << "Read Uncommitted"; break;
661 case SQL_TXN_READ_COMMITTED: cout << "Read Committed"; break;
662 case SQL_TXN_REPEATABLE_READ: cout << "Repeatable Read"; break;
663 case SQL_TXN_SERIALIZABLE: cout << "Serializable"; break;
664 #ifdef ODBC_V20
665 case SQL_TXN_VERSIONING: cout << "Versioning"; break;
666 #endif
667 }
668 cout << endl;
669
670 cout << "Transaction Isolation Options: ";
671 if (dbInf.txnIsolationOptions & SQL_TXN_READ_UNCOMMITTED)
672 cout << "Read Uncommitted, ";
673 if (dbInf.txnIsolationOptions & SQL_TXN_READ_COMMITTED)
674 cout << "Read Committed, ";
675 if (dbInf.txnIsolationOptions & SQL_TXN_REPEATABLE_READ)
676 cout << "Repeatable Read, ";
677 if (dbInf.txnIsolationOptions & SQL_TXN_SERIALIZABLE)
678 cout << "Serializable, ";
679 #ifdef ODBC_V20
680 if (dbInf.txnIsolationOptions & SQL_TXN_VERSIONING)
681 cout << "Versioning";
682 #endif
683 cout << endl;
684
685 cout << "Fetch Directions Supported:" << endl << " ";
686 if (dbInf.fetchDirections & SQL_FD_FETCH_NEXT)
687 cout << "Next, ";
688 if (dbInf.fetchDirections & SQL_FD_FETCH_PRIOR)
689 cout << "Prev, ";
690 if (dbInf.fetchDirections & SQL_FD_FETCH_FIRST)
691 cout << "First, ";
692 if (dbInf.fetchDirections & SQL_FD_FETCH_LAST)
693 cout << "Last, ";
694 if (dbInf.fetchDirections & SQL_FD_FETCH_ABSOLUTE)
695 cout << "Absolute, ";
696 if (dbInf.fetchDirections & SQL_FD_FETCH_RELATIVE)
697 cout << "Relative, ";
698 #ifdef ODBC_V20
699 if (dbInf.fetchDirections & SQL_FD_FETCH_RESUME)
700 cout << "Resume, ";
701 #endif
702 if (dbInf.fetchDirections & SQL_FD_FETCH_BOOKMARK)
703 cout << "Bookmark";
704 cout << endl;
705
706 cout << "Lock Types Supported (SQLSetPos): ";
707 if (dbInf.lockTypes & SQL_LCK_NO_CHANGE)
708 cout << "No Change, ";
709 if (dbInf.lockTypes & SQL_LCK_EXCLUSIVE)
710 cout << "Exclusive, ";
711 if (dbInf.lockTypes & SQL_LCK_UNLOCK)
712 cout << "UnLock";
713 cout << endl;
714
715 cout << "Position Operations Supported (SQLSetPos): ";
716 if (dbInf.posOperations & SQL_POS_POSITION)
717 cout << "Position, ";
718 if (dbInf.posOperations & SQL_POS_REFRESH)
719 cout << "Refresh, ";
720 if (dbInf.posOperations & SQL_POS_UPDATE)
721 cout << "Upd, ";
722 if (dbInf.posOperations & SQL_POS_DELETE)
723 cout << "Del, ";
724 if (dbInf.posOperations & SQL_POS_ADD)
725 cout << "Add";
726 cout << endl;
727
728 cout << "Positioned Statements Supported: ";
729 if (dbInf.posStmts & SQL_PS_POSITIONED_DELETE)
730 cout << "Pos delete, ";
731 if (dbInf.posStmts & SQL_PS_POSITIONED_UPDATE)
732 cout << "Pos update, ";
733 if (dbInf.posStmts & SQL_PS_SELECT_FOR_UPDATE)
734 cout << "Select for update";
735 cout << endl;
736
737 cout << "Scroll Concurrency: ";
738 if (dbInf.scrollConcurrency & SQL_SCCO_READ_ONLY)
739 cout << "Read Only, ";
740 if (dbInf.scrollConcurrency & SQL_SCCO_LOCK)
741 cout << "Lock, ";
742 if (dbInf.scrollConcurrency & SQL_SCCO_OPT_ROWVER)
743 cout << "Opt. Rowver, ";
744 if (dbInf.scrollConcurrency & SQL_SCCO_OPT_VALUES)
745 cout << "Opt. Values";
746 cout << endl;
747
748 cout << "Scroll Options: ";
749 if (dbInf.scrollOptions & SQL_SO_FORWARD_ONLY)
750 cout << "Fwd Only, ";
751 if (dbInf.scrollOptions & SQL_SO_STATIC)
752 cout << "Static, ";
753 if (dbInf.scrollOptions & SQL_SO_KEYSET_DRIVEN)
754 cout << "Keyset Driven, ";
755 if (dbInf.scrollOptions & SQL_SO_DYNAMIC)
756 cout << "Dynamic, ";
757 if (dbInf.scrollOptions & SQL_SO_MIXED)
758 cout << "Mixed";
759 cout << endl;
760
761 cout << "Static Sensitivity: ";
762 if (dbInf.staticSensitivity & SQL_SS_ADDITIONS)
763 cout << "Additions, ";
764 if (dbInf.staticSensitivity & SQL_SS_DELETIONS)
765 cout << "Deletions, ";
766 if (dbInf.staticSensitivity & SQL_SS_UPDATES)
767 cout << "Updates";
768 cout << endl;
769
770 cout << "Transaction Capable?: ";
771 switch(dbInf.txnCapable)
772 {
773 case SQL_TC_NONE: cout << "No"; break;
774 case SQL_TC_DML: cout << "DML Only"; break;
775 case SQL_TC_DDL_COMMIT: cout << "DDL Commit"; break;
776 case SQL_TC_DDL_IGNORE: cout << "DDL Ignore"; break;
777 case SQL_TC_ALL: cout << "DDL & DML"; break;
778 }
779 cout << endl;
780
781 cout << endl;
782 #endif
783
784 // Completed Successfully
785 return(TRUE);
786
787 } // wxDB::getDbInfo()
788
789
790 /********** wxDB::getDataTypeInfo() **********/
791 bool wxDB::getDataTypeInfo(SWORD fSqlType, SqlTypeInfo &structSQLTypeInfo)
792 {
793 /*
794 * fSqlType will be something like SQL_VARCHAR. This parameter determines
795 * the data type inf. is gathered for.
796 *
797 * SqlTypeInfo is a structure that is filled in with data type information,
798 */
799 RETCODE retcode;
800 SDWORD cbRet;
801
802 // Get information about the data type specified
803 if (SQLGetTypeInfo(hstmt, fSqlType) != SQL_SUCCESS)
804 return(DispAllErrors(henv, hdbc, hstmt));
805 // Fetch the record
806 if ((retcode = SQLFetch(hstmt)) != SQL_SUCCESS)
807 {
808 #ifdef DBDEBUG_CONSOLE
809 if (retcode == SQL_NO_DATA_FOUND)
810 cout << "SQL_NO_DATA_FOUND fetching inf. about data type." << endl;
811 #endif
812 DispAllErrors(henv, hdbc, hstmt);
813 SQLFreeStmt(hstmt, SQL_CLOSE);
814 return(FALSE);
815 }
816 // Obtain columns from the record
817 if (SQLGetData(hstmt, 1, SQL_C_CHAR, (UCHAR*) structSQLTypeInfo.TypeName, DB_TYPE_NAME_LEN, &cbRet) != SQL_SUCCESS)
818 return(DispAllErrors(henv, hdbc, hstmt));
819
820 // BJO 991209
821 if (Dbms() == dbmsMY_SQL)
822 {
823 if (!strcmp(structSQLTypeInfo.TypeName, "middleint")) strcpy(structSQLTypeInfo.TypeName, "mediumint");
824 if (!strcmp(structSQLTypeInfo.TypeName, "middleint unsigned")) strcpy(structSQLTypeInfo.TypeName, "mediumint unsigned");
825 if (!strcmp(structSQLTypeInfo.TypeName, "integer")) strcpy(structSQLTypeInfo.TypeName, "int");
826 if (!strcmp(structSQLTypeInfo.TypeName, "integer unsigned")) strcpy(structSQLTypeInfo.TypeName, "int unsigned");
827 if (!strcmp(structSQLTypeInfo.TypeName, "middleint")) strcpy(structSQLTypeInfo.TypeName, "mediumint");
828 if (!strcmp(structSQLTypeInfo.TypeName, "varchar")) strcpy(structSQLTypeInfo.TypeName, "char");
829 }
830
831 if (SQLGetData(hstmt, 3, SQL_C_LONG, (UCHAR*) &structSQLTypeInfo.Precision, 0, &cbRet) != SQL_SUCCESS)
832 return(DispAllErrors(henv, hdbc, hstmt));
833 if (SQLGetData(hstmt, 8, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.CaseSensitive, 0, &cbRet) != SQL_SUCCESS)
834 return(DispAllErrors(henv, hdbc, hstmt));
835 // if (SQLGetData(hstmt, 14, SQL_C_SHORT, (UCHAR*) &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
836 // return(DispAllErrors(henv, hdbc, hstmt));
837
838 if (SQLGetData(hstmt, 15, SQL_C_SHORT,(UCHAR*) &structSQLTypeInfo.MaximumScale, 0, &cbRet) != SQL_SUCCESS)
839 return(DispAllErrors(henv, hdbc, hstmt));
840
841 if (structSQLTypeInfo.MaximumScale < 0)
842 structSQLTypeInfo.MaximumScale = 0;
843
844 // Close the statement handle which closes open cursors
845 if (SQLFreeStmt(hstmt, SQL_CLOSE) != SQL_SUCCESS)
846 return(DispAllErrors(henv, hdbc, hstmt));
847
848 // Completed Successfully
849 return(TRUE);
850
851 } // wxDB::getDataTypeInfo()
852
853
854 /********** wxDB::Close() **********/
855 void wxDB::Close(void)
856 {
857 // Close the Sql Log file
858 if (fpSqlLog)
859 {
860 fclose(fpSqlLog);
861 fpSqlLog = 0;
862 }
863
864 // Free statement handle
865 if (dbIsOpen)
866 {
867 if (SQLFreeStmt(hstmt, SQL_DROP) != SQL_SUCCESS)
868 DispAllErrors(henv, hdbc);
869 }
870
871 // Disconnect from the datasource
872 if (SQLDisconnect(hdbc) != SQL_SUCCESS)
873 DispAllErrors(henv, hdbc);
874
875 // Free the connection to the datasource
876 if (SQLFreeConnect(hdbc) != SQL_SUCCESS)
877 DispAllErrors(henv, hdbc);
878
879 // There should be zero Ctable objects still connected to this db object
880 assert(nTables == 0);
881
882 #ifdef __WXDEBUG__
883 CstructTablesInUse *tiu;
884 wxNode *pNode;
885 pNode = TablesInUse.First();
886 char s[80];
887 char s2[80];
888 while (pNode)
889 {
890 tiu = (CstructTablesInUse *)pNode->Data();
891 if (tiu->pDb == this)
892 {
893 sprintf(s, "(%-20s) tableID:[%6lu] pDb:[%p]", tiu->tableName,tiu->tableID,tiu->pDb);
894 sprintf(s2,"Orphaned found using pDb:[%p]",this);
895 wxMessageBox (s,s2);
896 }
897 pNode = pNode->Next();
898 }
899 #endif
900
901 // Copy the error messages to a global variable
902 int i;
903 for (i = 0; i < DB_MAX_ERROR_HISTORY; i++)
904 wxStrcpy(DBerrorList[i],errorList[i]);
905
906 } // wxDB::Close()
907
908
909 /********** wxDB::CommitTrans() **********/
910 bool wxDB::CommitTrans(void)
911 {
912 if (this)
913 {
914 // Commit the transaction
915 if (SQLTransact(henv, hdbc, SQL_COMMIT) != SQL_SUCCESS)
916 return(DispAllErrors(henv, hdbc));
917 }
918
919 // Completed successfully
920 return(TRUE);
921
922 } // wxDB::CommitTrans()
923
924
925 /********** wxDB::RollbackTrans() **********/
926 bool wxDB::RollbackTrans(void)
927 {
928 // Rollback the transaction
929 if (SQLTransact(henv, hdbc, SQL_ROLLBACK) != SQL_SUCCESS)
930 return(DispAllErrors(henv, hdbc));
931
932 // Completed successfully
933 return(TRUE);
934
935 } // wxDB::RollbackTrans()
936
937
938 /********** wxDB::DispAllErrors() **********/
939 bool wxDB::DispAllErrors(HENV aHenv, HDBC aHdbc, HSTMT aHstmt)
940 {
941 char odbcErrMsg[DB_MAX_ERROR_MSG_LEN];
942
943 while (SQLError(aHenv, aHdbc, aHstmt, (UCHAR FAR *) sqlState, &nativeError, (UCHAR FAR *) errorMsg, SQL_MAX_MESSAGE_LENGTH - 1, &cbErrorMsg) == SQL_SUCCESS)
944 {
945 sprintf(odbcErrMsg, "SQL State = %s\nNative Error Code = %li\nError Message = %s\n", sqlState, nativeError, errorMsg);
946 logError(odbcErrMsg, sqlState);
947 if (!silent)
948 {
949 #ifdef DBDEBUG_CONSOLE
950 // When run in console mode, use standard out to display errors.
951 cout << odbcErrMsg << endl;
952 cout << "Press any key to continue..." << endl;
953 getchar();
954 #endif
955 }
956
957 #ifdef __WXDEBUG__
958 wxMessageBox(odbcErrMsg);
959 #endif
960 }
961
962 return(FALSE); // This function always returns false.
963
964 } // wxDB::DispAllErrors()
965
966
967 /********** wxDB::GetNextError() **********/
968 bool wxDB::GetNextError(HENV aHenv, HDBC aHdbc, HSTMT aHstmt)
969 {
970 if (SQLError(aHenv, aHdbc, aHstmt, (UCHAR FAR *) sqlState, &nativeError, (UCHAR FAR *) errorMsg, SQL_MAX_MESSAGE_LENGTH - 1, &cbErrorMsg) == SQL_SUCCESS)
971 return(TRUE);
972 else
973 return(FALSE);
974
975 } // wxDB::GetNextError()
976
977
978 /********** wxDB::DispNextError() **********/
979 void wxDB::DispNextError(void)
980 {
981 char odbcErrMsg[DB_MAX_ERROR_MSG_LEN];
982
983 sprintf(odbcErrMsg, "SQL State = %s\nNative Error Code = %li\nError Message = %s\n", sqlState, nativeError, errorMsg);
984 logError(odbcErrMsg, sqlState);
985
986 if (silent)
987 return;
988
989 #ifdef DBDEBUG_CONSOLE
990 // When run in console mode, use standard out to display errors.
991 cout << odbcErrMsg << endl;
992 cout << "Press any key to continue..." << endl;
993 getchar();
994 #endif
995
996 } // wxDB::DispNextError()
997
998
999 /********** wxDB::logError() **********/
1000 void wxDB::logError(const char *errMsg, const char *SQLState)
1001 {
1002 assert(errMsg && wxStrlen(errMsg));
1003
1004 static int pLast = -1;
1005 int dbStatus;
1006
1007 if (++pLast == DB_MAX_ERROR_HISTORY)
1008 {
1009 int i;
1010 for (i = 0; i < DB_MAX_ERROR_HISTORY; i++)
1011 wxStrcpy(errorList[i], errorList[i+1]);
1012 pLast--;
1013 }
1014
1015 wxStrcpy(errorList[pLast], errMsg);
1016
1017 if (SQLState && wxStrlen(SQLState))
1018 if ((dbStatus = TranslateSqlState(SQLState)) != DB_ERR_FUNCTION_SEQUENCE_ERROR)
1019 DB_STATUS = dbStatus;
1020
1021 // Add the errmsg to the sql log
1022 WriteSqlLog(errMsg);
1023
1024 } // wxDB::logError()
1025
1026
1027 /**********wxDB::TranslateSqlState() **********/
1028 int wxDB::TranslateSqlState(const char *SQLState)
1029 {
1030 if (!wxStrcmp(SQLState, "01000"))
1031 return(DB_ERR_GENERAL_WARNING);
1032 if (!wxStrcmp(SQLState, "01002"))
1033 return(DB_ERR_DISCONNECT_ERROR);
1034 if (!wxStrcmp(SQLState, "01004"))
1035 return(DB_ERR_DATA_TRUNCATED);
1036 if (!wxStrcmp(SQLState, "01006"))
1037 return(DB_ERR_PRIV_NOT_REVOKED);
1038 if (!wxStrcmp(SQLState, "01S00"))
1039 return(DB_ERR_INVALID_CONN_STR_ATTR);
1040 if (!wxStrcmp(SQLState, "01S01"))
1041 return(DB_ERR_ERROR_IN_ROW);
1042 if (!wxStrcmp(SQLState, "01S02"))
1043 return(DB_ERR_OPTION_VALUE_CHANGED);
1044 if (!wxStrcmp(SQLState, "01S03"))
1045 return(DB_ERR_NO_ROWS_UPD_OR_DEL);
1046 if (!wxStrcmp(SQLState, "01S04"))
1047 return(DB_ERR_MULTI_ROWS_UPD_OR_DEL);
1048 if (!wxStrcmp(SQLState, "07001"))
1049 return(DB_ERR_WRONG_NO_OF_PARAMS);
1050 if (!wxStrcmp(SQLState, "07006"))
1051 return(DB_ERR_DATA_TYPE_ATTR_VIOL);
1052 if (!wxStrcmp(SQLState, "08001"))
1053 return(DB_ERR_UNABLE_TO_CONNECT);
1054 if (!wxStrcmp(SQLState, "08002"))
1055 return(DB_ERR_CONNECTION_IN_USE);
1056 if (!wxStrcmp(SQLState, "08003"))
1057 return(DB_ERR_CONNECTION_NOT_OPEN);
1058 if (!wxStrcmp(SQLState, "08004"))
1059 return(DB_ERR_REJECTED_CONNECTION);
1060 if (!wxStrcmp(SQLState, "08007"))
1061 return(DB_ERR_CONN_FAIL_IN_TRANS);
1062 if (!wxStrcmp(SQLState, "08S01"))
1063 return(DB_ERR_COMM_LINK_FAILURE);
1064 if (!wxStrcmp(SQLState, "21S01"))
1065 return(DB_ERR_INSERT_VALUE_LIST_MISMATCH);
1066 if (!wxStrcmp(SQLState, "21S02"))
1067 return(DB_ERR_DERIVED_TABLE_MISMATCH);
1068 if (!wxStrcmp(SQLState, "22001"))
1069 return(DB_ERR_STRING_RIGHT_TRUNC);
1070 if (!wxStrcmp(SQLState, "22003"))
1071 return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG);
1072 if (!wxStrcmp(SQLState, "22005"))
1073 return(DB_ERR_ERROR_IN_ASSIGNMENT);
1074 if (!wxStrcmp(SQLState, "22008"))
1075 return(DB_ERR_DATETIME_FLD_OVERFLOW);
1076 if (!wxStrcmp(SQLState, "22012"))
1077 return(DB_ERR_DIVIDE_BY_ZERO);
1078 if (!wxStrcmp(SQLState, "22026"))
1079 return(DB_ERR_STR_DATA_LENGTH_MISMATCH);
1080 if (!wxStrcmp(SQLState, "23000"))
1081 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL);
1082 if (!wxStrcmp(SQLState, "24000"))
1083 return(DB_ERR_INVALID_CURSOR_STATE);
1084 if (!wxStrcmp(SQLState, "25000"))
1085 return(DB_ERR_INVALID_TRANS_STATE);
1086 if (!wxStrcmp(SQLState, "28000"))
1087 return(DB_ERR_INVALID_AUTH_SPEC);
1088 if (!wxStrcmp(SQLState, "34000"))
1089 return(DB_ERR_INVALID_CURSOR_NAME);
1090 if (!wxStrcmp(SQLState, "37000"))
1091 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL);
1092 if (!wxStrcmp(SQLState, "3C000"))
1093 return(DB_ERR_DUPLICATE_CURSOR_NAME);
1094 if (!wxStrcmp(SQLState, "40001"))
1095 return(DB_ERR_SERIALIZATION_FAILURE);
1096 if (!wxStrcmp(SQLState, "42000"))
1097 return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2);
1098 if (!wxStrcmp(SQLState, "70100"))
1099 return(DB_ERR_OPERATION_ABORTED);
1100 if (!wxStrcmp(SQLState, "IM001"))
1101 return(DB_ERR_UNSUPPORTED_FUNCTION);
1102 if (!wxStrcmp(SQLState, "IM002"))
1103 return(DB_ERR_NO_DATA_SOURCE);
1104 if (!wxStrcmp(SQLState, "IM003"))
1105 return(DB_ERR_DRIVER_LOAD_ERROR);
1106 if (!wxStrcmp(SQLState, "IM004"))
1107 return(DB_ERR_SQLALLOCENV_FAILED);
1108 if (!wxStrcmp(SQLState, "IM005"))
1109 return(DB_ERR_SQLALLOCCONNECT_FAILED);
1110 if (!wxStrcmp(SQLState, "IM006"))
1111 return(DB_ERR_SQLSETCONNECTOPTION_FAILED);
1112 if (!wxStrcmp(SQLState, "IM007"))
1113 return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB);
1114 if (!wxStrcmp(SQLState, "IM008"))
1115 return(DB_ERR_DIALOG_FAILED);
1116 if (!wxStrcmp(SQLState, "IM009"))
1117 return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL);
1118 if (!wxStrcmp(SQLState, "IM010"))
1119 return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG);
1120 if (!wxStrcmp(SQLState, "IM011"))
1121 return(DB_ERR_DRIVER_NAME_TOO_LONG);
1122 if (!wxStrcmp(SQLState, "IM012"))
1123 return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR);
1124 if (!wxStrcmp(SQLState, "IM013"))
1125 return(DB_ERR_TRACE_FILE_ERROR);
1126 if (!wxStrcmp(SQLState, "S0001"))
1127 return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS);
1128 if (!wxStrcmp(SQLState, "S0002"))
1129 return(DB_ERR_TABLE_NOT_FOUND);
1130 if (!wxStrcmp(SQLState, "S0011"))
1131 return(DB_ERR_INDEX_ALREADY_EXISTS);
1132 if (!wxStrcmp(SQLState, "S0012"))
1133 return(DB_ERR_INDEX_NOT_FOUND);
1134 if (!wxStrcmp(SQLState, "S0021"))
1135 return(DB_ERR_COLUMN_ALREADY_EXISTS);
1136 if (!wxStrcmp(SQLState, "S0022"))
1137 return(DB_ERR_COLUMN_NOT_FOUND);
1138 if (!wxStrcmp(SQLState, "S0023"))
1139 return(DB_ERR_NO_DEFAULT_FOR_COLUMN);
1140 if (!wxStrcmp(SQLState, "S1000"))
1141 return(DB_ERR_GENERAL_ERROR);
1142 if (!wxStrcmp(SQLState, "S1001"))
1143 return(DB_ERR_MEMORY_ALLOCATION_FAILURE);
1144 if (!wxStrcmp(SQLState, "S1002"))
1145 return(DB_ERR_INVALID_COLUMN_NUMBER);
1146 if (!wxStrcmp(SQLState, "S1003"))
1147 return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE);
1148 if (!wxStrcmp(SQLState, "S1004"))
1149 return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE);
1150 if (!wxStrcmp(SQLState, "S1008"))
1151 return(DB_ERR_OPERATION_CANCELLED);
1152 if (!wxStrcmp(SQLState, "S1009"))
1153 return(DB_ERR_INVALID_ARGUMENT_VALUE);
1154 if (!wxStrcmp(SQLState, "S1010"))
1155 return(DB_ERR_FUNCTION_SEQUENCE_ERROR);
1156 if (!wxStrcmp(SQLState, "S1011"))
1157 return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME);
1158 if (!wxStrcmp(SQLState, "S1012"))
1159 return(DB_ERR_INVALID_TRANS_OPERATION_CODE);
1160 if (!wxStrcmp(SQLState, "S1015"))
1161 return(DB_ERR_NO_CURSOR_NAME_AVAIL);
1162 if (!wxStrcmp(SQLState, "S1090"))
1163 return(DB_ERR_INVALID_STR_OR_BUF_LEN);
1164 if (!wxStrcmp(SQLState, "S1091"))
1165 return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE);
1166 if (!wxStrcmp(SQLState, "S1092"))
1167 return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE);
1168 if (!wxStrcmp(SQLState, "S1093"))
1169 return(DB_ERR_INVALID_PARAM_NO);
1170 if (!wxStrcmp(SQLState, "S1094"))
1171 return(DB_ERR_INVALID_SCALE_VALUE);
1172 if (!wxStrcmp(SQLState, "S1095"))
1173 return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE);
1174 if (!wxStrcmp(SQLState, "S1096"))
1175 return(DB_ERR_INF_TYPE_OUT_OF_RANGE);
1176 if (!wxStrcmp(SQLState, "S1097"))
1177 return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE);
1178 if (!wxStrcmp(SQLState, "S1098"))
1179 return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE);
1180 if (!wxStrcmp(SQLState, "S1099"))
1181 return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE);
1182 if (!wxStrcmp(SQLState, "S1100"))
1183 return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE);
1184 if (!wxStrcmp(SQLState, "S1101"))
1185 return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE);
1186 if (!wxStrcmp(SQLState, "S1103"))
1187 return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE);
1188 if (!wxStrcmp(SQLState, "S1104"))
1189 return(DB_ERR_INVALID_PRECISION_VALUE);
1190 if (!wxStrcmp(SQLState, "S1105"))
1191 return(DB_ERR_INVALID_PARAM_TYPE);
1192 if (!wxStrcmp(SQLState, "S1106"))
1193 return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE);
1194 if (!wxStrcmp(SQLState, "S1107"))
1195 return(DB_ERR_ROW_VALUE_OUT_OF_RANGE);
1196 if (!wxStrcmp(SQLState, "S1108"))
1197 return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE);
1198 if (!wxStrcmp(SQLState, "S1109"))
1199 return(DB_ERR_INVALID_CURSOR_POSITION);
1200 if (!wxStrcmp(SQLState, "S1110"))
1201 return(DB_ERR_INVALID_DRIVER_COMPLETION);
1202 if (!wxStrcmp(SQLState, "S1111"))
1203 return(DB_ERR_INVALID_BOOKMARK_VALUE);
1204 if (!wxStrcmp(SQLState, "S1C00"))
1205 return(DB_ERR_DRIVER_NOT_CAPABLE);
1206 if (!wxStrcmp(SQLState, "S1T00"))
1207 return(DB_ERR_TIMEOUT_EXPIRED);
1208
1209 // No match
1210 return(0);
1211
1212 } // wxDB::TranslateSqlState()
1213
1214
1215 /********** wxDB::Grant() **********/
1216 bool wxDB::Grant(int privileges, const char *tableName, const char *userList)
1217 {
1218 char sqlStmt[DB_MAX_STATEMENT_LEN];
1219
1220 // Build the grant statement
1221 wxStrcpy(sqlStmt, "GRANT ");
1222 if (privileges == DB_GRANT_ALL)
1223 wxStrcat(sqlStmt, "ALL");
1224 else
1225 {
1226 int c = 0;
1227 if (privileges & DB_GRANT_SELECT)
1228 {
1229 wxStrcat(sqlStmt, "SELECT");
1230 c++;
1231 }
1232 if (privileges & DB_GRANT_INSERT)
1233 {
1234 if (c++)
1235 wxStrcat(sqlStmt, ", ");
1236 wxStrcat(sqlStmt, "INSERT");
1237 }
1238 if (privileges & DB_GRANT_UPDATE)
1239 {
1240 if (c++)
1241 wxStrcat(sqlStmt, ", ");
1242 wxStrcat(sqlStmt, "UPDATE");
1243 }
1244 if (privileges & DB_GRANT_DELETE)
1245 {
1246 if (c++)
1247 wxStrcat(sqlStmt, ", ");
1248 wxStrcat(sqlStmt, "DELETE");
1249 }
1250 }
1251
1252 wxStrcat(sqlStmt, " ON ");
1253 wxStrcat(sqlStmt, tableName);
1254 wxStrcat(sqlStmt, " TO ");
1255 wxStrcat(sqlStmt, userList);
1256
1257 #ifdef DBDEBUG_CONSOLE
1258 cout << endl << sqlStmt << endl;
1259 #endif
1260
1261 WriteSqlLog(sqlStmt);
1262
1263 return(ExecSql(sqlStmt));
1264
1265 } // wxDB::Grant()
1266
1267
1268 /********** wxDB::CreateView() **********/
1269 bool wxDB::CreateView(const char *viewName, const char *colList, const char *pSqlStmt, bool attemptDrop)
1270 {
1271 char sqlStmt[DB_MAX_STATEMENT_LEN];
1272
1273 // Drop the view first
1274 if (attemptDrop && !DropView(viewName))
1275 return FALSE;
1276
1277 // Build the create view statement
1278 wxStrcpy(sqlStmt, "CREATE VIEW ");
1279 wxStrcat(sqlStmt, viewName);
1280
1281 if (wxStrlen(colList))
1282 {
1283 wxStrcat(sqlStmt, " (");
1284 wxStrcat(sqlStmt, colList);
1285 wxStrcat(sqlStmt, ")");
1286 }
1287
1288 wxStrcat(sqlStmt, " AS ");
1289 wxStrcat(sqlStmt, pSqlStmt);
1290
1291 WriteSqlLog(sqlStmt);
1292
1293 #ifdef DBDEBUG_CONSOLE
1294 cout << sqlStmt << endl;
1295 #endif
1296
1297 return(ExecSql(sqlStmt));
1298
1299 } // wxDB::CreateView()
1300
1301
1302 /********** wxDB::DropView() **********/
1303 bool wxDB::DropView(const char *viewName)
1304 {
1305 /*
1306 * NOTE: This function returns TRUE if the View does not exist, but
1307 * only for identified databases. Code will need to be added
1308 * below for any other databases when those databases are defined
1309 * to handle this situation consistently
1310 */
1311 char sqlStmt[DB_MAX_STATEMENT_LEN];
1312
1313 sprintf(sqlStmt, "DROP VIEW %s", viewName);
1314
1315 WriteSqlLog(sqlStmt);
1316
1317 #ifdef DBDEBUG_CONSOLE
1318 cout << endl << sqlStmt << endl;
1319 #endif
1320
1321 if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt, SQL_NTS) != SQL_SUCCESS)
1322 {
1323 // Check for "Base table not found" error and ignore
1324 GetNextError(henv, hdbc, hstmt);
1325 if (wxStrcmp(sqlState,"S0002")) // "Base table not found"
1326 {
1327 // Check for product specific error codes
1328 if (!((Dbms() == dbmsSYBASE_ASA && !wxStrcmp(sqlState,"42000")))) // 5.x (and lower?)
1329 {
1330 DispNextError();
1331 DispAllErrors(henv, hdbc, hstmt);
1332 RollbackTrans();
1333 return(FALSE);
1334 }
1335 }
1336 }
1337
1338 // Commit the transaction
1339 if (! CommitTrans())
1340 return(FALSE);
1341
1342 return TRUE;
1343
1344 } // wxDB::DropView()
1345
1346
1347 /********** wxDB::ExecSql() **********/
1348 bool wxDB::ExecSql(const char *pSqlStmt)
1349 {
1350 SQLFreeStmt(hstmt, SQL_CLOSE);
1351 if (SQLExecDirect(hstmt, (UCHAR FAR *) pSqlStmt, SQL_NTS) == SQL_SUCCESS)
1352 return(TRUE);
1353 else
1354 {
1355 DispAllErrors(henv, hdbc, hstmt);
1356 return(FALSE);
1357 }
1358
1359 } // wxDB::ExecSql()
1360
1361
1362 /********** wxDB::GetNext() **********/
1363 bool wxDB::GetNext(void)
1364 {
1365 if (SQLFetch(hstmt) == SQL_SUCCESS)
1366 return(TRUE);
1367 else
1368 {
1369 DispAllErrors(henv, hdbc, hstmt);
1370 return(FALSE);
1371 }
1372
1373 } // wxDB::GetNext()
1374
1375
1376 /********** wxDB::GetData() **********/
1377 bool wxDB::GetData(UWORD colNo, SWORD cType, PTR pData, SDWORD maxLen, SDWORD FAR *cbReturned)
1378 {
1379 assert(pData);
1380 assert(cbReturned);
1381
1382 if (SQLGetData(hstmt, colNo, cType, pData, maxLen, cbReturned) == SQL_SUCCESS)
1383 return(TRUE);
1384 else
1385 {
1386 DispAllErrors(henv, hdbc, hstmt);
1387 return(FALSE);
1388 }
1389
1390 } // wxDB::GetData()
1391
1392
1393 /********** wxDB::GetKeyFields() **********/
1394 int wxDB::GetKeyFields(char *tableName, wxColInf* colInf,int noCols)
1395 {
1396 char szPkTable[DB_MAX_TABLE_NAME_LEN+1]; /* Primary key table name */
1397 char szFkTable[DB_MAX_TABLE_NAME_LEN+1]; /* Foreign key table name */
1398 short iKeySeq;
1399 // SQLSMALLINT iKeySeq;
1400 char szPkCol[DB_MAX_COLUMN_NAME_LEN+1]; /* Primary key column */
1401 char szFkCol[DB_MAX_COLUMN_NAME_LEN+1]; /* Foreign key column */
1402 SQLRETURN retcode;
1403 SDWORD cb;
1404 int i;
1405 wxString Temp0;
1406 /*
1407 * ---------------------------------------------------------------------
1408 * -- 19991224 : mj10777@gmx.net : Create ------
1409 * -- : Three things are done and stored here : ------
1410 * -- : 1) which Column(s) is/are Primary Key(s) ------
1411 * -- : 2) which tables use this Key as a Foreign Key ------
1412 * -- : 3) which columns are Foreign Key and the name ------
1413 * -- : of the Table where the Key is the Primary Key -----
1414 * -- : Called from GetColumns(char *tableName, ------
1415 * -- int *numCols,const char *userID ) ------
1416 * ---------------------------------------------------------------------
1417 */
1418
1419 /*---------------------------------------------------------------------*/
1420 /* Get the names of the columns in the primary key. */
1421 /*---------------------------------------------------------------------*/
1422 retcode = SQLPrimaryKeys(hstmt,
1423 NULL, 0, /* Catalog name */
1424 NULL, 0, /* Schema name */
1425 (UCHAR *) tableName, SQL_NTS); /* Table name */
1426
1427 /*---------------------------------------------------------------------*/
1428 /* Fetch and display the result set. This will be a list of the */
1429 /* columns in the primary key of the tableName table. */
1430 /*---------------------------------------------------------------------*/
1431 while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
1432 {
1433 retcode = SQLFetch(hstmt);
1434 if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
1435 {
1436 GetData( 4, SQL_C_CHAR, szPkCol, DB_MAX_COLUMN_NAME_LEN+1, &cb);
1437 GetData( 5, SQL_C_SSHORT, &iKeySeq, 0, &cb);
1438 //-------
1439 for (i=0;i<noCols;i++) // Find the Column name
1440 if (!wxStrcmp(colInf[i].colName,szPkCol)) // We have found the Column
1441 colInf[i].PkCol = iKeySeq; // Which Primary Key is this (first, second usw.) ?
1442 } // if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
1443 } // while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
1444 SQLFreeStmt(hstmt, SQL_CLOSE); /* Close the cursor (the hstmt is still allocated). */
1445
1446 /*---------------------------------------------------------------------*/
1447 /* Get all the foreign keys that refer to tableName primary key. */
1448 /*---------------------------------------------------------------------*/
1449 retcode = SQLForeignKeys(hstmt,
1450 NULL, 0, /* Primary catalog */
1451 NULL, 0, /* Primary schema */
1452 (UCHAR *)tableName, SQL_NTS, /* Primary table */
1453 NULL, 0, /* Foreign catalog */
1454 NULL, 0, /* Foreign schema */
1455 NULL, 0); /* Foreign table */
1456
1457 /*---------------------------------------------------------------------*/
1458 /* Fetch and display the result set. This will be all of the foreign */
1459 /* keys in other tables that refer to the tableName primary key. */
1460 /*---------------------------------------------------------------------*/
1461 Temp0.Empty();
1462 szPkCol[0] = 0;
1463 while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
1464 {
1465 retcode = SQLFetch(hstmt);
1466 if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
1467 {
1468 GetData( 3, SQL_C_CHAR, szPkTable, DB_MAX_TABLE_NAME_LEN+1, &cb);
1469 GetData( 4, SQL_C_CHAR, szPkCol, DB_MAX_COLUMN_NAME_LEN+1, &cb);
1470 GetData( 5, SQL_C_SSHORT, &iKeySeq, 0, &cb);
1471 GetData( 7, SQL_C_CHAR, szFkTable, DB_MAX_TABLE_NAME_LEN+1, &cb);
1472 GetData( 8, SQL_C_CHAR, szFkCol, DB_MAX_COLUMN_NAME_LEN+1, &cb);
1473 Temp0.Printf("%s[%s] ",Temp0.c_str(),szFkTable); // [ ] in case there is a blank in the Table name
1474 } // if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
1475 } // while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
1476 Temp0.Trim(); // Get rid of any unneeded blanks
1477 if (Temp0 != "")
1478 {
1479 for (i=0;i<noCols;i++) // Find the Column name
1480 if (!wxStrcmp(colInf[i].colName,szPkCol)) // We have found the Column, store the Information
1481 strcpy(colInf[i].PkTableName,Temp0); // Name of the Tables where this Primary Key is used as a Foreign Key
1482 } // if (Temp0 != "")
1483 SQLFreeStmt(hstmt, SQL_CLOSE); /* Close the cursor (the hstmt is still allocated). */
1484
1485 /*---------------------------------------------------------------------*/
1486 /* Get all the foreign keys in the tablename table. */
1487 /*---------------------------------------------------------------------*/
1488 retcode = SQLForeignKeys(hstmt,
1489 NULL, 0, /* Primary catalog */
1490 NULL, 0, /* Primary schema */
1491 NULL, 0, /* Primary table */
1492 NULL, 0, /* Foreign catalog */
1493 NULL, 0, /* Foreign schema */
1494 (UCHAR *)tableName, SQL_NTS); /* Foreign table */
1495
1496 /*---------------------------------------------------------------------*/
1497 /* Fetch and display the result set. This will be all of the */
1498 /* primary keys in other tables that are referred to by foreign */
1499 /* keys in the tableName table. */
1500 /*---------------------------------------------------------------------*/
1501 i = 0;
1502 while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
1503 {
1504 retcode = SQLFetch(hstmt);
1505 if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
1506 {
1507 GetData( 3, SQL_C_CHAR, szPkTable, DB_MAX_TABLE_NAME_LEN+1, &cb);
1508 GetData( 5, SQL_C_SSHORT, &iKeySeq, 0, &cb);
1509 GetData( 8, SQL_C_CHAR, szFkCol, DB_MAX_COLUMN_NAME_LEN+1, &cb);
1510 //-------
1511 for (i=0;i<noCols;i++) // Find the Column name
1512 {
1513 if (!wxStrcmp(colInf[i].colName,szFkCol)) // We have found the (Foreign Key) Column
1514 {
1515 colInf[i].FkCol = iKeySeq; // Which Foreign Key is this (first, second usw.) ?
1516 strcpy(colInf[i].FkTableName,szPkTable); // Name of the Table where this Foriegn is the Primary Key
1517 } // if (!wxStrcmp(colInf[i].colName,szFkCol))
1518 } // for (i=0;i<noCols;i++)
1519 } // if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
1520 } // while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO))
1521 SQLFreeStmt(hstmt, SQL_CLOSE); /* Close the cursor (the hstmt is still allocated). */
1522
1523 /*---------------------------------------------------------------------*/
1524 return TRUE;
1525 } // wxDB::GetKeyFields()
1526
1527
1528 /********** wxDB::GetColumns() **********/
1529 wxColInf *wxDB::GetColumns(char *tableName[], const char *userID)
1530 /*
1531 * 1) The last array element of the tableName[] argument must be zero (null).
1532 * This is how the end of the array is detected.
1533 * 2) This function returns an array of wxColInf structures. If no columns
1534 * were found, or an error occured, this pointer will be zero (null). THE
1535 * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
1536 * IS FINISHED WITH IT. i.e.
1537 *
1538 * wxColInf *colInf = pDb->GetColumns(tableList, userID);
1539 * if (colInf)
1540 * {
1541 * // Use the column inf
1542 * .......
1543 * // Destroy the memory
1544 * delete [] colInf;
1545 * }
1546 *
1547 * userID is evaluated in the following manner:
1548 * userID == NULL ... UserID is ignored
1549 * userID == "" ... UserID set equal to 'this->uid'
1550 * userID != "" ... UserID set equal to 'userID'
1551 *
1552 * NOTE: ALL column bindings associated with this wxDB instance are unbound
1553 * by this function. This function should use its own wxDB instance
1554 * to avoid undesired unbinding of columns.
1555 */
1556 {
1557 int noCols = 0;
1558 int colNo = 0;
1559 wxColInf *colInf = 0;
1560
1561 RETCODE retcode;
1562 SDWORD cb;
1563
1564 wxString UserID;
1565 wxString TableName;
1566
1567 if (userID)
1568 {
1569 if (!wxStrlen(userID))
1570 UserID = uid;
1571 else
1572 UserID = userID;
1573 }
1574 else
1575 UserID = "";
1576
1577 // dBase does not use user names, and some drivers fail if you try to pass one
1578 if (Dbms() == dbmsDBASE)
1579 UserID = "";
1580
1581 // Oracle user names may only be in uppercase, so force
1582 // the name to uppercase
1583 if (Dbms() == dbmsORACLE)
1584 UserID = UserID.Upper();
1585
1586 // Pass 1 - Determine how many columns there are.
1587 // Pass 2 - Allocate the wxColInf array and fill in
1588 // the array with the column information.
1589 int pass;
1590 for (pass = 1; pass <= 2; pass++)
1591 {
1592 if (pass == 2)
1593 {
1594 if (noCols == 0) // Probably a bogus table name(s)
1595 break;
1596 // Allocate n wxColInf objects to hold the column information
1597 colInf = new wxColInf[noCols+1];
1598 if (!colInf)
1599 break;
1600 // Mark the end of the array
1601 wxStrcpy(colInf[noCols].tableName, "");
1602 wxStrcpy(colInf[noCols].colName, "");
1603 colInf[noCols].sqlDataType = 0;
1604 }
1605 // Loop through each table name
1606 int tbl;
1607 for (tbl = 0; tableName[tbl]; tbl++)
1608 {
1609 TableName = tableName[tbl];
1610 // Oracle table names are uppercase only, so force
1611 // the name to uppercase just in case programmer forgot to do this
1612 if (Dbms() == dbmsORACLE)
1613 TableName = TableName.Upper();
1614
1615 SQLFreeStmt(hstmt, SQL_CLOSE);
1616
1617 // MySQL and Access cannot accept a user name when looking up column names, so we
1618 // use the call below that leaves out the user name
1619 if (wxStrcmp(UserID.GetData(),"") &&
1620 Dbms() != dbmsMY_SQL &&
1621 Dbms() != dbmsACCESS)
1622 {
1623 retcode = SQLColumns(hstmt,
1624 NULL, 0, // All qualifiers
1625 (UCHAR *) UserID.GetData(), SQL_NTS, // Owner
1626 (UCHAR *) TableName.GetData(), SQL_NTS,
1627 NULL, 0); // All columns
1628 }
1629 else
1630 {
1631 retcode = SQLColumns(hstmt,
1632 NULL, 0, // All qualifiers
1633 NULL, 0, // Owner
1634 (UCHAR *) TableName.GetData(), SQL_NTS,
1635 NULL, 0); // All columns
1636 }
1637 if (retcode != SQL_SUCCESS)
1638 { // Error occured, abort
1639 DispAllErrors(henv, hdbc, hstmt);
1640 if (colInf)
1641 delete [] colInf;
1642 SQLFreeStmt(hstmt, SQL_CLOSE);
1643 return(0);
1644 }
1645
1646 while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
1647 {
1648 if (pass == 1) // First pass, just add up the number of columns
1649 noCols++;
1650 else // Pass 2; Fill in the array of structures
1651 {
1652 if (colNo < noCols) // Some extra error checking to prevent memory overwrites
1653 {
1654 // NOTE: Only the ODBC 1.x fields are retrieved
1655 GetData( 1, SQL_C_CHAR, (UCHAR*) colInf[colNo].catalog, 128+1, &cb);
1656 GetData( 2, SQL_C_CHAR, (UCHAR*) colInf[colNo].schema, 128+1, &cb);
1657 GetData( 3, SQL_C_CHAR, (UCHAR*) colInf[colNo].tableName, DB_MAX_TABLE_NAME_LEN+1, &cb);
1658 GetData( 4, SQL_C_CHAR, (UCHAR*) colInf[colNo].colName, DB_MAX_COLUMN_NAME_LEN+1, &cb);
1659 GetData( 5, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].sqlDataType, 0, &cb);
1660 GetData( 6, SQL_C_CHAR, (UCHAR*) colInf[colNo].typeName, 128+1, &cb);
1661 GetData( 7, SQL_C_SLONG, (UCHAR*) &colInf[colNo].columnSize, 0, &cb);
1662 GetData( 8, SQL_C_SLONG, (UCHAR*) &colInf[colNo].bufferLength, 0, &cb);
1663 GetData( 9, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].decimalDigits,0, &cb);
1664 GetData(10, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].numPrecRadix, 0, &cb);
1665 GetData(11, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].nullable, 0, &cb);
1666 GetData(12, SQL_C_CHAR, (UCHAR*) colInf[colNo].remarks, 254+1, &cb);
1667
1668 // Determine the wxDB data type that is used to represent the native data type of this data source
1669 colInf[colNo].dbDataType = 0;
1670 if (!wxStricmp(typeInfVarchar.TypeName,colInf[colNo].typeName))
1671 colInf[colNo].dbDataType = DB_DATA_TYPE_VARCHAR;
1672 else if (!wxStricmp(typeInfInteger.TypeName,colInf[colNo].typeName))
1673 colInf[colNo].dbDataType = DB_DATA_TYPE_INTEGER;
1674 else if (!wxStricmp(typeInfFloat.TypeName,colInf[colNo].typeName))
1675 colInf[colNo].dbDataType = DB_DATA_TYPE_FLOAT;
1676 else if (!wxStricmp(typeInfDate.TypeName,colInf[colNo].typeName))
1677 colInf[colNo].dbDataType = DB_DATA_TYPE_DATE;
1678
1679 colNo++;
1680 }
1681 }
1682 }
1683 if (retcode != SQL_NO_DATA_FOUND)
1684 { // Error occured, abort
1685 DispAllErrors(henv, hdbc, hstmt);
1686 if (colInf)
1687 delete [] colInf;
1688 SQLFreeStmt(hstmt, SQL_CLOSE);
1689 return(0);
1690 }
1691 }
1692 }
1693
1694 SQLFreeStmt(hstmt, SQL_CLOSE);
1695 return colInf;
1696
1697 } // wxDB::GetColumns()
1698
1699
1700 /********** wxDB::GetColumns() **********/
1701 wxColInf *wxDB::GetColumns(char *tableName, int *numCols, const char *userID)
1702 /*
1703 * Same as the above GetColumns() function except this one gets columns
1704 * only for a single table, and if 'numCols' is not NULL, the number of
1705 * columns stored in the returned wxColInf is set in '*numCols'
1706 *
1707 * userID is evaluated in the following manner:
1708 * userID == NULL ... UserID is ignored
1709 * userID == "" ... UserID set equal to 'this->uid'
1710 * userID != "" ... UserID set equal to 'userID'
1711 *
1712 * NOTE: ALL column bindings associated with this wxDB instance are unbound
1713 * by this function. This function should use its own wxDB instance
1714 * to avoid undesired unbinding of columns.
1715 */
1716 {
1717 int noCols = 0;
1718 int colNo = 0;
1719 wxColInf *colInf = 0;
1720
1721 RETCODE retcode;
1722 SDWORD cb;
1723
1724 wxString UserID;
1725 wxString TableName;
1726
1727 if (userID)
1728 {
1729 if (!wxStrlen(userID))
1730 UserID = uid;
1731 else
1732 UserID = userID;
1733 }
1734 else
1735 UserID = "";
1736
1737 // dBase does not use user names, and some drivers fail if you try to pass one
1738 if (Dbms() == dbmsDBASE)
1739 UserID = "";
1740
1741 // Oracle user names may only be in uppercase, so force
1742 // the name to uppercase
1743 if (Dbms() == dbmsORACLE)
1744 UserID = UserID.Upper();
1745
1746 // Pass 1 - Determine how many columns there are.
1747 // Pass 2 - Allocate the wxColInf array and fill in
1748 // the array with the column information.
1749 int pass;
1750 for (pass = 1; pass <= 2; pass++)
1751 {
1752 if (pass == 2)
1753 {
1754 if (noCols == 0) // Probably a bogus table name(s)
1755 break;
1756 // Allocate n wxColInf objects to hold the column information
1757 colInf = new wxColInf[noCols+1];
1758 if (!colInf)
1759 break;
1760 // Mark the end of the array
1761 wxStrcpy(colInf[noCols].tableName, "");
1762 wxStrcpy(colInf[noCols].colName, "");
1763 colInf[noCols].sqlDataType = 0;
1764 }
1765
1766 TableName = tableName;
1767 // Oracle table names are uppercase only, so force
1768 // the name to uppercase just in case programmer forgot to do this
1769 if (Dbms() == dbmsORACLE)
1770 TableName = TableName.Upper();
1771
1772 SQLFreeStmt(hstmt, SQL_CLOSE);
1773
1774 // MySQL and Access cannot accept a user name when looking up column names, so we
1775 // use the call below that leaves out the user name
1776 if (wxStrcmp(UserID.GetData(),"") &&
1777 Dbms() != dbmsMY_SQL &&
1778 Dbms() != dbmsACCESS)
1779 {
1780 retcode = SQLColumns(hstmt,
1781 NULL, 0, // All qualifiers
1782 (UCHAR *) UserID.GetData(), SQL_NTS, // Owner
1783 (UCHAR *) TableName.GetData(), SQL_NTS,
1784 NULL, 0); // All columns
1785 }
1786 else
1787 {
1788 retcode = SQLColumns(hstmt,
1789 NULL, 0, // All qualifiers
1790 NULL, 0, // Owner
1791 (UCHAR *) TableName.GetData(), SQL_NTS,
1792 NULL, 0); // All columns
1793 }
1794 if (retcode != SQL_SUCCESS)
1795 { // Error occured, abort
1796 DispAllErrors(henv, hdbc, hstmt);
1797 if (colInf)
1798 delete [] colInf;
1799 SQLFreeStmt(hstmt, SQL_CLOSE);
1800 if (numCols)
1801 *numCols = 0;
1802 return(0);
1803 }
1804
1805 while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
1806 {
1807 if (pass == 1) // First pass, just add up the number of columns
1808 noCols++;
1809 else // Pass 2; Fill in the array of structures
1810 {
1811 if (colNo < noCols) // Some extra error checking to prevent memory overwrites
1812 {
1813 // NOTE: Only the ODBC 1.x fields are retrieved
1814 GetData( 1, SQL_C_CHAR, (UCHAR*) colInf[colNo].catalog, 128+1, &cb);
1815 GetData( 2, SQL_C_CHAR, (UCHAR*) colInf[colNo].schema, 128+1, &cb);
1816 GetData( 3, SQL_C_CHAR, (UCHAR*) colInf[colNo].tableName, DB_MAX_TABLE_NAME_LEN+1, &cb);
1817 GetData( 4, SQL_C_CHAR, (UCHAR*) colInf[colNo].colName, DB_MAX_COLUMN_NAME_LEN+1, &cb);
1818 GetData( 5, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].sqlDataType, 0, &cb);
1819 GetData( 6, SQL_C_CHAR, (UCHAR*) colInf[colNo].typeName, 128+1, &cb);
1820 GetData( 7, SQL_C_SLONG, (UCHAR*) &colInf[colNo].columnSize, 0, &cb);
1821 // BJO 991214 : SQL_C_SSHORT instead of SQL_C_SLONG, otherwise fails on Sparc (probably all 64 bit architectures)
1822 GetData( 8, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].bufferLength, 0, &cb);
1823 GetData( 9, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].decimalDigits,0, &cb);
1824 GetData(10, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].numPrecRadix, 0, &cb);
1825 GetData(11, SQL_C_SSHORT, (UCHAR*) &colInf[colNo].nullable, 0, &cb);
1826 GetData(12, SQL_C_CHAR, (UCHAR*) colInf[colNo].remarks, 254+1, &cb);
1827 // Start Values for Primary/Foriegn Key (=No)
1828 colInf[colNo].PkCol = 0; // Primary key column 0=No; 1= First Key, 2 = Second Key etc.
1829 colInf[colNo].PkTableName[0] = 0; // Tablenames where Primary Key is used as a Foreign Key
1830 colInf[colNo].FkCol = 0; // Foreign key column 0=No; 1= First Key, 2 = Second Key etc.
1831 colInf[colNo].FkTableName[0] = 0; // Foreign key table name
1832
1833 // Determine the wxDB data type that is used to represent the native data type of this data source
1834 colInf[colNo].dbDataType = 0;
1835 if (!wxStricmp(typeInfVarchar.TypeName,colInf[colNo].typeName))
1836 colInf[colNo].dbDataType = DB_DATA_TYPE_VARCHAR;
1837 else if (!wxStricmp(typeInfInteger.TypeName,colInf[colNo].typeName))
1838 colInf[colNo].dbDataType = DB_DATA_TYPE_INTEGER;
1839 else if (!wxStricmp(typeInfFloat.TypeName,colInf[colNo].typeName))
1840 colInf[colNo].dbDataType = DB_DATA_TYPE_FLOAT;
1841 else if (!wxStricmp(typeInfDate.TypeName,colInf[colNo].typeName))
1842 colInf[colNo].dbDataType = DB_DATA_TYPE_DATE;
1843
1844 colNo++;
1845 }
1846 }
1847 }
1848 if (retcode != SQL_NO_DATA_FOUND)
1849 { // Error occured, abort
1850 DispAllErrors(henv, hdbc, hstmt);
1851 if (colInf)
1852 delete [] colInf;
1853 SQLFreeStmt(hstmt, SQL_CLOSE);
1854 if (numCols)
1855 *numCols = 0;
1856 return(0);
1857 }
1858 }
1859
1860 SQLFreeStmt(hstmt, SQL_CLOSE);
1861
1862 // Store Primary and Foriegn Keys
1863 GetKeyFields(tableName,colInf,noCols);
1864
1865 if (numCols)
1866 *numCols = noCols;
1867 return colInf;
1868
1869 } // wxDB::GetColumns()
1870
1871
1872 /********** wxDB::GetColumnCount() **********/
1873 int wxDB::GetColumnCount(char *tableName, const char *userID)
1874 /*
1875 * Returns a count of how many columns are in a table.
1876 * If an error occurs in computing the number of columns
1877 * this function will return a -1 for the count
1878 *
1879 * userID is evaluated in the following manner:
1880 * userID == NULL ... UserID is ignored
1881 * userID == "" ... UserID set equal to 'this->uid'
1882 * userID != "" ... UserID set equal to 'userID'
1883 *
1884 * NOTE: ALL column bindings associated with this wxDB instance are unbound
1885 * by this function. This function should use its own wxDB instance
1886 * to avoid undesired unbinding of columns.
1887 */
1888 {
1889 int noCols = 0;
1890
1891 RETCODE retcode;
1892
1893 wxString UserID;
1894 wxString TableName;
1895
1896 if (userID)
1897 {
1898 if (!wxStrlen(userID))
1899 UserID = uid;
1900 else
1901 UserID = userID;
1902 }
1903 else
1904 UserID = "";
1905
1906 // dBase does not use user names, and some drivers fail if you try to pass one
1907 if (Dbms() == dbmsDBASE)
1908 UserID = "";
1909
1910 // Oracle user names may only be in uppercase, so force
1911 // the name to uppercase
1912 if (Dbms() == dbmsORACLE)
1913 UserID = UserID.Upper();
1914
1915 {
1916 // Loop through each table name
1917 {
1918 TableName = tableName;
1919 // Oracle table names are uppercase only, so force
1920 // the name to uppercase just in case programmer forgot to do this
1921 if (Dbms() == dbmsORACLE)
1922 TableName = TableName.Upper();
1923
1924 SQLFreeStmt(hstmt, SQL_CLOSE);
1925
1926 // MySQL and Access cannot accept a user name when looking up column names, so we
1927 // use the call below that leaves out the user name
1928 if (wxStrcmp(UserID.GetData(),"") &&
1929 Dbms() != dbmsMY_SQL &&
1930 Dbms() != dbmsACCESS)
1931 {
1932 retcode = SQLColumns(hstmt,
1933 NULL, 0, // All qualifiers
1934 (UCHAR *) UserID.GetData(), SQL_NTS, // Owner
1935 (UCHAR *) TableName.GetData(), SQL_NTS,
1936 NULL, 0); // All columns
1937 }
1938 else
1939 {
1940 retcode = SQLColumns(hstmt,
1941 NULL, 0, // All qualifiers
1942 NULL, 0, // Owner
1943 (UCHAR *) TableName.GetData(), SQL_NTS,
1944 NULL, 0); // All columns
1945 }
1946 if (retcode != SQL_SUCCESS)
1947 { // Error occured, abort
1948 DispAllErrors(henv, hdbc, hstmt);
1949 SQLFreeStmt(hstmt, SQL_CLOSE);
1950 return(-1);
1951 }
1952
1953 // Count the columns
1954 while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
1955 noCols++;
1956
1957 if (retcode != SQL_NO_DATA_FOUND)
1958 { // Error occured, abort
1959 DispAllErrors(henv, hdbc, hstmt);
1960 SQLFreeStmt(hstmt, SQL_CLOSE);
1961 return(-1);
1962 }
1963 }
1964 }
1965
1966 SQLFreeStmt(hstmt, SQL_CLOSE);
1967 return noCols;
1968
1969 } // wxDB::GetColumnCount()
1970
1971
1972 /********** wxDB::GetCatalog() *******/
1973 wxDbInf *wxDB::GetCatalog(char *userID)
1974 /*
1975 * ---------------------------------------------------------------------
1976 * -- 19991203 : mj10777@gmx.net : Create ------
1977 * -- : Creates a wxDbInf with Tables / Cols Array ------
1978 * -- : uses SQLTables and fills pTableInf; ------
1979 * -- : pColInf is set to NULL and numCols to 0; ------
1980 * -- : returns pDbInf (wxDbInf) ------
1981 * -- - if unsuccesfull (pDbInf == NULL) ------
1982 * -- : pColInf can be filled with GetColumns(..); ------
1983 * -- : numCols can be filled with GetColumnCount(..); ------
1984 * ---------------------------------------------------------------------
1985 *
1986 * userID is evaluated in the following manner:
1987 * userID == NULL ... UserID is ignored
1988 * userID == "" ... UserID set equal to 'this->uid'
1989 * userID != "" ... UserID set equal to 'userID'
1990 *
1991 * NOTE: ALL column bindings associated with this wxDB instance are unbound
1992 * by this function. This function should use its own wxDB instance
1993 * to avoid undesired unbinding of columns.
1994 */
1995 {
1996 wxDbInf *pDbInf = NULL; // Array of catalog entries
1997 int noTab = 0; // Counter while filling table entries
1998 int i = 0;
1999 int pass;
2000 RETCODE retcode;
2001 SDWORD cb;
2002 char tblNameSave[DB_MAX_TABLE_NAME_LEN+1];
2003
2004 wxString UserID;
2005
2006 if (userID)
2007 {
2008 if (!wxStrlen(userID))
2009 UserID = uid;
2010 else
2011 UserID = userID;
2012 }
2013 else
2014 UserID = "";
2015
2016 // dBase does not use user names, and some drivers fail if you try to pass one
2017 if (Dbms() == dbmsDBASE)
2018 UserID = "";
2019
2020 // Oracle user names may only be in uppercase, so force
2021 // the name to uppercase
2022 if (Dbms() == dbmsORACLE)
2023 UserID = UserID.Upper();
2024
2025 //-------------------------------------------------------------
2026 pDbInf = new wxDbInf; // Create the Database Arrray
2027 pDbInf->catalog[0] = 0;
2028 pDbInf->schema[0] = 0;
2029 pDbInf->numTables = 0; // Counter for Tables
2030 pDbInf->pTableInf = NULL; // Array of Tables
2031 //-------------------------------------------------------------
2032 // Table Information
2033 // Pass 1 - Determine how many Tables there are.
2034 // Pass 2 - Create the Table array and fill it
2035 // - Create the Cols array = NULL
2036 //-------------------------------------------------------------
2037 for (pass = 1; pass <= 2; pass++)
2038 {
2039 SQLFreeStmt(hstmt, SQL_CLOSE); // Close if Open
2040 strcpy(tblNameSave,"");
2041
2042 if (wxStrcmp(UserID.GetData(),"") &&
2043 Dbms() != dbmsMY_SQL &&
2044 Dbms() != dbmsACCESS)
2045 {
2046 retcode = SQLTables(hstmt,
2047 NULL, 0, // All qualifiers
2048 (UCHAR *) UserID.GetData(), SQL_NTS, // User specified
2049 NULL, 0, // All tables
2050 NULL, 0); // All columns
2051 }
2052 else
2053 {
2054 retcode = SQLTables(hstmt,
2055 NULL, 0, // All qualifiers
2056 NULL, 0, // User specified
2057 NULL, 0, // All tables
2058 NULL, 0); // All columns
2059 }
2060 if (retcode != SQL_SUCCESS)
2061 {
2062 DispAllErrors(henv, hdbc, hstmt);
2063 pDbInf = NULL;
2064 SQLFreeStmt(hstmt, SQL_CLOSE);
2065 return pDbInf;
2066 }
2067
2068 while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS) // Table Information
2069 {
2070 if (pass == 1) // First pass, just count the Tables
2071 {
2072 if (pDbInf->numTables == 0)
2073 {
2074 GetData( 1, SQL_C_CHAR, (UCHAR*) pDbInf->catalog, 128+1, &cb);
2075 GetData( 2, SQL_C_CHAR, (UCHAR*) pDbInf->schema, 128+1, &cb);
2076 }
2077 pDbInf->numTables++; // Counter for Tables
2078 } // if (pass == 1)
2079 if (pass == 2) // Create and fill the Table entries
2080 {
2081 if (pDbInf->pTableInf == NULL) // Has the Table Array been created
2082 { // no, then create the Array
2083 pDbInf->pTableInf = new wxTableInf[pDbInf->numTables];
2084 for (noTab=0;noTab<pDbInf->numTables;noTab++)
2085 {
2086 (pDbInf->pTableInf+noTab)->tableName[0] = 0;
2087 (pDbInf->pTableInf+noTab)->tableType[0] = 0;
2088 (pDbInf->pTableInf+noTab)->tableRemarks[0] = 0;
2089 (pDbInf->pTableInf+noTab)->numCols = 0;
2090 (pDbInf->pTableInf+noTab)->pColInf = NULL;
2091 }
2092 noTab = 0;
2093 } // if (pDbInf->pTableInf == NULL) // Has the Table Array been created
2094 GetData( 3, SQL_C_CHAR, (UCHAR*) (pDbInf->pTableInf+noTab)->tableName, DB_MAX_TABLE_NAME_LEN+1, &cb);
2095 GetData( 4, SQL_C_CHAR, (UCHAR*) (pDbInf->pTableInf+noTab)->tableType, 30+1, &cb);
2096 GetData( 5, SQL_C_CHAR, (UCHAR*) (pDbInf->pTableInf+noTab)->tableRemarks, 254+1, &cb);
2097 noTab++;
2098 } // if (pass == 2) We now know the amount of Tables
2099 } // while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
2100 } // for (pass = 1; pass <= 2; pass++)
2101 SQLFreeStmt(hstmt, SQL_CLOSE);
2102
2103 // Query how many columns are in each table
2104 for (noTab=0;noTab<pDbInf->numTables;noTab++)
2105 {
2106 (pDbInf->pTableInf+noTab)->numCols = GetColumnCount((pDbInf->pTableInf+noTab)->tableName,UserID);
2107 }
2108 return pDbInf;
2109 } // wxDB::GetCatalog()
2110
2111
2112 /********** wxDB::Catalog() **********/
2113 bool wxDB::Catalog(const char *userID, const char *fileName)
2114 /*
2115 * Creates the text file specified in 'filename' which will contain
2116 * a minimal data dictionary of all tables accessible by the user specified
2117 * in 'userID'
2118 *
2119 * userID is evaluated in the following manner:
2120 * userID == NULL ... UserID is ignored
2121 * userID == "" ... UserID set equal to 'this->uid'
2122 * userID != "" ... UserID set equal to 'userID'
2123 *
2124 * NOTE: ALL column bindings associated with this wxDB instance are unbound
2125 * by this function. This function should use its own wxDB instance
2126 * to avoid undesired unbinding of columns.
2127 */
2128 {
2129 assert(fileName && wxStrlen(fileName));
2130
2131 RETCODE retcode;
2132 SDWORD cb;
2133 char tblName[DB_MAX_TABLE_NAME_LEN+1];
2134 char tblNameSave[DB_MAX_TABLE_NAME_LEN+1];
2135 char colName[DB_MAX_COLUMN_NAME_LEN+1];
2136 SWORD sqlDataType;
2137 char typeName[30+1];
2138 SWORD precision, length;
2139
2140 wxString UserID;
2141
2142 FILE *fp = fopen(fileName,"wt");
2143 if (fp == NULL)
2144 return(FALSE);
2145
2146 SQLFreeStmt(hstmt, SQL_CLOSE);
2147
2148 if (userID)
2149 {
2150 if (!wxStrlen(userID))
2151 UserID = uid;
2152 else
2153 UserID = userID;
2154 }
2155 else
2156 UserID = "";
2157
2158 // dBase does not use user names, and some drivers fail if you try to pass one
2159 if (Dbms() == dbmsDBASE)
2160 UserID = "";
2161
2162 // Oracle user names may only be in uppercase, so force
2163 // the name to uppercase
2164 if (Dbms() == dbmsORACLE)
2165 UserID = UserID.Upper();
2166
2167 if (wxStrcmp(UserID.GetData(),"") &&
2168 Dbms() != dbmsMY_SQL &&
2169 Dbms() != dbmsACCESS)
2170 {
2171 retcode = SQLColumns(hstmt,
2172 NULL, 0, // All qualifiers
2173 (UCHAR *) UserID.GetData(), SQL_NTS, // User specified
2174 NULL, 0, // All tables
2175 NULL, 0); // All columns
2176 }
2177 else
2178 {
2179 retcode = SQLColumns(hstmt,
2180 NULL, 0, // All qualifiers
2181 NULL, 0, // User specified
2182 NULL, 0, // All tables
2183 NULL, 0); // All columns
2184 }
2185 if (retcode != SQL_SUCCESS)
2186 {
2187 DispAllErrors(henv, hdbc, hstmt);
2188 fclose(fp);
2189 return(FALSE);
2190 }
2191
2192 wxString outStr;
2193 wxStrcpy(tblNameSave,"");
2194 int cnt = 0;
2195
2196 while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
2197 {
2198 if (wxStrcmp(tblName,tblNameSave))
2199 {
2200 if (cnt)
2201 fputs("\n", fp);
2202 fputs("================================ ", fp);
2203 fputs("================================ ", fp);
2204 fputs("===================== ", fp);
2205 fputs("========= ", fp);
2206 fputs("=========\n", fp);
2207 outStr.sprintf("%-32s %-32s %-21s %9s %9s\n",
2208 "TABLE NAME", "COLUMN NAME", "DATA TYPE", "PRECISION", "LENGTH");
2209 fputs(outStr.GetData(), fp);
2210 fputs("================================ ", fp);
2211 fputs("================================ ", fp);
2212 fputs("===================== ", fp);
2213 fputs("========= ", fp);
2214 fputs("=========\n", fp);
2215 wxStrcpy(tblNameSave,tblName);
2216 }
2217
2218 GetData(3,SQL_C_CHAR, (UCHAR *)tblName, DB_MAX_TABLE_NAME_LEN+1, &cb);
2219 GetData(4,SQL_C_CHAR, (UCHAR *)colName, DB_MAX_COLUMN_NAME_LEN+1,&cb);
2220 GetData(5,SQL_C_SSHORT,(UCHAR *)&sqlDataType,0, &cb);
2221 GetData(6,SQL_C_CHAR, (UCHAR *)typeName, sizeof(typeName), &cb);
2222 GetData(7,SQL_C_SSHORT,(UCHAR *)&precision, 0, &cb);
2223 GetData(8,SQL_C_SSHORT,(UCHAR *)&length, 0, &cb);
2224
2225 outStr.sprintf("%-32s %-32s (%04d)%-15s %9d %9d\n",
2226 tblName, colName, sqlDataType, typeName, precision, length);
2227 if (fputs(outStr.GetData(), fp) == EOF)
2228 {
2229 SQLFreeStmt(hstmt, SQL_CLOSE);
2230 fclose(fp);
2231 return(FALSE);
2232 }
2233 cnt++;
2234 }
2235
2236 if (retcode != SQL_NO_DATA_FOUND)
2237 DispAllErrors(henv, hdbc, hstmt);
2238
2239 SQLFreeStmt(hstmt, SQL_CLOSE);
2240
2241 fclose(fp);
2242 return(retcode == SQL_NO_DATA_FOUND);
2243
2244 } // wxDB::Catalog()
2245
2246
2247 bool wxDB::TableExists(const char *tableName, const char *userID, const char *tablePath)
2248 /*
2249 * Table name can refer to a table, view, alias or synonym. Returns true
2250 * if the object exists in the database. This function does not indicate
2251 * whether or not the user has privleges to query or perform other functions
2252 * on the table.
2253 *
2254 * userID is evaluated in the following manner:
2255 * userID == NULL ... UserID is ignored
2256 * userID == "" ... UserID set equal to 'this->uid'
2257 * userID != "" ... UserID set equal to 'userID'
2258 */
2259 {
2260 wxString UserID;
2261 wxString TableName;
2262
2263 assert(tableName && wxStrlen(tableName));
2264
2265 if (Dbms() == dbmsDBASE)
2266 {
2267 wxString dbName;
2268 if (tablePath && wxStrlen(tablePath))
2269 dbName.sprintf("%s/%s.dbf",tablePath,tableName);
2270 else
2271 dbName.sprintf("%s.dbf",tableName);
2272
2273 bool exists;
2274 exists = wxFileExists(dbName.GetData());
2275 return exists;
2276 }
2277
2278 if (userID)
2279 {
2280 if (!wxStrlen(userID))
2281 UserID = uid;
2282 else
2283 UserID = userID;
2284 }
2285 else
2286 UserID = "";
2287
2288 // Oracle user names may only be in uppercase, so force
2289 // the name to uppercase
2290 if (Dbms() == dbmsORACLE)
2291 UserID = UserID.Upper();
2292
2293 TableName = tableName;
2294 // Oracle table names are uppercase only, so force
2295 // the name to uppercase just in case programmer forgot to do this
2296 if (Dbms() == dbmsORACLE)
2297 TableName = TableName.Upper();
2298
2299 SQLFreeStmt(hstmt, SQL_CLOSE);
2300 RETCODE retcode;
2301
2302 // MySQL and Access cannot accept a user name when looking up table names, so we
2303 // use the call below that leaves out the user name
2304 if (wxStrcmp(UserID,"") &&
2305 Dbms() != dbmsMY_SQL &&
2306 Dbms() != dbmsACCESS)
2307 {
2308 retcode = SQLTables(hstmt,
2309 NULL, 0, // All qualifiers
2310 (UCHAR *) UserID.GetData(), SQL_NTS, // All owners
2311 (UCHAR FAR *)TableName.GetData(), SQL_NTS,
2312 NULL, 0); // All table types
2313 }
2314 else
2315 {
2316 retcode = SQLTables(hstmt,
2317 NULL, 0, // All qualifiers
2318 NULL, 0, // All owners
2319 (UCHAR FAR *)TableName.GetData(), SQL_NTS,
2320 NULL, 0); // All table types
2321 }
2322 if (retcode != SQL_SUCCESS)
2323 return(DispAllErrors(henv, hdbc, hstmt));
2324
2325 retcode = SQLFetch(hstmt);
2326 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
2327 {
2328 SQLFreeStmt(hstmt, SQL_CLOSE);
2329 return(DispAllErrors(henv, hdbc, hstmt));
2330 }
2331
2332 SQLFreeStmt(hstmt, SQL_CLOSE);
2333 return(TRUE);
2334
2335 } // wxDB::TableExists()
2336
2337
2338 /********** wxDB::SqlLog() **********/
2339 bool wxDB::SqlLog(enum sqlLog state, const char *filename, bool append)
2340 {
2341 assert(state == sqlLogON || state == sqlLogOFF);
2342 assert(state == sqlLogOFF || filename);
2343
2344 if (state == sqlLogON)
2345 {
2346 if (fpSqlLog == 0)
2347 {
2348 fpSqlLog = fopen(filename, (append ? "at" : "wt"));
2349 if (fpSqlLog == NULL)
2350 return(FALSE);
2351 }
2352 }
2353 else // sqlLogOFF
2354 {
2355 if (fpSqlLog)
2356 {
2357 if (fclose(fpSqlLog))
2358 return(FALSE);
2359 fpSqlLog = 0;
2360 }
2361 }
2362
2363 sqlLogState = state;
2364 return(TRUE);
2365
2366 } // wxDB::SqlLog()
2367
2368
2369 /********** wxDB::WriteSqlLog() **********/
2370 bool wxDB::WriteSqlLog(const char *logMsg)
2371 {
2372 assert(logMsg);
2373
2374 if (fpSqlLog == 0 || sqlLogState == sqlLogOFF)
2375 return(FALSE);
2376
2377 if (fputs("\n", fpSqlLog) == EOF) return(FALSE);
2378 if (fputs(logMsg, fpSqlLog) == EOF) return(FALSE);
2379 if (fputs("\n", fpSqlLog) == EOF) return(FALSE);
2380
2381 return(TRUE);
2382
2383 } // wxDB::WriteSqlLog()
2384
2385
2386 /********** wxDB::Dbms() **********/
2387 DBMS wxDB::Dbms(void)
2388 /*
2389 * Be aware that not all database engines use the exact same syntax, and not
2390 * every ODBC compliant database is compliant to the same level of compliancy.
2391 * Some manufacturers support the minimum Level 1 compliancy, and others up
2392 * through Level 3. Others support subsets of features for levels above 1.
2393 *
2394 * If you find an inconsistency between the wxDB class and a specific database
2395 * engine, and an identifier to this section, and special handle the database in
2396 * the area where behavior is non-conforming with the other databases.
2397 *
2398 *
2399 * NOTES ABOUT ISSUES SPECIFIC TO EACH DATABASE ENGINE
2400 * ---------------------------------------------------
2401 *
2402 * ORACLE
2403 * - Currently the only database supported by the class to support VIEWS
2404 *
2405 * DBASE
2406 * - Does not support the SQL_TIMESTAMP structure
2407 * - Supports only one cursor and one connect (apparently? with Microsoft driver only?)
2408 * - Does not automatically create the primary index if the 'keyField' param of SetColDef
2409 * is TRUE. The user must create ALL indexes from their program.
2410 * - Table names can only be 8 characters long
2411 * - Column names can only be 10 characters long
2412 *
2413 * SYBASE (all)
2414 * - To lock a record during QUERY functions, the reserved word 'HOLDLOCK' must be added
2415 * after every table name involved in the query/join if that tables matching record(s)
2416 * are to be locked
2417 * - Ignores the keywords 'FOR UPDATE'. Use the HOLDLOCK functionality described above
2418 *
2419 * SYBASE (Enterprise)
2420 * - If a column is part of the Primary Key, the column cannot be NULL
2421 *
2422 * MY_SQL
2423 * - If a column is part of the Primary Key, the column cannot be NULL
2424 * - Cannot support selecting for update [::CanSelectForUpdate()]. Always returns FALSE
2425 *
2426 * POSTGRES
2427 * - Does not support the keywords 'ASC' or 'DESC' as of release v6.5.0
2428 *
2429 *
2430 */
2431 {
2432 wxChar baseName[25+1];
2433
2434 wxStrncpy(baseName,dbInf.dbmsName,25);
2435 if (!wxStricmp(dbInf.dbmsName,"Adaptive Server Anywhere"))
2436 return(dbmsSYBASE_ASA);
2437 if (!wxStricmp(dbInf.dbmsName,"SQL Server")) // Sybase Adaptive Server
2438 return(dbmsSYBASE_ASE);
2439 if (!wxStricmp(dbInf.dbmsName,"Microsoft SQL Server"))
2440 return(dbmsMS_SQL_SERVER);
2441 if (!wxStricmp(dbInf.dbmsName,"MySQL"))
2442 return(dbmsMY_SQL);
2443 if (!wxStricmp(dbInf.dbmsName,"PostgreSQL")) // v6.5.0
2444 return(dbmsPOSTGRES);
2445
2446 baseName[8] = 0;
2447 if (!wxStricmp(baseName,"Informix"))
2448 return(dbmsINFORMIX);
2449
2450 baseName[6] = 0;
2451 if (!wxStricmp(baseName,"Oracle"))
2452 return(dbmsORACLE);
2453 if (!wxStricmp(dbInf.dbmsName,"ACCESS"))
2454 return(dbmsACCESS);
2455 if (!wxStricmp(dbInf.dbmsName,"MySQL"))
2456 return(dbmsMY_SQL);
2457
2458 baseName[5] = 0;
2459 if (!wxStricmp(baseName,"DBASE"))
2460 return(dbmsDBASE);
2461
2462 return(dbmsUNIDENTIFIED);
2463 } // wxDB::Dbms()
2464
2465
2466 /********** GetDbConnection() **********/
2467 wxDB WXDLLEXPORT *GetDbConnection(DbStuff *pDbStuff, bool FwdOnlyCursors)
2468 {
2469 DbList *pList;
2470
2471 // Scan the linked list searching for an available database connection
2472 // that's already been opened but is currently not in use.
2473 for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
2474 {
2475 // The database connection must be for the same datasource
2476 // name and must currently not be in use.
2477 if (pList->Free && (! wxStrcmp(pDbStuff->Dsn, pList->Dsn))) // Found a free connection
2478 {
2479 pList->Free = FALSE;
2480 return(pList->PtrDb);
2481 }
2482 }
2483
2484 // No available connections. A new connection must be made and
2485 // appended to the end of the linked list.
2486 if (PtrBegDbList)
2487 {
2488 // Find the end of the list
2489 for (pList = PtrBegDbList; pList->PtrNext; pList = pList->PtrNext);
2490 // Append a new list item
2491 pList->PtrNext = new DbList;
2492 pList->PtrNext->PtrPrev = pList;
2493 pList = pList->PtrNext;
2494 }
2495 else // Empty list
2496 {
2497 // Create the first node on the list
2498 pList = PtrBegDbList = new DbList;
2499 pList->PtrPrev = 0;
2500 }
2501
2502 // Initialize new node in the linked list
2503 pList->PtrNext = 0;
2504 pList->Free = FALSE;
2505 wxStrcpy(pList->Dsn, pDbStuff->Dsn);
2506 pList->PtrDb = new wxDB(pDbStuff->Henv,FwdOnlyCursors);
2507
2508 // Connect to the datasource
2509 if (pList->PtrDb->Open(pDbStuff->Dsn, pDbStuff->Uid, pDbStuff->AuthStr))
2510 {
2511 pList->PtrDb->SqlLog(SQLLOGstate,SQLLOGfn,TRUE);
2512 return(pList->PtrDb);
2513 }
2514 else // Unable to connect, destroy list item
2515 {
2516 if (pList->PtrPrev)
2517 pList->PtrPrev->PtrNext = 0;
2518 else
2519 PtrBegDbList = 0; // Empty list again
2520 pList->PtrDb->CommitTrans(); // Commit any open transactions on wxDB object
2521 pList->PtrDb->Close(); // Close the wxDB object
2522 delete pList->PtrDb; // Deletes the wxDB object
2523 delete pList; // Deletes the linked list object
2524 return(0);
2525 }
2526
2527 } // GetDbConnection()
2528
2529
2530 /********** FreeDbConnection() **********/
2531 bool WXDLLEXPORT FreeDbConnection(wxDB *pDb)
2532 {
2533 DbList *pList;
2534
2535 // Scan the linked list searching for the database connection
2536 for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
2537 {
2538 if (pList->PtrDb == pDb) // Found it!!!
2539 return(pList->Free = TRUE);
2540 }
2541
2542 // Never found the database object, return failure
2543 return(FALSE);
2544
2545 } // FreeDbConnection()
2546
2547
2548 /********** CloseDbConnections() **********/
2549 void WXDLLEXPORT CloseDbConnections(void)
2550 {
2551 DbList *pList, *pNext;
2552
2553 // Traverse the linked list closing database connections and freeing memory as I go.
2554 for (pList = PtrBegDbList; pList; pList = pNext)
2555 {
2556 pNext = pList->PtrNext; // Save the pointer to next
2557 pList->PtrDb->CommitTrans(); // Commit any open transactions on wxDB object
2558 pList->PtrDb->Close(); // Close the wxDB object
2559 delete pList->PtrDb; // Deletes the wxDB object
2560 delete pList; // Deletes the linked list object
2561 }
2562
2563 // Mark the list as empty
2564 PtrBegDbList = 0;
2565
2566 } // CloseDbConnections()
2567
2568
2569 /********** NumberDbConnectionsInUse() **********/
2570 int WXDLLEXPORT NumberDbConnectionsInUse(void)
2571 {
2572 DbList *pList;
2573 int cnt = 0;
2574
2575 // Scan the linked list counting db connections that are currently in use
2576 for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
2577 {
2578 if (pList->Free == FALSE)
2579 cnt++;
2580 }
2581
2582 return(cnt);
2583
2584 } // NumberDbConnectionsInUse()
2585
2586
2587 /********** SqlLog() **********/
2588 bool SqlLog(enum sqlLog state, char *filename)
2589 {
2590 bool append = FALSE;
2591 DbList *pList;
2592
2593 for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
2594 {
2595 if (!pList->PtrDb->SqlLog(state,filename,append))
2596 return(FALSE);
2597 append = TRUE;
2598 }
2599
2600 SQLLOGstate = state;
2601 wxStrcpy(SQLLOGfn,filename);
2602
2603 return(TRUE);
2604
2605 } // SqlLog()
2606
2607
2608 /********** GetDataSource() **********/
2609 bool GetDataSource(HENV henv, char *Dsn, SWORD DsnMax, char *DsDesc, SWORD DsDescMax,
2610 UWORD direction)
2611 /*
2612 * Dsn and DsDesc will contain the data source name and data source
2613 * description upon return
2614 */
2615 {
2616 SWORD cb;
2617
2618 if (SQLDataSources(henv, direction, (UCHAR FAR *) Dsn, DsnMax, &cb,
2619 (UCHAR FAR *) DsDesc, DsDescMax, &cb) == SQL_SUCCESS)
2620 {
2621 DsDesc[cb+1] = 0; // Set the terminating character for the string
2622 return(TRUE);
2623 }
2624 else
2625 return(FALSE);
2626
2627 } // GetDataSource()
2628
2629 #endif
2630 // wxUSE_ODBC
2631