]> git.saurik.com Git - wxWidgets.git/blame - src/common/dbtable.cpp
Added chapter on collection and container classes to contents
[wxWidgets.git] / src / common / dbtable.cpp
CommitLineData
108106cf 1///////////////////////////////////////////////////////////////////////////////
1fc5dd6f 2// Name: dbtable.cpp
f6bcfd97 3// Purpose: Implementation of the wxDbTable class.
108106cf 4// Author: Doug Card
67e9aaa3 5// Modified by: George Tasker
3ca6a5f0
BP
6// Bart Jourquin
7// Mark Johnson
108106cf
JS
8// Created: 9.96
9// RCS-ID: $Id$
10// Copyright: (c) 1996 Remstar International, Inc.
11// Licence: wxWindows licence, plus:
1e92909e 12// Notice: This class library and its intellectual design are free of charge for use,
108106cf
JS
13// modification, enhancement, debugging under the following conditions:
14// 1) These classes may only be used as part of the implementation of a
15// wxWindows-based application
16// 2) All enhancements and bug fixes are to be submitted back to the wxWindows
17// user groups free of all charges for use with the wxWindows library.
18// 3) These classes may not be distributed as part of any other class library,
19// DLL, text (written or electronic), other than a complete distribution of
20// the wxWindows GUI development toolkit.
21///////////////////////////////////////////////////////////////////////////////
22
23/*
24// SYNOPSIS START
25// SYNOPSIS STOP
26*/
27
a2115c88
GT
28// Use this line for wxWindows v1.x
29//#include "wx_ver.h"
30// Use this line for wxWindows v2.x
a2115c88 31#include "wx/wxprec.h"
f6bcfd97 32#include "wx/version.h"
a2115c88
GT
33
34#if wxMAJOR_VERSION == 2
3ca6a5f0
BP
35 #ifdef __GNUG__
36 #pragma implementation "dbtable.h"
37 #endif
108106cf 38#endif
108106cf 39
0b8410f3
GT
40#ifdef DBDEBUG_CONSOLE
41 #include "wx/ioswrap.h"
42#endif
108106cf 43
f6bcfd97 44
108106cf 45#ifdef __BORLANDC__
1e92909e 46 #pragma hdrstop
108106cf
JS
47#endif //__BORLANDC__
48
a2115c88 49#if wxMAJOR_VERSION == 2
1e92909e
GT
50 #ifndef WX_PRECOMP
51 #include "wx/string.h"
52 #include "wx/object.h"
53 #include "wx/list.h"
54 #include "wx/utils.h"
55 #include "wx/msgdlg.h"
0efe4a98 56 #include "wx/log.h"
89894079
VZ
57 #endif
58 #include "wx/filefn.h"
a2115c88 59#endif
108106cf 60
a2115c88 61#if wxMAJOR_VERSION == 1
89894079
VZ
62# if defined(wx_msw) || defined(wx_x)
63# ifdef WX_PRECOMP
64# include "wx_prec.h"
65# else
66# include "wx.h"
67# endif
68# endif
69# define wxUSE_ODBC 1
a2115c88 70#endif
108106cf 71
f6bcfd97 72
a2115c88 73#if wxUSE_ODBC
108106cf
JS
74
75#include <stdio.h>
76#include <stdlib.h>
77#include <string.h>
1fc5dd6f 78#include <assert.h>
67e9aaa3 79
a2115c88 80#if wxMAJOR_VERSION == 1
89894079 81 #include "table.h"
a2115c88 82#elif wxMAJOR_VERSION == 2
89894079 83 #include "wx/dbtable.h"
a2115c88 84#endif
108106cf 85
1fc5dd6f 86#ifdef __UNIX__
108106cf
JS
87// The HPUX preprocessor lines below were commented out on 8/20/97
88// because macros.h currently redefines DEBUG and is unneeded.
89// # ifdef HPUX
90// # include <macros.h>
91// # endif
1fc5dd6f 92# ifdef LINUX
108106cf
JS
93# include <sys/minmax.h>
94# endif
95#endif
96
a2115c88
GT
97ULONG lastTableID = 0;
98
99
e041ce57 100#ifdef __WXDEBUG__
89894079 101 wxList TablesInUse;
a2115c88
GT
102#endif
103
104
f6bcfd97
BP
105/********** wxDbTable::wxDbTable() **********/
106wxDbTable::wxDbTable(wxDb *pwxDb, const char *tblName, const int nCols,
89894079 107 const char *qryTblName, bool qryOnly, const char *tblPath)
108106cf 108{
f6bcfd97 109 pDb = pwxDb; // Pointer to the wxDb object
89894079
VZ
110 henv = 0;
111 hdbc = 0;
112 hstmt = 0;
113 hstmtDefault = 0; // Initialized below
114 hstmtCount = 0; // Initialized first time it is needed
115 hstmtInsert = 0;
116 hstmtDelete = 0;
117 hstmtUpdate = 0;
118 hstmtInternal = 0;
119 colDefs = 0;
120 tableID = 0;
121 noCols = nCols; // No. of cols in the table
f6bcfd97
BP
122 where = ""; // Where clause
123 orderBy = ""; // Order By clause
124 from = ""; // From clause
89894079
VZ
125 selectForUpdate = FALSE; // SELECT ... FOR UPDATE; Indicates whether to include the FOR UPDATE phrase
126 queryOnly = qryOnly;
f6bcfd97 127 insertable = TRUE;
89894079
VZ
128
129 assert (tblName);
130
131 wxStrcpy(tableName, tblName); // Table Name
132 if (tblPath)
133 wxStrcpy(tablePath, tblPath); // Table Path - used for dBase files
3ca6a5f0 134
89894079
VZ
135 if (qryTblName) // Name of the table/view to query
136 wxStrcpy(queryTableName, qryTblName);
137 else
138 wxStrcpy(queryTableName, tblName);
3ca6a5f0 139
89894079
VZ
140 if (!pDb)
141 return;
3ca6a5f0 142
f6bcfd97 143 pDb->incrementTableCount();
3ca6a5f0 144
1e92909e 145 wxString s;
89894079 146 tableID = ++lastTableID;
f6bcfd97 147 s.sprintf("wxDbTable constructor (%-20s) tableID:[%6lu] pDb:[%p]", tblName,tableID,pDb);
3ca6a5f0 148
e041ce57 149#ifdef __WXDEBUG__
8128349e
GT
150 wxTablesInUse *tableInUse;
151 tableInUse = new wxTablesInUse();
89894079 152 tableInUse->tableName = tblName;
1e92909e
GT
153 tableInUse->tableID = tableID;
154 tableInUse->pDb = pDb;
89894079 155 TablesInUse.Append(tableInUse);
a2115c88 156#endif
3ca6a5f0 157
f6bcfd97 158 pDb->WriteSqlLog(s.c_str());
3ca6a5f0 159
f6bcfd97
BP
160 // Grab the HENV and HDBC from the wxDb object
161 henv = pDb->GetHENV();
162 hdbc = pDb->GetHDBC();
3ca6a5f0 163
89894079
VZ
164 // Allocate space for column definitions
165 if (noCols)
f6bcfd97 166 colDefs = new wxDbColDef[noCols]; // Points to the first column defintion
3ca6a5f0 167
89894079
VZ
168 // Allocate statement handles for the table
169 if (!queryOnly)
170 {
171 // Allocate a separate statement handle for performing inserts
172 if (SQLAllocStmt(hdbc, &hstmtInsert) != SQL_SUCCESS)
173 pDb->DispAllErrors(henv, hdbc);
174 // Allocate a separate statement handle for performing deletes
175 if (SQLAllocStmt(hdbc, &hstmtDelete) != SQL_SUCCESS)
176 pDb->DispAllErrors(henv, hdbc);
177 // Allocate a separate statement handle for performing updates
178 if (SQLAllocStmt(hdbc, &hstmtUpdate) != SQL_SUCCESS)
179 pDb->DispAllErrors(henv, hdbc);
180 }
181 // Allocate a separate statement handle for internal use
182 if (SQLAllocStmt(hdbc, &hstmtInternal) != SQL_SUCCESS)
183 pDb->DispAllErrors(henv, hdbc);
3ca6a5f0 184
89894079
VZ
185 // Set the cursor type for the statement handles
186 cursorType = SQL_CURSOR_STATIC;
3ca6a5f0 187
89894079 188 if (SQLSetStmtOption(hstmtInternal, SQL_CURSOR_TYPE, cursorType) != SQL_SUCCESS)
3ca6a5f0 189 {
89894079
VZ
190 // Check to see if cursor type is supported
191 pDb->GetNextError(henv, hdbc, hstmtInternal);
192 if (! wxStrcmp(pDb->sqlState, "01S02")) // Option Value Changed
3ca6a5f0 193 {
89894079
VZ
194 // Datasource does not support static cursors. Driver
195 // will substitute a cursor type. Call SQLGetStmtOption()
196 // to determine which cursor type was selected.
197 if (SQLGetStmtOption(hstmtInternal, SQL_CURSOR_TYPE, &cursorType) != SQL_SUCCESS)
3ca6a5f0 198 pDb->DispAllErrors(henv, hdbc, hstmtInternal);
a2115c88 199#ifdef DBDEBUG_CONSOLE
89894079
VZ
200 cout << "Static cursor changed to: ";
201 switch(cursorType)
3ca6a5f0
BP
202 {
203 case SQL_CURSOR_FORWARD_ONLY:
204 cout << "Forward Only";
205 break;
206 case SQL_CURSOR_STATIC:
207 cout << "Static";
208 break;
209 case SQL_CURSOR_KEYSET_DRIVEN:
210 cout << "Keyset Driven";
211 break;
212 case SQL_CURSOR_DYNAMIC:
213 cout << "Dynamic";
214 break;
215 }
89894079 216 cout << endl << endl;
108106cf 217#endif
3ca6a5f0
BP
218 // BJO20000425
219 if (pDb->FwdOnlyCursors() && cursorType != SQL_CURSOR_FORWARD_ONLY)
220 {
221 // Force the use of a forward only cursor...
222 cursorType = SQL_CURSOR_FORWARD_ONLY;
223 if (SQLSetStmtOption(hstmtInternal, SQL_CURSOR_TYPE, cursorType) != SQL_SUCCESS)
224 {
225 // Should never happen
226 pDb->GetNextError(henv, hdbc, hstmtInternal);
227 return;
228 }
229 }
230 }
231 else
232 {
89894079
VZ
233 pDb->DispNextError();
234 pDb->DispAllErrors(henv, hdbc, hstmtInternal);
3ca6a5f0 235 }
89894079 236 }
a2115c88 237#ifdef DBDEBUG_CONSOLE
89894079
VZ
238 else
239 cout << "Cursor Type set to STATIC" << endl << endl;
108106cf 240#endif
3ca6a5f0 241
89894079
VZ
242 if (!queryOnly)
243 {
244 // Set the cursor type for the INSERT statement handle
245 if (SQLSetStmtOption(hstmtInsert, SQL_CURSOR_TYPE, SQL_CURSOR_FORWARD_ONLY) != SQL_SUCCESS)
246 pDb->DispAllErrors(henv, hdbc, hstmtInsert);
247 // Set the cursor type for the DELETE statement handle
248 if (SQLSetStmtOption(hstmtDelete, SQL_CURSOR_TYPE, SQL_CURSOR_FORWARD_ONLY) != SQL_SUCCESS)
249 pDb->DispAllErrors(henv, hdbc, hstmtDelete);
250 // Set the cursor type for the UPDATE statement handle
251 if (SQLSetStmtOption(hstmtUpdate, SQL_CURSOR_TYPE, SQL_CURSOR_FORWARD_ONLY) != SQL_SUCCESS)
252 pDb->DispAllErrors(henv, hdbc, hstmtUpdate);
253 }
3ca6a5f0 254
89894079 255 // Make the default cursor the active cursor
f6bcfd97 256 hstmtDefault = GetNewCursor(FALSE,FALSE);
89894079
VZ
257 assert(hstmtDefault);
258 hstmt = *hstmtDefault;
3ca6a5f0 259
f6bcfd97 260} // wxDbTable::wxDbTable()
108106cf 261
67e9aaa3 262
f6bcfd97
BP
263/********** wxDbTable::~wxDbTable() **********/
264wxDbTable::~wxDbTable()
108106cf 265{
1e92909e 266 wxString s;
89894079
VZ
267 if (pDb)
268 {
f6bcfd97
BP
269 s.sprintf("wxDbTable destructor (%-20s) tableID:[%6lu] pDb:[%p]", tableName,tableID,pDb);
270 pDb->WriteSqlLog(s.c_str());
89894079 271 }
a2115c88 272
e041ce57 273#ifdef __WXDEBUG__
89894079
VZ
274 if (tableID)
275 {
a4086039 276 TablesInUse.DeleteContents(TRUE);
89894079 277 bool found = FALSE;
1e92909e 278
0b8410f3 279 wxNode *pNode;
89894079
VZ
280 pNode = TablesInUse.First();
281 while (pNode && !found)
282 {
8128349e 283 if (((wxTablesInUse *)pNode->Data())->tableID == tableID)
89894079
VZ
284 {
285 found = TRUE;
286 if (!TablesInUse.DeleteNode(pNode))
f6bcfd97 287 wxLogDebug (s.c_str(),wxT("Unable to delete node!"));
89894079
VZ
288 }
289 else
290 pNode = pNode->Next();
291 }
292 if (!found)
293 {
1e92909e 294 wxString msg;
f6bcfd97
BP
295 msg.sprintf(wxT("Unable to find the tableID in the linked\nlist of tables in use.\n\n%s"),s.c_str());
296 wxLogDebug (msg.c_str(),wxT("NOTICE..."));
89894079
VZ
297 }
298 }
a2115c88 299#endif
e041ce57 300
f6bcfd97 301 // Decrement the wxDb table count
89894079 302 if (pDb)
f6bcfd97 303 pDb->decrementTableCount();
89894079
VZ
304
305 // Delete memory allocated for column definitions
306 if (colDefs)
307 delete [] colDefs;
308
309 // Free statement handles
310 if (!queryOnly)
311 {
312 if (hstmtInsert)
313 if (SQLFreeStmt(hstmtInsert, SQL_DROP) != SQL_SUCCESS)
314 pDb->DispAllErrors(henv, hdbc);
7c5c05ae 315
89894079
VZ
316 if (hstmtDelete)
317 if (SQLFreeStmt(hstmtDelete, SQL_DROP) != SQL_SUCCESS)
7c5c05ae 318
89894079
VZ
319 if (hstmtUpdate)
320 if (SQLFreeStmt(hstmtUpdate, SQL_DROP) != SQL_SUCCESS)
321 pDb->DispAllErrors(henv, hdbc);
322 }
3ca6a5f0 323
89894079
VZ
324 if (hstmtInternal)
325 if (SQLFreeStmt(hstmtInternal, SQL_DROP) != SQL_SUCCESS)
326 pDb->DispAllErrors(henv, hdbc);
327
328 // Delete dynamically allocated cursors
329 if (hstmtDefault)
330 DeleteCursor(hstmtDefault);
7c5c05ae 331
89894079
VZ
332 if (hstmtCount)
333 DeleteCursor(hstmtCount);
108106cf 334
f6bcfd97 335} // wxDbTable::~wxDbTable()
108106cf 336
67e9aaa3
GT
337
338
6919c53f
GT
339/***************************** PRIVATE FUNCTIONS *****************************/
340
67e9aaa3
GT
341
342
f6bcfd97
BP
343/********** wxDbTable::bindInsertParams() **********/
344bool wxDbTable::bindInsertParams(void)
6919c53f 345{
89894079
VZ
346 assert(!queryOnly);
347 if (queryOnly)
348 return(FALSE);
349
350 SWORD fSqlType = 0;
351 UDWORD precision = 0;
352 SWORD scale = 0;
353
354 // Bind each column (that can be inserted) of the table to a parameter marker
355 int i,colNo;
356 for (i = 0, colNo = 1; i < noCols; i++)
357 {
358 if (! colDefs[i].InsertAllowed)
359 continue;
360 switch(colDefs[i].DbDataType)
361 {
3ca6a5f0
BP
362 case DB_DATA_TYPE_VARCHAR:
363 fSqlType = pDb->GetTypeInfVarchar().FsqlType;
364 precision = colDefs[i].SzDataObj;
365 scale = 0;
366 colDefs[i].CbValue = SQL_NTS;
367 break;
368 case DB_DATA_TYPE_INTEGER:
369 fSqlType = pDb->GetTypeInfInteger().FsqlType;
370 precision = pDb->GetTypeInfInteger().Precision;
371 scale = 0;
372 colDefs[i].CbValue = 0;
373 break;
374 case DB_DATA_TYPE_FLOAT:
375 fSqlType = pDb->GetTypeInfFloat().FsqlType;
376 precision = pDb->GetTypeInfFloat().Precision;
377 scale = pDb->GetTypeInfFloat().MaximumScale;
378 // SQL Sybase Anywhere v5.5 returned a negative number for the
379 // MaxScale. This caused ODBC to kick out an error on ibscale.
380 // I check for this here and set the scale = precision.
381 //if (scale < 0)
382 // scale = (short) precision;
383 colDefs[i].CbValue = 0;
384 break;
385 case DB_DATA_TYPE_DATE:
386 fSqlType = pDb->GetTypeInfDate().FsqlType;
387 precision = pDb->GetTypeInfDate().Precision;
388 scale = 0;
389 colDefs[i].CbValue = 0;
390 break;
89894079
VZ
391 }
392 // Null values
393 if (colDefs[i].Null)
394 {
395 colDefs[i].CbValue = SQL_NULL_DATA;
396 colDefs[i].Null = FALSE;
397 }
f6bcfd97 398
3ca6a5f0
BP
399 if (SQLBindParameter(hstmtInsert, colNo++, SQL_PARAM_INPUT, colDefs[i].SqlCtype,
400 fSqlType, precision, scale, (UCHAR*) colDefs[i].PtrDataObj,
401 precision+1,&colDefs[i].CbValue) != SQL_SUCCESS)
402 {
403 return(pDb->DispAllErrors(henv, hdbc, hstmtInsert));
404 }
89894079
VZ
405 }
406
407 // Completed successfully
408 return(TRUE);
6919c53f 409
f6bcfd97 410} // wxDbTable::bindInsertParams()
6919c53f 411
67e9aaa3 412
f6bcfd97
BP
413/********** wxDbTable::bindUpdateParams() **********/
414bool wxDbTable::bindUpdateParams(void)
6919c53f 415{
89894079
VZ
416 assert(!queryOnly);
417 if (queryOnly)
418 return(FALSE);
3ca6a5f0 419
89894079
VZ
420 SWORD fSqlType = 0;
421 UDWORD precision = 0;
422 SWORD scale = 0;
423
424 // Bind each UPDATEABLE column of the table to a parameter marker
425 int i,colNo;
426 for (i = 0, colNo = 1; i < noCols; i++)
427 {
428 if (! colDefs[i].Updateable)
429 continue;
430 switch(colDefs[i].DbDataType)
431 {
3ca6a5f0
BP
432 case DB_DATA_TYPE_VARCHAR:
433 fSqlType = pDb->GetTypeInfVarchar().FsqlType;
434 precision = colDefs[i].SzDataObj;
435 scale = 0;
436 colDefs[i].CbValue = SQL_NTS;
437 break;
438 case DB_DATA_TYPE_INTEGER:
439 fSqlType = pDb->GetTypeInfInteger().FsqlType;
440 precision = pDb->GetTypeInfInteger().Precision;
441 scale = 0;
442 colDefs[i].CbValue = 0;
443 break;
444 case DB_DATA_TYPE_FLOAT:
445 fSqlType = pDb->GetTypeInfFloat().FsqlType;
446 precision = pDb->GetTypeInfFloat().Precision;
447 scale = pDb->GetTypeInfFloat().MaximumScale;
448 // SQL Sybase Anywhere v5.5 returned a negative number for the
449 // MaxScale. This caused ODBC to kick out an error on ibscale.
450 // I check for this here and set the scale = precision.
451 //if (scale < 0)
452 // scale = (short) precision;
453 colDefs[i].CbValue = 0;
454 break;
455 case DB_DATA_TYPE_DATE:
456 fSqlType = pDb->GetTypeInfDate().FsqlType;
457 precision = pDb->GetTypeInfDate().Precision;
458 scale = 0;
459 colDefs[i].CbValue = 0;
460 break;
461 }
462
463 if (SQLBindParameter(hstmtUpdate, colNo++, SQL_PARAM_INPUT, colDefs[i].SqlCtype,
464 fSqlType, precision, scale, (UCHAR*) colDefs[i].PtrDataObj,
465 precision+1, &colDefs[i].CbValue) != SQL_SUCCESS)
466 {
467 return(pDb->DispAllErrors(henv, hdbc, hstmtUpdate));
89894079 468 }
89894079 469 }
3ca6a5f0 470
89894079
VZ
471 // Completed successfully
472 return(TRUE);
6919c53f 473
f6bcfd97 474} // wxDbTable::bindUpdateParams()
6919c53f 475
67e9aaa3 476
f6bcfd97
BP
477/********** wxDbTable::bindCols() **********/
478bool wxDbTable::bindCols(HSTMT cursor)
6919c53f 479{
89894079
VZ
480 static SDWORD cb;
481
482 // Bind each column of the table to a memory address for fetching data
483 int i;
484 for (i = 0; i < noCols; i++)
3ca6a5f0
BP
485 {
486 if (SQLBindCol(cursor, i+1, colDefs[i].SqlCtype, (UCHAR*) colDefs[i].PtrDataObj,
487 colDefs[i].SzDataObj, &cb) != SQL_SUCCESS)
488 {
489 return (pDb->DispAllErrors(henv, hdbc, cursor));
490 }
491 }
89894079
VZ
492
493 // Completed successfully
494 return(TRUE);
6919c53f 495
f6bcfd97 496} // wxDbTable::bindCols()
6919c53f 497
67e9aaa3 498
f6bcfd97
BP
499/********** wxDbTable::getRec() **********/
500bool wxDbTable::getRec(UWORD fetchType)
6919c53f 501{
89894079
VZ
502 RETCODE retcode;
503
504 if (!pDb->FwdOnlyCursors())
505 {
506 // Fetch the NEXT, PREV, FIRST or LAST record, depending on fetchType
507 UDWORD cRowsFetched;
508 UWORD rowStatus;
509
510 retcode = SQLExtendedFetch(hstmt, fetchType, 0, &cRowsFetched, &rowStatus);
511 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
3ca6a5f0 512 {
89894079
VZ
513 if (retcode == SQL_NO_DATA_FOUND)
514 return(FALSE);
515 else
516 return(pDb->DispAllErrors(henv, hdbc, hstmt));
3ca6a5f0 517 }
89894079
VZ
518 }
519 else
520 {
521 // Fetch the next record from the record set
522 retcode = SQLFetch(hstmt);
523 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
524 {
525 if (retcode == SQL_NO_DATA_FOUND)
526 return(FALSE);
527 else
528 return(pDb->DispAllErrors(henv, hdbc, hstmt));
529 }
530 }
531
532 // Completed successfully
533 return(TRUE);
6919c53f 534
f6bcfd97 535} // wxDbTable::getRec()
6919c53f 536
67e9aaa3 537
f6bcfd97
BP
538/********** wxDbTable::execDelete() **********/
539bool wxDbTable::execDelete(const char *pSqlStmt)
6919c53f 540{
89894079
VZ
541 // Execute the DELETE statement
542 if (SQLExecDirect(hstmtDelete, (UCHAR FAR *) pSqlStmt, SQL_NTS) != SQL_SUCCESS)
543 return(pDb->DispAllErrors(henv, hdbc, hstmtDelete));
6919c53f 544
89894079
VZ
545 // Record deleted successfully
546 return(TRUE);
6919c53f 547
f6bcfd97 548} // wxDbTable::execDelete()
6919c53f 549
67e9aaa3 550
f6bcfd97
BP
551/********** wxDbTable::execUpdate() **********/
552bool wxDbTable::execUpdate(const char *pSqlStmt)
6919c53f 553{
89894079
VZ
554 // Execute the UPDATE statement
555 if (SQLExecDirect(hstmtUpdate, (UCHAR FAR *) pSqlStmt, SQL_NTS) != SQL_SUCCESS)
556 return(pDb->DispAllErrors(henv, hdbc, hstmtUpdate));
6919c53f 557
89894079
VZ
558 // Record deleted successfully
559 return(TRUE);
6919c53f 560
f6bcfd97 561} // wxDbTable::execUpdate()
6919c53f 562
67e9aaa3 563
f6bcfd97
BP
564/********** wxDbTable::query() **********/
565bool wxDbTable::query(int queryType, bool forUpdate, bool distinct, const char *pSqlStmt)
6919c53f 566{
89894079 567 char sqlStmt[DB_MAX_STATEMENT_LEN];
6919c53f 568
89894079
VZ
569 // Set the selectForUpdate member variable
570 if (forUpdate)
571 // The user may wish to select for update, but the DBMS may not be capable
572 selectForUpdate = CanSelectForUpdate();
573 else
574 selectForUpdate = FALSE;
6919c53f 575
89894079
VZ
576 // Set the SQL SELECT string
577 if (queryType != DB_SELECT_STATEMENT) // A select statement was not passed in,
3ca6a5f0 578 { // so generate a select statement.
f6bcfd97 579 BuildSelectStmt(sqlStmt, queryType, distinct);
89894079 580 pDb->WriteSqlLog(sqlStmt);
3ca6a5f0
BP
581 }
582 else
583 wxStrcpy(sqlStmt, pSqlStmt);
f6bcfd97
BP
584
585 SQLFreeStmt(hstmt, SQL_CLOSE);
586 if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt, SQL_NTS) == SQL_SUCCESS)
3ca6a5f0 587 return(TRUE);
f6bcfd97 588 else
3ca6a5f0 589 {
f6bcfd97
BP
590 pDb->DispAllErrors(henv, hdbc, hstmt);
591 return(FALSE);
3ca6a5f0 592 }
6919c53f 593
89894079
VZ
594 // Make sure the cursor is closed first
595 if (! CloseCursor(hstmt))
3ca6a5f0 596 return(FALSE);
6919c53f 597
89894079 598 // Execute the SQL SELECT statement
f6bcfd97
BP
599 int retcode;
600 retcode = SQLExecDirect(hstmt, (UCHAR FAR *) (queryType == DB_SELECT_STATEMENT ? pSqlStmt : sqlStmt), SQL_NTS);
89894079 601 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
3ca6a5f0 602 return(pDb->DispAllErrors(henv, hdbc, hstmt));
6919c53f 603
89894079
VZ
604 // Completed successfully
605 return(TRUE);
6919c53f 606
f6bcfd97 607} // wxDbTable::query()
6919c53f
GT
608
609
67e9aaa3
GT
610/***************************** PUBLIC FUNCTIONS *****************************/
611
6919c53f 612
f6bcfd97
BP
613/********** wxDbTable::Open() **********/
614bool wxDbTable::Open(void)
108106cf 615{
89894079 616 if (!pDb)
f6bcfd97 617 return FALSE;
3ca6a5f0 618
89894079 619 int i;
1e92909e 620 wxString sqlStmt;
89894079
VZ
621
622 // Verify that the table exists in the database
e16143f6 623 if (!pDb->TableExists(tableName,pDb->GetUsername(),tablePath))
89894079 624 {
e16143f6 625 wxString s;
89894079 626 if (wxStrcmp(tablePath,""))
f6bcfd97 627 s.sprintf(wxT("Error opening '%s/%s'.\n"),tablePath,tableName);
e16143f6 628 else
f6bcfd97 629 s.sprintf(wxT("Error opening '%s'.\n"), tableName);
e16143f6 630 if (!pDb->TableExists(tableName,NULL,tablePath))
f6bcfd97 631 s += wxT("Table/view does not exist in the database.\n");
e16143f6 632 else
f6bcfd97
BP
633 s += wxT("Current logged in user does not have sufficient privileges to access this table.\n");
634 pDb->LogError(s.c_str());
89894079
VZ
635 return(FALSE);
636 }
637
638 // Bind the member variables for field exchange between
f6bcfd97 639 // the wxDbTable object and the ODBC record.
89894079
VZ
640 if (!queryOnly)
641 {
642 if (!bindInsertParams()) // Inserts
643 return(FALSE);
f6bcfd97 644
89894079
VZ
645 if (!bindUpdateParams()) // Updates
646 return(FALSE);
647 }
3ca6a5f0 648
89894079
VZ
649 if (!bindCols(*hstmtDefault)) // Selects
650 return(FALSE);
3ca6a5f0 651
89894079
VZ
652 if (!bindCols(hstmtInternal)) // Internal use only
653 return(FALSE);
3ca6a5f0 654
89894079
VZ
655 /*
656 * Do NOT bind the hstmtCount cursor!!!
657 */
658
659 // Build an insert statement using parameter markers
660 if (!queryOnly && noCols > 0)
661 {
662 bool needComma = FALSE;
1e92909e 663 sqlStmt.sprintf("INSERT INTO %s (", tableName);
89894079
VZ
664 for (i = 0; i < noCols; i++)
665 {
666 if (! colDefs[i].InsertAllowed)
667 continue;
668 if (needComma)
1e92909e
GT
669 sqlStmt += ",";
670 sqlStmt += colDefs[i].ColName;
89894079
VZ
671 needComma = TRUE;
672 }
673 needComma = FALSE;
1e92909e 674 sqlStmt += ") VALUES (";
f6bcfd97 675
3ca6a5f0 676 int insertableCount = 0;
f6bcfd97 677
89894079
VZ
678 for (i = 0; i < noCols; i++)
679 {
680 if (! colDefs[i].InsertAllowed)
681 continue;
682 if (needComma)
1e92909e
GT
683 sqlStmt += ",";
684 sqlStmt += "?";
89894079 685 needComma = TRUE;
3ca6a5f0 686 insertableCount++;
89894079 687 }
1e92909e 688 sqlStmt += ")";
3ca6a5f0 689
89894079 690 // Prepare the insert statement for execution
f6bcfd97 691 if (insertableCount)
3ca6a5f0 692 {
f6bcfd97
BP
693 if (SQLPrepare(hstmtInsert, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
694 return(pDb->DispAllErrors(henv, hdbc, hstmtInsert));
695 }
3ca6a5f0 696 else
f6bcfd97 697 insertable= FALSE;
89894079
VZ
698 }
699
700 // Completed successfully
701 return(TRUE);
108106cf 702
f6bcfd97 703} // wxDbTable::Open()
108106cf 704
67e9aaa3 705
f6bcfd97
BP
706/********** wxDbTable::Query() **********/
707bool wxDbTable::Query(bool forUpdate, bool distinct)
108106cf
JS
708{
709
89894079 710 return(query(DB_SELECT_WHERE, forUpdate, distinct));
108106cf 711
f6bcfd97 712} // wxDbTable::Query()
108106cf 713
67e9aaa3 714
f6bcfd97
BP
715/********** wxDbTable::QueryBySqlStmt() **********/
716bool wxDbTable::QueryBySqlStmt(const char *pSqlStmt)
108106cf 717{
89894079 718 pDb->WriteSqlLog(pSqlStmt);
108106cf 719
89894079 720 return(query(DB_SELECT_STATEMENT, FALSE, FALSE, pSqlStmt));
108106cf 721
f6bcfd97 722} // wxDbTable::QueryBySqlStmt()
108106cf 723
67e9aaa3 724
f6bcfd97
BP
725/********** wxDbTable::QueryMatching() **********/
726bool wxDbTable::QueryMatching(bool forUpdate, bool distinct)
108106cf
JS
727{
728
89894079 729 return(query(DB_SELECT_MATCHING, forUpdate, distinct));
108106cf 730
f6bcfd97 731} // wxDbTable::QueryMatching()
108106cf 732
67e9aaa3 733
f6bcfd97
BP
734/********** wxDbTable::QueryOnKeyFields() **********/
735bool wxDbTable::QueryOnKeyFields(bool forUpdate, bool distinct)
108106cf
JS
736{
737
89894079 738 return(query(DB_SELECT_KEYFIELDS, forUpdate, distinct));
108106cf 739
f6bcfd97 740} // wxDbTable::QueryOnKeyFields()
108106cf 741
67e9aaa3 742
f6bcfd97
BP
743/********** wxDbTable::GetPrev() **********/
744bool wxDbTable::GetPrev(void)
a3439c7d 745{
89894079
VZ
746 if (pDb->FwdOnlyCursors())
747 {
f6bcfd97 748 wxFAIL_MSG(wxT("GetPrev()::Backward scrolling cursors are not enabled for this instance of wxDbTable"));
89894079
VZ
749 return FALSE;
750 }
751 else
752 return(getRec(SQL_FETCH_PRIOR));
3ca6a5f0 753
f6bcfd97 754} // wxDbTable::GetPrev()
a3439c7d 755
67e9aaa3 756
f6bcfd97
BP
757/********** wxDbTable::operator-- **********/
758bool wxDbTable::operator--(int)
a3439c7d 759{
89894079
VZ
760 if (pDb->FwdOnlyCursors())
761 {
f6bcfd97 762 wxFAIL_MSG(wxT("operator--:Backward scrolling cursors are not enabled for this instance of wxDbTable"));
89894079
VZ
763 return FALSE;
764 }
765 else
766 return(getRec(SQL_FETCH_PRIOR));
3ca6a5f0 767
f6bcfd97 768} // wxDbTable::operator--
a3439c7d 769
67e9aaa3 770
f6bcfd97
BP
771/********** wxDbTable::GetFirst() **********/
772bool wxDbTable::GetFirst(void)
a3439c7d 773{
89894079
VZ
774 if (pDb->FwdOnlyCursors())
775 {
f6bcfd97 776 wxFAIL_MSG(wxT("GetFirst():Backward scrolling cursors are not enabled for this instance of wxDbTable"));
89894079
VZ
777 return FALSE;
778 }
779 else
780 return(getRec(SQL_FETCH_FIRST));
3ca6a5f0 781
f6bcfd97 782} // wxDbTable::GetFirst()
a3439c7d 783
67e9aaa3 784
f6bcfd97
BP
785/********** wxDbTable::GetLast() **********/
786bool wxDbTable::GetLast(void)
a3439c7d 787{
89894079
VZ
788 if (pDb->FwdOnlyCursors())
789 {
f6bcfd97 790 wxFAIL_MSG(wxT("GetLast()::Backward scrolling cursors are not enabled for this instance of wxDbTable"));
89894079
VZ
791 return FALSE;
792 }
793 else
794 return(getRec(SQL_FETCH_LAST));
3ca6a5f0 795
f6bcfd97 796} // wxDbTable::GetLast()
a3439c7d 797
67e9aaa3 798
f6bcfd97
BP
799/********** wxDbTable::BuildSelectStmt() **********/
800void wxDbTable::BuildSelectStmt(char *pSqlStmt, int typeOfSelect, bool distinct)
108106cf 801{
89894079
VZ
802 char whereClause[DB_MAX_WHERE_CLAUSE_LEN];
803
804 whereClause[0] = 0;
805
806 // Build a select statement to query the database
807 wxStrcpy(pSqlStmt, "SELECT ");
808
809 // SELECT DISTINCT values only?
810 if (distinct)
811 wxStrcat(pSqlStmt, "DISTINCT ");
812
813 // Was a FROM clause specified to join tables to the base table?
814 // Available for ::Query() only!!!
815 bool appendFromClause = FALSE;
f6bcfd97 816#if wxODBC_BACKWARD_COMPATABILITY
89894079
VZ
817 if (typeOfSelect == DB_SELECT_WHERE && from && wxStrlen(from))
818 appendFromClause = TRUE;
f6bcfd97
BP
819#else
820 if (typeOfSelect == DB_SELECT_WHERE && from.Length())
821 appendFromClause = TRUE;
822#endif
89894079
VZ
823
824 // Add the column list
825 int i;
826 for (i = 0; i < noCols; i++)
827 {
828 // If joining tables, the base table column names must be qualified to avoid ambiguity
829 if (appendFromClause)
830 {
831 wxStrcat(pSqlStmt, queryTableName);
832 wxStrcat(pSqlStmt, ".");
833 }
834 wxStrcat(pSqlStmt, colDefs[i].ColName);
835 if (i + 1 < noCols)
836 wxStrcat(pSqlStmt, ",");
837 }
838
839 // If the datasource supports ROWID, get this column as well. Exception: Don't retrieve
840 // the ROWID if querying distinct records. The rowid will always be unique.
841 if (!distinct && CanUpdByROWID())
842 {
843 // If joining tables, the base table column names must be qualified to avoid ambiguity
844 if (appendFromClause)
845 {
846 wxStrcat(pSqlStmt, ",");
847 wxStrcat(pSqlStmt, queryTableName);
848 wxStrcat(pSqlStmt, ".ROWID");
849 }
850 else
851 wxStrcat(pSqlStmt, ",ROWID");
852 }
853
854 // Append the FROM tablename portion
855 wxStrcat(pSqlStmt, " FROM ");
856 wxStrcat(pSqlStmt, queryTableName);
857
858 // Sybase uses the HOLDLOCK keyword to lock a record during query.
859 // The HOLDLOCK keyword follows the table name in the from clause.
860 // Each table in the from clause must specify HOLDLOCK or
861 // NOHOLDLOCK (the default). Note: The "FOR UPDATE" clause
862 // is parsed but ignored in SYBASE Transact-SQL.
863 if (selectForUpdate && (pDb->Dbms() == dbmsSYBASE_ASA || pDb->Dbms() == dbmsSYBASE_ASE))
864 wxStrcat(pSqlStmt, " HOLDLOCK");
865
866 if (appendFromClause)
867 wxStrcat(pSqlStmt, from);
868
869 // Append the WHERE clause. Either append the where clause for the class
870 // or build a where clause. The typeOfSelect determines this.
871 switch(typeOfSelect)
872 {
3ca6a5f0 873 case DB_SELECT_WHERE:
f6bcfd97 874#if wxODBC_BACKWARD_COMPATABILITY
3ca6a5f0 875 if (where && wxStrlen(where)) // May not want a where clause!!!
f6bcfd97 876#else
3ca6a5f0 877 if (where.Length()) // May not want a where clause!!!
f6bcfd97 878#endif
3ca6a5f0
BP
879 {
880 wxStrcat(pSqlStmt, " WHERE ");
881 wxStrcat(pSqlStmt, where);
882 }
883 break;
884 case DB_SELECT_KEYFIELDS:
885 BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS);
886 if (wxStrlen(whereClause))
887 {
888 wxStrcat(pSqlStmt, " WHERE ");
889 wxStrcat(pSqlStmt, whereClause);
890 }
891 break;
892 case DB_SELECT_MATCHING:
893 BuildWhereClause(whereClause, DB_WHERE_MATCHING);
894 if (wxStrlen(whereClause))
895 {
896 wxStrcat(pSqlStmt, " WHERE ");
897 wxStrcat(pSqlStmt, whereClause);
898 }
899 break;
89894079
VZ
900 }
901
902 // Append the ORDER BY clause
f6bcfd97 903#if wxODBC_BACKWARD_COMPATABILITY
89894079 904 if (orderBy && wxStrlen(orderBy))
f6bcfd97 905#else
3ca6a5f0 906 if (orderBy.Length())
f6bcfd97 907#endif
89894079
VZ
908 {
909 wxStrcat(pSqlStmt, " ORDER BY ");
910 wxStrcat(pSqlStmt, orderBy);
911 }
912
913 // SELECT FOR UPDATE if told to do so and the datasource is capable. Sybase
914 // parses the FOR UPDATE clause but ignores it. See the comment above on the
915 // HOLDLOCK for Sybase.
916 if (selectForUpdate && CanSelectForUpdate())
917 wxStrcat(pSqlStmt, " FOR UPDATE");
108106cf 918
f6bcfd97 919} // wxDbTable::BuildSelectStmt()
108106cf 920
67e9aaa3 921
f6bcfd97
BP
922/********** wxDbTable::GetRowNum() **********/
923UWORD wxDbTable::GetRowNum(void)
108106cf 924{
89894079 925 UDWORD rowNum;
108106cf 926
89894079
VZ
927 if (SQLGetStmtOption(hstmt, SQL_ROW_NUMBER, (UCHAR*) &rowNum) != SQL_SUCCESS)
928 {
929 pDb->DispAllErrors(henv, hdbc, hstmt);
930 return(0);
931 }
108106cf 932
89894079
VZ
933 // Completed successfully
934 return((UWORD) rowNum);
108106cf 935
f6bcfd97 936} // wxDbTable::GetRowNum()
108106cf 937
67e9aaa3 938
f6bcfd97
BP
939/********** wxDbTable::CloseCursor() **********/
940bool wxDbTable::CloseCursor(HSTMT cursor)
108106cf 941{
89894079
VZ
942 if (SQLFreeStmt(cursor, SQL_CLOSE) != SQL_SUCCESS)
943 return(pDb->DispAllErrors(henv, hdbc, cursor));
108106cf 944
89894079
VZ
945 // Completed successfully
946 return(TRUE);
108106cf 947
f6bcfd97 948} // wxDbTable::CloseCursor()
108106cf 949
67e9aaa3 950
f6bcfd97
BP
951/********** wxDbTable::CreateTable() **********/
952bool wxDbTable::CreateTable(bool attemptDrop)
108106cf 953{
89894079
VZ
954 if (!pDb)
955 return FALSE;
1fc5dd6f 956
89894079 957 int i, j;
1e92909e 958 wxString sqlStmt;
108106cf 959
a2115c88 960#ifdef DBDEBUG_CONSOLE
89894079 961 cout << "Creating Table " << tableName << "..." << endl;
108106cf
JS
962#endif
963
89894079
VZ
964 // Drop table first
965 if (attemptDrop && !DropTable())
966 return FALSE;
108106cf 967
89894079 968 // Create the table
a2115c88 969#ifdef DBDEBUG_CONSOLE
89894079
VZ
970 for (i = 0; i < noCols; i++)
971 {
972 // Exclude derived columns since they are NOT part of the base table
973 if (colDefs[i].DerivedCol)
974 continue;
975 cout << i + 1 << ": " << colDefs[i].ColName << "; ";
976 switch(colDefs[i].DbDataType)
977 {
978 case DB_DATA_TYPE_VARCHAR:
979 cout << pDb->typeInfVarchar.TypeName << "(" << colDefs[i].SzDataObj << ")";
980 break;
981 case DB_DATA_TYPE_INTEGER:
982 cout << pDb->typeInfInteger.TypeName;
983 break;
984 case DB_DATA_TYPE_FLOAT:
985 cout << pDb->typeInfFloat.TypeName;
986 break;
987 case DB_DATA_TYPE_DATE:
988 cout << pDb->typeInfDate.TypeName;
989 break;
990 }
991 cout << endl;
992 }
108106cf
JS
993#endif
994
89894079
VZ
995 // Build a CREATE TABLE string from the colDefs structure.
996 bool needComma = FALSE;
1e92909e
GT
997 sqlStmt.sprintf("CREATE TABLE %s (", tableName);
998
89894079
VZ
999 for (i = 0; i < noCols; i++)
1000 {
1001 // Exclude derived columns since they are NOT part of the base table
1002 if (colDefs[i].DerivedCol)
1003 continue;
1004 // Comma Delimiter
1005 if (needComma)
3ca6a5f0 1006 sqlStmt += ",";
89894079 1007 // Column Name
1e92909e
GT
1008 sqlStmt += colDefs[i].ColName;
1009 sqlStmt += " ";
89894079
VZ
1010 // Column Type
1011 switch(colDefs[i].DbDataType)
1012 {
1013 case DB_DATA_TYPE_VARCHAR:
3ca6a5f0
BP
1014 sqlStmt += pDb->GetTypeInfVarchar().TypeName;
1015 break;
89894079 1016 case DB_DATA_TYPE_INTEGER:
3ca6a5f0
BP
1017 sqlStmt += pDb->GetTypeInfInteger().TypeName;
1018 break;
89894079 1019 case DB_DATA_TYPE_FLOAT:
3ca6a5f0
BP
1020 sqlStmt += pDb->GetTypeInfFloat().TypeName;
1021 break;
89894079 1022 case DB_DATA_TYPE_DATE:
3ca6a5f0
BP
1023 sqlStmt += pDb->GetTypeInfDate().TypeName;
1024 break;
89894079
VZ
1025 }
1026 // For varchars, append the size of the string
1027 if (colDefs[i].DbDataType == DB_DATA_TYPE_VARCHAR)
1028 {
1e92909e 1029 wxString s;
89894079
VZ
1030 // wxStrcat(sqlStmt, "(");
1031 // wxStrcat(sqlStmt, itoa(colDefs[i].SzDataObj, s, 10));
1032 // wxStrcat(sqlStmt, ")");
1e92909e 1033 s.sprintf("(%d)", colDefs[i].SzDataObj);
f6bcfd97 1034 sqlStmt += s.c_str();
89894079
VZ
1035 }
1036
1037 if (pDb->Dbms() == dbmsSYBASE_ASE || pDb->Dbms() == dbmsMY_SQL)
1038 {
1039 if (colDefs[i].KeyField)
1040 {
1e92909e 1041 sqlStmt += " NOT NULL";
89894079
VZ
1042 }
1043 }
1044
1045 needComma = TRUE;
1046 }
1047 // If there is a primary key defined, include it in the create statement
1048 for (i = j = 0; i < noCols; i++)
1049 {
1050 if (colDefs[i].KeyField)
1051 {
1052 j++;
1053 break;
1054 }
1055 }
1056 if (j && pDb->Dbms() != dbmsDBASE) // Found a keyfield
1057 {
1058 if (pDb->Dbms() != dbmsMY_SQL)
1059 {
1e92909e
GT
1060 sqlStmt += ",CONSTRAINT ";
1061 sqlStmt += tableName;
1062 sqlStmt += "_PIDX PRIMARY KEY (";
89894079
VZ
1063 }
1064 else
1065 {
1066 /* MySQL goes out on this one. We also declare the relevant key NON NULL above */
1e92909e 1067 sqlStmt += ", PRIMARY KEY (";
89894079
VZ
1068 }
1069
1070 // List column name(s) of column(s) comprising the primary key
1071 for (i = j = 0; i < noCols; i++)
1072 {
1073 if (colDefs[i].KeyField)
1074 {
1075 if (j++) // Multi part key, comma separate names
1e92909e
GT
1076 sqlStmt += ",";
1077 sqlStmt += colDefs[i].ColName;
89894079
VZ
1078 }
1079 }
1e92909e 1080 sqlStmt += ")";
89894079
VZ
1081 }
1082 // Append the closing parentheses for the create table statement
1e92909e 1083 sqlStmt += ")";
a2115c88 1084
f6bcfd97 1085 pDb->WriteSqlLog(sqlStmt.c_str());
1fc5dd6f 1086
a2115c88 1087#ifdef DBDEBUG_CONSOLE
f6bcfd97 1088 cout << endl << sqlStmt.c_str() << endl;
108106cf
JS
1089#endif
1090
89894079 1091 // Execute the CREATE TABLE statement
f6bcfd97 1092 RETCODE retcode = SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS);
89894079
VZ
1093 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
1094 {
1095 pDb->DispAllErrors(henv, hdbc, hstmt);
1096 pDb->RollbackTrans();
1097 CloseCursor(hstmt);
1098 return(FALSE);
1099 }
1100
1101 // Commit the transaction and close the cursor
3ca6a5f0 1102 if (!pDb->CommitTrans())
89894079 1103 return(FALSE);
3ca6a5f0 1104 if (!CloseCursor(hstmt))
89894079
VZ
1105 return(FALSE);
1106
1107 // Database table created successfully
1108 return(TRUE);
108106cf 1109
f6bcfd97 1110} // wxDbTable::CreateTable()
108106cf 1111
67e9aaa3 1112
f6bcfd97
BP
1113/********** wxDbTable::DropTable() **********/
1114bool wxDbTable::DropTable()
a2115c88 1115{
89894079
VZ
1116 // NOTE: This function returns TRUE if the Table does not exist, but
1117 // only for identified databases. Code will need to be added
0b8410f3 1118 // below for any other databases when those databases are defined
89894079 1119 // to handle this situation consistently
a2115c88 1120
1e92909e 1121 wxString sqlStmt;
a2115c88 1122
1e92909e 1123 sqlStmt.sprintf("DROP TABLE %s", tableName);
a2115c88 1124
f6bcfd97 1125 pDb->WriteSqlLog(sqlStmt.c_str());
a2115c88
GT
1126
1127#ifdef DBDEBUG_CONSOLE
f6bcfd97 1128 cout << endl << sqlStmt.c_str() << endl;
a2115c88
GT
1129#endif
1130
f6bcfd97 1131 if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
89894079
VZ
1132 {
1133 // Check for "Base table not found" error and ignore
3ca6a5f0 1134 pDb->GetNextError(henv, hdbc, hstmt);
f6bcfd97 1135 if (wxStrcmp(pDb->sqlState,"S0002") && wxStrcmp(pDb->sqlState, "S1000")) // "Base table not found"
3ca6a5f0 1136 {
89894079 1137 // Check for product specific error codes
3ca6a5f0
BP
1138 if (!((pDb->Dbms() == dbmsSYBASE_ASA && !wxStrcmp(pDb->sqlState,"42000")) || // 5.x (and lower?)
1139 (pDb->Dbms() == dbmsSYBASE_ASE && !wxStrcmp(pDb->sqlState,"37000")) ||
1140 (pDb->Dbms() == dbmsPOSTGRES && !wxStrcmp(pDb->sqlState,"08S01"))))
89894079
VZ
1141 {
1142 pDb->DispNextError();
1143 pDb->DispAllErrors(henv, hdbc, hstmt);
1144 pDb->RollbackTrans();
1145 CloseCursor(hstmt);
1146 return(FALSE);
1147 }
1148 }
1149 }
1150
1151 // Commit the transaction and close the cursor
1152 if (! pDb->CommitTrans())
1153 return(FALSE);
1154 if (! CloseCursor(hstmt))
1155 return(FALSE);
1156
1157 return(TRUE);
f6bcfd97 1158} // wxDbTable::DropTable()
a2115c88 1159
67e9aaa3 1160
f6bcfd97
BP
1161/********** wxDbTable::CreateIndex() **********/
1162bool wxDbTable::CreateIndex(const char * idxName, bool unique, int noIdxCols, wxDbIdxDef *pIdxDefs, bool attemptDrop)
108106cf 1163{
1e92909e 1164 wxString sqlStmt;
89894079
VZ
1165
1166 // Drop the index first
1167 if (attemptDrop && !DropIndex(idxName))
1168 return (FALSE);
1169
3ca6a5f0
BP
1170 // MySQL (and possibly Sybase ASE?? - gt) require that any columns which are used as portions
1171 // of an index have the columns defined as "NOT NULL". During initial table creation though,
1172 // it may not be known which columns are necessarily going to be part of an index (e.g. the
1173 // table was created, then months later you determine that an additional index while
1174 // give better performance, so you want to add an index).
1175 //
1176 // The following block of code will modify the column definition to make the column be
1177 // defined with the "NOT NULL" qualifier.
1178 if (pDb->Dbms() == dbmsMY_SQL)
1179 {
1180 wxString sqlStmt;
1181 int i;
1182 bool ok = TRUE;
1183 for (i = 0; i < noIdxCols && ok; i++)
1184 {
1185 int j = 0;
1186 bool found = FALSE;
1187 // Find the column definition that has the ColName that matches the
1188 // index column name. We need to do this to get the DB_DATA_TYPE of
1189 // the index column, as MySQL's syntax for the ALTER column requires
1190 // this information
1191 while (!found && (j < this->noCols))
1192 {
1193 if (wxStrcmp(colDefs[j].ColName,pIdxDefs[i].ColName) == 0)
1194 found = TRUE;
1195 if (!found)
1196 j++;
1197 }
1198
1199 if (found)
1200 {
1201 wxString typeNameAndSize;
1202
1203 switch(colDefs[j].DbDataType)
1204 {
1205 case DB_DATA_TYPE_VARCHAR:
1206 typeNameAndSize = pDb->GetTypeInfVarchar().TypeName;
1207 break;
1208 case DB_DATA_TYPE_INTEGER:
1209 typeNameAndSize = pDb->GetTypeInfInteger().TypeName;
1210 break;
1211 case DB_DATA_TYPE_FLOAT:
1212 typeNameAndSize = pDb->GetTypeInfFloat().TypeName;
1213 break;
1214 case DB_DATA_TYPE_DATE:
1215 typeNameAndSize = pDb->GetTypeInfDate().TypeName;
1216 break;
1217 }
1218
1219 // For varchars, append the size of the string
1220 if (colDefs[j].DbDataType == DB_DATA_TYPE_VARCHAR)
1221 {
1222 wxString s;
1223 s.sprintf("(%d)", colDefs[i].SzDataObj);
1224 typeNameAndSize += s.c_str();
1225 }
1226
1227 sqlStmt.sprintf("ALTER TABLE %s MODIFY %s %s NOT NULL",tableName,pIdxDefs[i].ColName,typeNameAndSize.c_str());
1228 ok = pDb->ExecSql(sqlStmt.c_str());
1229
1230 if (!ok)
1231 {
1232 wxODBC_ERRORS retcode;
1233 // Oracle returns a DB_ERR_GENERAL_ERROR if the column is already
1234 // defined to be NOT NULL, but reportedly MySQL doesn't mind.
1235 // This line is just here for debug checking of the value
1236 retcode = (wxODBC_ERRORS)pDb->DB_STATUS;
1237 }
1238 }
1239 else
1240 ok = FALSE;
1241 }
1242 if (ok)
1243 pDb->CommitTrans();
1244 else
1245 {
1246 pDb->RollbackTrans();
1247 return(FALSE);
1248 }
1249 }
1250
89894079 1251 // Build a CREATE INDEX statement
1e92909e 1252 sqlStmt = "CREATE ";
89894079 1253 if (unique)
1e92909e 1254 sqlStmt += "UNIQUE ";
3ca6a5f0 1255
1e92909e
GT
1256 sqlStmt += "INDEX ";
1257 sqlStmt += idxName;
1258 sqlStmt += " ON ";
1259 sqlStmt += tableName;
1260 sqlStmt += " (";
3ca6a5f0 1261
89894079
VZ
1262 // Append list of columns making up index
1263 int i;
1264 for (i = 0; i < noIdxCols; i++)
1265 {
1e92909e
GT
1266 sqlStmt += pIdxDefs[i].ColName;
1267 /* Postgres doesn't cope with ASC */
89894079
VZ
1268 if (pDb->Dbms() != dbmsPOSTGRES)
1269 {
1270 if (pIdxDefs[i].Ascending)
1e92909e 1271 sqlStmt += " ASC";
89894079 1272 else
1e92909e 1273 sqlStmt += " DESC";
89894079
VZ
1274 }
1275
1276 if ((i + 1) < noIdxCols)
1e92909e 1277 sqlStmt += ",";
89894079
VZ
1278 }
1279
1280 // Append closing parentheses
1e92909e 1281 sqlStmt += ")";
89894079 1282
f6bcfd97 1283 pDb->WriteSqlLog(sqlStmt.c_str());
1fc5dd6f 1284
a2115c88 1285#ifdef DBDEBUG_CONSOLE
f6bcfd97 1286 cout << endl << sqlStmt.c_str() << endl << endl;
108106cf
JS
1287#endif
1288
89894079 1289 // Execute the CREATE INDEX statement
f6bcfd97 1290 if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
89894079
VZ
1291 {
1292 pDb->DispAllErrors(henv, hdbc, hstmt);
1293 pDb->RollbackTrans();
1294 CloseCursor(hstmt);
1295 return(FALSE);
1296 }
108106cf 1297
89894079
VZ
1298 // Commit the transaction and close the cursor
1299 if (! pDb->CommitTrans())
1300 return(FALSE);
1301 if (! CloseCursor(hstmt))
1302 return(FALSE);
108106cf 1303
89894079
VZ
1304 // Index Created Successfully
1305 return(TRUE);
108106cf 1306
f6bcfd97 1307} // wxDbTable::CreateIndex()
108106cf 1308
67e9aaa3 1309
f6bcfd97
BP
1310/********** wxDbTable::DropIndex() **********/
1311bool wxDbTable::DropIndex(const char * idxName)
a2115c88 1312{
89894079
VZ
1313 // NOTE: This function returns TRUE if the Index does not exist, but
1314 // only for identified databases. Code will need to be added
1315 // below for any other databases when those databases are defined
1316 // to handle this situation consistently
a2115c88 1317
1e92909e 1318 wxString sqlStmt;
a2115c88 1319
3ca6a5f0 1320 if (pDb->Dbms() == dbmsACCESS || pDb->Dbms() == dbmsMY_SQL)
1e92909e 1321 sqlStmt.sprintf("DROP INDEX %s ON %s",idxName,tableName);
89894079 1322 else if (pDb->Dbms() == dbmsSYBASE_ASE)
1e92909e 1323 sqlStmt.sprintf("DROP INDEX %s.%s",tableName,idxName);
89894079 1324 else
1e92909e 1325 sqlStmt.sprintf("DROP INDEX %s",idxName);
a2115c88 1326
f6bcfd97 1327 pDb->WriteSqlLog(sqlStmt.c_str());
a2115c88
GT
1328
1329#ifdef DBDEBUG_CONSOLE
f6bcfd97 1330 cout << endl << sqlStmt.c_str() << endl;
a2115c88
GT
1331#endif
1332
f6bcfd97 1333 if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
89894079
VZ
1334 {
1335 // Check for "Index not found" error and ignore
1336 pDb->GetNextError(henv, hdbc, hstmt);
1337 if (wxStrcmp(pDb->sqlState,"S0012")) // "Index not found"
1338 {
1339 // Check for product specific error codes
3ca6a5f0
BP
1340 if (!((pDb->Dbms() == dbmsSYBASE_ASA && !wxStrcmp(pDb->sqlState,"42000")) || // v5.x (and lower?)
1341 (pDb->Dbms() == dbmsSYBASE_ASE && !wxStrcmp(pDb->sqlState,"37000")) ||
1342 (pDb->Dbms() == dbmsMS_SQL_SERVER && !wxStrcmp(pDb->sqlState,"S1000")) ||
1343 (pDb->Dbms() == dbmsSYBASE_ASE && !wxStrcmp(pDb->sqlState,"S0002")) || // Base table not found
1344 (pDb->Dbms() == dbmsMY_SQL && !wxStrcmp(pDb->sqlState,"42S12")) || // tested by Christopher Ludwik Marino-Cebulski using v3.23.21beta
1345 (pDb->Dbms() == dbmsPOSTGRES && !wxStrcmp(pDb->sqlState,"08S01"))
1346 ))
89894079
VZ
1347 {
1348 pDb->DispNextError();
1349 pDb->DispAllErrors(henv, hdbc, hstmt);
1350 pDb->RollbackTrans();
1351 CloseCursor(hstmt);
1352 return(FALSE);
1353 }
1354 }
1355 }
1356
1357 // Commit the transaction and close the cursor
1358 if (! pDb->CommitTrans())
1359 return(FALSE);
1360 if (! CloseCursor(hstmt))
1361 return(FALSE);
1362
1363 return(TRUE);
f6bcfd97 1364} // wxDbTable::DropIndex()
a2115c88 1365
67e9aaa3 1366
f6bcfd97
BP
1367/********** wxDbTable::Insert() **********/
1368int wxDbTable::Insert(void)
108106cf 1369{
89894079 1370 assert(!queryOnly);
f6bcfd97 1371 if (queryOnly || !insertable)
89894079
VZ
1372 return(DB_FAILURE);
1373
1374 bindInsertParams();
1375
1376 // Insert the record by executing the already prepared insert statement
1377 RETCODE retcode;
1378 retcode=SQLExecute(hstmtInsert);
1379 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
1380 {
1381 // Check to see if integrity constraint was violated
1382 pDb->GetNextError(henv, hdbc, hstmtInsert);
1383 if (! wxStrcmp(pDb->sqlState, "23000")) // Integrity constraint violated
1384 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL);
1385 else
1386 {
1387 pDb->DispNextError();
1388 pDb->DispAllErrors(henv, hdbc, hstmtInsert);
1389 return(DB_FAILURE);
1390 }
1391 }
1392
1393 // Record inserted into the datasource successfully
1394 return(DB_SUCCESS);
108106cf 1395
f6bcfd97 1396} // wxDbTable::Insert()
108106cf 1397
67e9aaa3 1398
f6bcfd97
BP
1399/********** wxDbTable::Update() **********/
1400bool wxDbTable::Update(void)
108106cf 1401{
89894079
VZ
1402 assert(!queryOnly);
1403 if (queryOnly)
1404 return(FALSE);
a2115c88 1405
89894079 1406 char sqlStmt[DB_MAX_STATEMENT_LEN];
108106cf 1407
89894079 1408 // Build the SQL UPDATE statement
f6bcfd97 1409 BuildUpdateStmt(sqlStmt, DB_UPD_KEYFIELDS);
108106cf 1410
89894079 1411 pDb->WriteSqlLog(sqlStmt);
1fc5dd6f 1412
a2115c88 1413#ifdef DBDEBUG_CONSOLE
0b8410f3 1414 cout << endl << sqlStmt << endl << endl;
108106cf
JS
1415#endif
1416
89894079
VZ
1417 // Execute the SQL UPDATE statement
1418 return(execUpdate(sqlStmt));
108106cf 1419
f6bcfd97 1420} // wxDbTable::Update()
108106cf 1421
67e9aaa3 1422
f6bcfd97
BP
1423/********** wxDbTable::Update(pSqlStmt) **********/
1424bool wxDbTable::Update(const char *pSqlStmt)
6919c53f 1425{
89894079
VZ
1426 assert(!queryOnly);
1427 if (queryOnly)
1428 return(FALSE);
6919c53f 1429
89894079 1430 pDb->WriteSqlLog(pSqlStmt);
6919c53f 1431
89894079 1432 return(execUpdate(pSqlStmt));
6919c53f 1433
f6bcfd97 1434} // wxDbTable::Update(pSqlStmt)
6919c53f 1435
67e9aaa3 1436
f6bcfd97
BP
1437/********** wxDbTable::UpdateWhere() **********/
1438bool wxDbTable::UpdateWhere(const char *pWhereClause)
108106cf 1439{
89894079
VZ
1440 assert(!queryOnly);
1441 if (queryOnly)
1442 return(FALSE);
a2115c88 1443
89894079 1444 char sqlStmt[DB_MAX_STATEMENT_LEN];
108106cf 1445
89894079 1446 // Build the SQL UPDATE statement
f6bcfd97 1447 BuildUpdateStmt(sqlStmt, DB_UPD_WHERE, pWhereClause);
108106cf 1448
89894079 1449 pDb->WriteSqlLog(sqlStmt);
1fc5dd6f 1450
a2115c88 1451#ifdef DBDEBUG_CONSOLE
0b8410f3 1452 cout << endl << sqlStmt << endl << endl;
108106cf
JS
1453#endif
1454
89894079
VZ
1455 // Execute the SQL UPDATE statement
1456 return(execUpdate(sqlStmt));
108106cf 1457
f6bcfd97 1458} // wxDbTable::UpdateWhere()
108106cf 1459
67e9aaa3 1460
f6bcfd97
BP
1461/********** wxDbTable::Delete() **********/
1462bool wxDbTable::Delete(void)
108106cf 1463{
89894079
VZ
1464 assert(!queryOnly);
1465 if (queryOnly)
1466 return(FALSE);
a2115c88 1467
89894079 1468 char sqlStmt[DB_MAX_STATEMENT_LEN];
108106cf 1469
89894079 1470 // Build the SQL DELETE statement
f6bcfd97 1471 BuildDeleteStmt(sqlStmt, DB_DEL_KEYFIELDS);
108106cf 1472
89894079 1473 pDb->WriteSqlLog(sqlStmt);
1fc5dd6f 1474
89894079
VZ
1475 // Execute the SQL DELETE statement
1476 return(execDelete(sqlStmt));
108106cf 1477
f6bcfd97 1478} // wxDbTable::Delete()
108106cf 1479
67e9aaa3 1480
f6bcfd97
BP
1481/********** wxDbTable::DeleteWhere() **********/
1482bool wxDbTable::DeleteWhere(const char *pWhereClause)
108106cf 1483{
89894079
VZ
1484 assert(!queryOnly);
1485 if (queryOnly)
1486 return(FALSE);
a2115c88 1487
89894079 1488 char sqlStmt[DB_MAX_STATEMENT_LEN];
108106cf 1489
89894079 1490 // Build the SQL DELETE statement
f6bcfd97 1491 BuildDeleteStmt(sqlStmt, DB_DEL_WHERE, pWhereClause);
108106cf 1492
89894079 1493 pDb->WriteSqlLog(sqlStmt);
1fc5dd6f 1494
89894079
VZ
1495 // Execute the SQL DELETE statement
1496 return(execDelete(sqlStmt));
108106cf 1497
f6bcfd97 1498} // wxDbTable::DeleteWhere()
108106cf 1499
67e9aaa3 1500
f6bcfd97
BP
1501/********** wxDbTable::DeleteMatching() **********/
1502bool wxDbTable::DeleteMatching(void)
108106cf 1503{
89894079
VZ
1504 assert(!queryOnly);
1505 if (queryOnly)
1506 return(FALSE);
a2115c88 1507
89894079 1508 char sqlStmt[DB_MAX_STATEMENT_LEN];
108106cf 1509
89894079 1510 // Build the SQL DELETE statement
f6bcfd97 1511 BuildDeleteStmt(sqlStmt, DB_DEL_MATCHING);
108106cf 1512
89894079 1513 pDb->WriteSqlLog(sqlStmt);
1fc5dd6f 1514
89894079
VZ
1515 // Execute the SQL DELETE statement
1516 return(execDelete(sqlStmt));
108106cf 1517
f6bcfd97 1518} // wxDbTable::DeleteMatching()
108106cf 1519
67e9aaa3 1520
f6bcfd97
BP
1521/********** wxDbTable::BuildUpdateStmt() **********/
1522void wxDbTable::BuildUpdateStmt(char *pSqlStmt, int typeOfUpd, const char *pWhereClause)
108106cf 1523{
89894079
VZ
1524 assert(!queryOnly);
1525 if (queryOnly)
1526 return;
1527
1528 char whereClause[DB_MAX_WHERE_CLAUSE_LEN];
1529 bool firstColumn = TRUE;
1530
1531 whereClause[0] = 0;
1532 sprintf(pSqlStmt, "UPDATE %s SET ", tableName);
1533
1534 // Append a list of columns to be updated
1535 int i;
1536 for (i = 0; i < noCols; i++)
1537 {
1538 // Only append Updateable columns
1539 if (colDefs[i].Updateable)
1540 {
1541 if (! firstColumn)
1542 wxStrcat(pSqlStmt, ",");
1543 else
1544 firstColumn = FALSE;
1545 wxStrcat(pSqlStmt, colDefs[i].ColName);
1546 wxStrcat(pSqlStmt, " = ?");
1547 }
1548 }
1549
1550 // Append the WHERE clause to the SQL UPDATE statement
1551 wxStrcat(pSqlStmt, " WHERE ");
1552 switch(typeOfUpd)
1553 {
3ca6a5f0
BP
1554 case DB_UPD_KEYFIELDS:
1555 // If the datasource supports the ROWID column, build
1556 // the where on ROWID for efficiency purposes.
1557 // e.g. UPDATE PARTS SET Col1 = ?, Col2 = ? WHERE ROWID = '111.222.333'
1558 if (CanUpdByROWID())
89894079 1559 {
3ca6a5f0
BP
1560 SDWORD cb;
1561 char rowid[wxDB_ROWID_LEN];
1562
1563 // Get the ROWID value. If not successful retreiving the ROWID,
1564 // simply fall down through the code and build the WHERE clause
1565 // based on the key fields.
1566 if (SQLGetData(hstmt, noCols+1, SQL_C_CHAR, (UCHAR*) rowid, wxDB_ROWID_LEN, &cb) == SQL_SUCCESS)
1567 {
1568 wxStrcat(pSqlStmt, "ROWID = '");
1569 wxStrcat(pSqlStmt, rowid);
1570 wxStrcat(pSqlStmt, "'");
1571 break;
1572 }
89894079 1573 }
3ca6a5f0
BP
1574 // Unable to delete by ROWID, so build a WHERE
1575 // clause based on the keyfields.
1576 BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS);
1577 wxStrcat(pSqlStmt, whereClause);
1578 break;
1579 case DB_UPD_WHERE:
1580 wxStrcat(pSqlStmt, pWhereClause);
1581 break;
89894079 1582 }
f6bcfd97 1583} // BuildUpdateStmt()
108106cf 1584
67e9aaa3 1585
f6bcfd97
BP
1586/********** wxDbTable::BuildDeleteStmt() **********/
1587void wxDbTable::BuildDeleteStmt(char *pSqlStmt, int typeOfDel, const char *pWhereClause)
108106cf 1588{
89894079
VZ
1589 assert(!queryOnly);
1590 if (queryOnly)
1591 return;
1592
1593 char whereClause[DB_MAX_WHERE_CLAUSE_LEN];
1594
1595 whereClause[0] = 0;
1596
1597 // Handle the case of DeleteWhere() and the where clause is blank. It should
1598 // delete all records from the database in this case.
1599 if (typeOfDel == DB_DEL_WHERE && (pWhereClause == 0 || wxStrlen(pWhereClause) == 0))
1600 {
1601 sprintf(pSqlStmt, "DELETE FROM %s", tableName);
1602 return;
1603 }
1604
1605 sprintf(pSqlStmt, "DELETE FROM %s WHERE ", tableName);
1606
1607 // Append the WHERE clause to the SQL DELETE statement
1608 switch(typeOfDel)
1609 {
3ca6a5f0
BP
1610 case DB_DEL_KEYFIELDS:
1611 // If the datasource supports the ROWID column, build
1612 // the where on ROWID for efficiency purposes.
1613 // e.g. DELETE FROM PARTS WHERE ROWID = '111.222.333'
1614 if (CanUpdByROWID())
89894079 1615 {
3ca6a5f0
BP
1616 SDWORD cb;
1617 char rowid[wxDB_ROWID_LEN];
1618
1619 // Get the ROWID value. If not successful retreiving the ROWID,
1620 // simply fall down through the code and build the WHERE clause
1621 // based on the key fields.
1622 if (SQLGetData(hstmt, noCols+1, SQL_C_CHAR, (UCHAR*) rowid, wxDB_ROWID_LEN, &cb) == SQL_SUCCESS)
1623 {
1624 wxStrcat(pSqlStmt, "ROWID = '");
1625 wxStrcat(pSqlStmt, rowid);
1626 wxStrcat(pSqlStmt, "'");
1627 break;
1628 }
89894079 1629 }
3ca6a5f0
BP
1630 // Unable to delete by ROWID, so build a WHERE
1631 // clause based on the keyfields.
1632 BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS);
1633 wxStrcat(pSqlStmt, whereClause);
1634 break;
1635 case DB_DEL_WHERE:
1636 wxStrcat(pSqlStmt, pWhereClause);
1637 break;
1638 case DB_DEL_MATCHING:
1639 BuildWhereClause(whereClause, DB_WHERE_MATCHING);
1640 wxStrcat(pSqlStmt, whereClause);
1641 break;
89894079 1642 }
108106cf 1643
f6bcfd97 1644} // BuildDeleteStmt()
108106cf 1645
67e9aaa3 1646
f6bcfd97
BP
1647/********** wxDbTable::BuildWhereClause() **********/
1648void wxDbTable::BuildWhereClause(char *pWhereClause, int typeOfWhere,
1649 const char *qualTableName, bool useLikeComparison)
108106cf 1650/*
f6bcfd97 1651 * Note: BuildWhereClause() currently ignores timestamp columns.
108106cf
JS
1652 * They are not included as part of the where clause.
1653 */
108106cf 1654{
89894079
VZ
1655 bool moreThanOneColumn = FALSE;
1656 char colValue[255];
1657
1658 // Loop through the columns building a where clause as you go
1659 int i;
1660 for (i = 0; i < noCols; i++)
1661 {
1662 // Determine if this column should be included in the WHERE clause
1663 if ((typeOfWhere == DB_WHERE_KEYFIELDS && colDefs[i].KeyField) ||
1664 (typeOfWhere == DB_WHERE_MATCHING && (! IsColNull(i))))
1665 {
1666 // Skip over timestamp columns
1667 if (colDefs[i].SqlCtype == SQL_C_TIMESTAMP)
1668 continue;
1669 // If there is more than 1 column, join them with the keyword "AND"
1670 if (moreThanOneColumn)
1671 wxStrcat(pWhereClause, " AND ");
1672 else
1673 moreThanOneColumn = TRUE;
1674 // Concatenate where phrase for the column
1675 if (qualTableName && wxStrlen(qualTableName))
1676 {
1677 wxStrcat(pWhereClause, qualTableName);
1678 wxStrcat(pWhereClause, ".");
1679 }
1680 wxStrcat(pWhereClause, colDefs[i].ColName);
8128349e
GT
1681 if (useLikeComparison && (colDefs[i].SqlCtype == SQL_C_CHAR))
1682 wxStrcat(pWhereClause, " LIKE ");
1683 else
1684 wxStrcat(pWhereClause, " = ");
89894079
VZ
1685 switch(colDefs[i].SqlCtype)
1686 {
3ca6a5f0
BP
1687 case SQL_C_CHAR:
1688 sprintf(colValue, "'%s'", (UCHAR FAR *) colDefs[i].PtrDataObj);
1689 break;
1690 case SQL_C_SSHORT:
1691 sprintf(colValue, "%hi", *((SWORD *) colDefs[i].PtrDataObj));
1692 break;
1693 case SQL_C_USHORT:
1694 sprintf(colValue, "%hu", *((UWORD *) colDefs[i].PtrDataObj));
1695 break;
1696 case SQL_C_SLONG:
1697 sprintf(colValue, "%li", *((SDWORD *) colDefs[i].PtrDataObj));
1698 break;
1699 case SQL_C_ULONG:
1700 sprintf(colValue, "%lu", *((UDWORD *) colDefs[i].PtrDataObj));
1701 break;
1702 case SQL_C_FLOAT:
1703 sprintf(colValue, "%.6f", *((SFLOAT *) colDefs[i].PtrDataObj));
1704 break;
1705 case SQL_C_DOUBLE:
1706 sprintf(colValue, "%.6f", *((SDOUBLE *) colDefs[i].PtrDataObj));
1707 break;
89894079
VZ
1708 }
1709 wxStrcat(pWhereClause, colValue);
1710 }
1711 }
f6bcfd97 1712} // wxDbTable::BuildWhereClause()
108106cf 1713
67e9aaa3 1714
f6bcfd97
BP
1715/********** wxDbTable::IsColNull() **********/
1716bool wxDbTable::IsColNull(int colNo)
108106cf 1717{
89894079
VZ
1718 switch(colDefs[colNo].SqlCtype)
1719 {
3ca6a5f0
BP
1720 case SQL_C_CHAR:
1721 return(((UCHAR FAR *) colDefs[colNo].PtrDataObj)[0] == 0);
1722 case SQL_C_SSHORT:
1723 return(( *((SWORD *) colDefs[colNo].PtrDataObj)) == 0);
1724 case SQL_C_USHORT:
1725 return(( *((UWORD*) colDefs[colNo].PtrDataObj)) == 0);
1726 case SQL_C_SLONG:
1727 return(( *((SDWORD *) colDefs[colNo].PtrDataObj)) == 0);
1728 case SQL_C_ULONG:
1729 return(( *((UDWORD *) colDefs[colNo].PtrDataObj)) == 0);
1730 case SQL_C_FLOAT:
1731 return(( *((SFLOAT *) colDefs[colNo].PtrDataObj)) == 0);
1732 case SQL_C_DOUBLE:
1733 return((*((SDOUBLE *) colDefs[colNo].PtrDataObj)) == 0);
1734 case SQL_C_TIMESTAMP:
1735 TIMESTAMP_STRUCT *pDt;
1736 pDt = (TIMESTAMP_STRUCT *) colDefs[colNo].PtrDataObj;
1737 if (pDt->year == 0 && pDt->month == 0 && pDt->day == 0)
1738 return(TRUE);
1739 else
1740 return(FALSE);
1741 default:
89894079 1742 return(TRUE);
89894079 1743 }
f6bcfd97 1744} // wxDbTable::IsColNull()
108106cf 1745
67e9aaa3 1746
f6bcfd97
BP
1747/********** wxDbTable::CanSelectForUpdate() **********/
1748bool wxDbTable::CanSelectForUpdate(void)
108106cf 1749{
89894079
VZ
1750 if (pDb->Dbms() == dbmsMY_SQL)
1751 return FALSE;
a2115c88 1752
89894079
VZ
1753 if (pDb->dbInf.posStmts & SQL_PS_SELECT_FOR_UPDATE)
1754 return(TRUE);
1755 else
1756 return(FALSE);
108106cf 1757
f6bcfd97 1758} // wxDbTable::CanSelectForUpdate()
108106cf 1759
67e9aaa3 1760
f6bcfd97
BP
1761/********** wxDbTable::CanUpdByROWID() **********/
1762bool wxDbTable::CanUpdByROWID(void)
108106cf 1763{
67e9aaa3
GT
1764/*
1765 * NOTE: Returning FALSE for now until this can be debugged,
89894079 1766 * as the ROWID is not getting updated correctly
67e9aaa3 1767 */
89894079 1768 return FALSE;
108106cf 1769
89894079
VZ
1770 if (pDb->Dbms() == dbmsORACLE)
1771 return(TRUE);
1772 else
1773 return(FALSE);
108106cf 1774
f6bcfd97 1775} // wxDbTable::CanUpdByROWID()
108106cf 1776
67e9aaa3 1777
f6bcfd97
BP
1778/********** wxDbTable::IsCursorClosedOnCommit() **********/
1779bool wxDbTable::IsCursorClosedOnCommit(void)
108106cf 1780{
89894079
VZ
1781 if (pDb->dbInf.cursorCommitBehavior == SQL_CB_PRESERVE)
1782 return(FALSE);
1783 else
1784 return(TRUE);
108106cf 1785
f6bcfd97 1786} // wxDbTable::IsCursorClosedOnCommit()
108106cf 1787
67e9aaa3 1788
f6bcfd97
BP
1789/********** wxDbTable::ClearMemberVars() **********/
1790void wxDbTable::ClearMemberVars(void)
108106cf 1791{
89894079
VZ
1792 // Loop through the columns setting each member variable to zero
1793 int i;
1794 for (i = 0; i < noCols; i++)
1795 {
1796 switch(colDefs[i].SqlCtype)
1797 {
3ca6a5f0
BP
1798 case SQL_C_CHAR:
1799 ((UCHAR FAR *) colDefs[i].PtrDataObj)[0] = 0;
1800 break;
1801 case SQL_C_SSHORT:
1802 *((SWORD *) colDefs[i].PtrDataObj) = 0;
1803 break;
1804 case SQL_C_USHORT:
1805 *((UWORD*) colDefs[i].PtrDataObj) = 0;
1806 break;
1807 case SQL_C_SLONG:
1808 *((SDWORD *) colDefs[i].PtrDataObj) = 0;
1809 break;
1810 case SQL_C_ULONG:
1811 *((UDWORD *) colDefs[i].PtrDataObj) = 0;
1812 break;
1813 case SQL_C_FLOAT:
1814 *((SFLOAT *) colDefs[i].PtrDataObj) = 0.0f;
1815 break;
1816 case SQL_C_DOUBLE:
1817 *((SDOUBLE *) colDefs[i].PtrDataObj) = 0.0f;
1818 break;
1819 case SQL_C_TIMESTAMP:
1820 TIMESTAMP_STRUCT *pDt;
1821 pDt = (TIMESTAMP_STRUCT *) colDefs[i].PtrDataObj;
1822 pDt->year = 0;
1823 pDt->month = 0;
1824 pDt->day = 0;
1825 pDt->hour = 0;
1826 pDt->minute = 0;
1827 pDt->second = 0;
1828 pDt->fraction = 0;
1829 break;
89894079
VZ
1830 }
1831 }
108106cf 1832
f6bcfd97 1833} // wxDbTable::ClearMemberVars()
108106cf 1834
67e9aaa3 1835
f6bcfd97
BP
1836/********** wxDbTable::SetQueryTimeout() **********/
1837bool wxDbTable::SetQueryTimeout(UDWORD nSeconds)
108106cf 1838{
89894079
VZ
1839 if (SQLSetStmtOption(hstmtInsert, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
1840 return(pDb->DispAllErrors(henv, hdbc, hstmtInsert));
1841 if (SQLSetStmtOption(hstmtUpdate, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
1842 return(pDb->DispAllErrors(henv, hdbc, hstmtUpdate));
1843 if (SQLSetStmtOption(hstmtDelete, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
1844 return(pDb->DispAllErrors(henv, hdbc, hstmtDelete));
1845 if (SQLSetStmtOption(hstmtInternal, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
1846 return(pDb->DispAllErrors(henv, hdbc, hstmtInternal));
1847
1848 // Completed Successfully
1849 return(TRUE);
108106cf 1850
f6bcfd97 1851} // wxDbTable::SetQueryTimeout()
108106cf 1852
67e9aaa3 1853
f6bcfd97
BP
1854/********** wxDbTable::SetColDefs() **********/
1855void wxDbTable::SetColDefs (int index, const char *fieldName, int dataType, void *pData,
3ca6a5f0
BP
1856 int cType, int size, bool keyField, bool upd,
1857 bool insAllow, bool derivedCol)
108106cf 1858{
89894079
VZ
1859 if (!colDefs) // May happen if the database connection fails
1860 return;
1861
1862 if (wxStrlen(fieldName) > (unsigned int) DB_MAX_COLUMN_NAME_LEN)
1863 {
1864 wxStrncpy (colDefs[index].ColName, fieldName, DB_MAX_COLUMN_NAME_LEN);
1865 colDefs[index].ColName[DB_MAX_COLUMN_NAME_LEN] = 0;
1866 }
1867 else
1868 wxStrcpy(colDefs[index].ColName, fieldName);
1869
1870 colDefs[index].DbDataType = dataType;
1871 colDefs[index].PtrDataObj = pData;
1872 colDefs[index].SqlCtype = cType;
1873 colDefs[index].SzDataObj = size;
1874 colDefs[index].KeyField = keyField;
1875 colDefs[index].DerivedCol = derivedCol;
1876 // Derived columns by definition would NOT be "Insertable" or "Updateable"
1877 if (derivedCol)
1878 {
1879 colDefs[index].Updateable = FALSE;
1880 colDefs[index].InsertAllowed = FALSE;
1881 }
1882 else
1883 {
1884 colDefs[index].Updateable = upd;
1885 colDefs[index].InsertAllowed = insAllow;
1886 }
1887
1888 colDefs[index].Null = FALSE;
1889
f6bcfd97 1890} // wxDbTable::SetColDefs()
108106cf 1891
67e9aaa3 1892
f6bcfd97
BP
1893/********** wxDbTable::SetColDef() **********/
1894wxDbColDataPtr* wxDbTable::SetColDefs (wxDbColInf *pColInfs, ULONG numCols)
67e9aaa3 1895{
89894079 1896 assert(pColInfs);
f6bcfd97 1897 wxDbColDataPtr *pColDataPtrs = NULL;
67e9aaa3 1898
89894079
VZ
1899 if (pColInfs)
1900 {
1901 ULONG index;
1b7274a8 1902
f6bcfd97 1903 pColDataPtrs = new wxDbColDataPtr[numCols+1];
67e9aaa3
GT
1904
1905 for (index = 0; index < numCols; index++)
89894079 1906 {
89894079
VZ
1907 // Process the fields
1908 switch (pColInfs[index].dbDataType)
1909 {
1910 case DB_DATA_TYPE_VARCHAR:
0b8410f3
GT
1911 pColDataPtrs[index].PtrDataObj = new char[pColInfs[index].bufferLength+1];
1912 pColDataPtrs[index].SzDataObj = pColInfs[index].columnSize;
1913 pColDataPtrs[index].SqlCtype = SQL_C_CHAR;
1914 break;
89894079 1915 case DB_DATA_TYPE_INTEGER:
67e9aaa3 1916 // Can be long or short
89894079
VZ
1917 if (pColInfs[index].bufferLength == sizeof(long))
1918 {
1919 pColDataPtrs[index].PtrDataObj = new long;
1920 pColDataPtrs[index].SzDataObj = sizeof(long);
1921 pColDataPtrs[index].SqlCtype = SQL_C_SLONG;
1922 }
1923 else
1924 {
1925 pColDataPtrs[index].PtrDataObj = new short;
1926 pColDataPtrs[index].SzDataObj = sizeof(short);
1927 pColDataPtrs[index].SqlCtype = SQL_C_SSHORT;
1928 }
1929 break;
89894079 1930 case DB_DATA_TYPE_FLOAT:
89894079
VZ
1931 // Can be float or double
1932 if (pColInfs[index].bufferLength == sizeof(float))
1933 {
1934 pColDataPtrs[index].PtrDataObj = new float;
1935 pColDataPtrs[index].SzDataObj = sizeof(float);
1936 pColDataPtrs[index].SqlCtype = SQL_C_FLOAT;
1937 }
1938 else
1939 {
1940 pColDataPtrs[index].PtrDataObj = new double;
1941 pColDataPtrs[index].SzDataObj = sizeof(double);
1942 pColDataPtrs[index].SqlCtype = SQL_C_DOUBLE;
1943 }
1944 break;
89894079 1945 case DB_DATA_TYPE_DATE:
89894079
VZ
1946 pColDataPtrs[index].PtrDataObj = new TIMESTAMP_STRUCT;
1947 pColDataPtrs[index].SzDataObj = sizeof(TIMESTAMP_STRUCT);
1948 pColDataPtrs[index].SqlCtype = SQL_C_TIMESTAMP;
1949 break;
89894079 1950 }
3ca6a5f0 1951 SetColDefs (index,pColInfs[index].colName,pColInfs[index].dbDataType, pColDataPtrs[index].PtrDataObj, pColDataPtrs[index].SqlCtype, pColDataPtrs[index].SzDataObj);
89894079
VZ
1952 }
1953 }
3ca6a5f0 1954
89894079 1955 return (pColDataPtrs);
3ca6a5f0 1956
f6bcfd97 1957} // wxDbTable::SetColDef()
67e9aaa3
GT
1958
1959
f6bcfd97
BP
1960/********** wxDbTable::SetCursor() **********/
1961void wxDbTable::SetCursor(HSTMT *hstmtActivate)
108106cf 1962{
f6bcfd97 1963 if (hstmtActivate == wxDB_DEFAULT_CURSOR)
89894079
VZ
1964 hstmt = *hstmtDefault;
1965 else
1966 hstmt = *hstmtActivate;
108106cf 1967
f6bcfd97 1968} // wxDbTable::SetCursor()
108106cf 1969
67e9aaa3 1970
f6bcfd97
BP
1971/********** wxDbTable::Count(const char *) **********/
1972ULONG wxDbTable::Count(const char *args)
108106cf 1973{
3ca6a5f0 1974 ULONG count;
1e92909e 1975 wxString sqlStmt;
89894079
VZ
1976 SDWORD cb;
1977
1978 // Build a "SELECT COUNT(*) FROM queryTableName [WHERE whereClause]" SQL Statement
1e92909e
GT
1979 sqlStmt = "SELECT COUNT(";
1980 sqlStmt += args;
1981 sqlStmt += ") FROM ";
1982 sqlStmt += queryTableName;
f6bcfd97 1983#if wxODBC_BACKWARD_COMPATABILITY
89894079 1984 if (from && wxStrlen(from))
f6bcfd97
BP
1985#else
1986 if (from.Length())
1987#endif
1e92909e 1988 sqlStmt += from;
89894079
VZ
1989
1990 // Add the where clause if one is provided
f6bcfd97 1991#if wxODBC_BACKWARD_COMPATABILITY
89894079 1992 if (where && wxStrlen(where))
f6bcfd97
BP
1993#else
1994 if (where.Length())
1995#endif
89894079 1996 {
1e92909e
GT
1997 sqlStmt += " WHERE ";
1998 sqlStmt += where;
89894079
VZ
1999 }
2000
f6bcfd97 2001 pDb->WriteSqlLog(sqlStmt.c_str());
89894079
VZ
2002
2003 // Initialize the Count cursor if it's not already initialized
2004 if (!hstmtCount)
2005 {
f6bcfd97 2006 hstmtCount = GetNewCursor(FALSE,FALSE);
89894079
VZ
2007 assert(hstmtCount);
2008 if (!hstmtCount)
2009 return(0);
2010 }
2011
2012 // Execute the SQL statement
f6bcfd97 2013 if (SQLExecDirect(*hstmtCount, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
89894079
VZ
2014 {
2015 pDb->DispAllErrors(henv, hdbc, *hstmtCount);
2016 return(0);
2017 }
2018
2019 // Fetch the record
2020 if (SQLFetch(*hstmtCount) != SQL_SUCCESS)
2021 {
2022 pDb->DispAllErrors(henv, hdbc, *hstmtCount);
2023 return(0);
2024 }
2025
2026 // Obtain the result
3ca6a5f0 2027 if (SQLGetData(*hstmtCount, 1, SQL_C_ULONG, &count, sizeof(count), &cb) != SQL_SUCCESS)
89894079
VZ
2028 {
2029 pDb->DispAllErrors(henv, hdbc, *hstmtCount);
2030 return(0);
2031 }
2032
2033 // Free the cursor
2034 if (SQLFreeStmt(*hstmtCount, SQL_CLOSE) != SQL_SUCCESS)
2035 pDb->DispAllErrors(henv, hdbc, *hstmtCount);
2036
2037 // Return the record count
3ca6a5f0 2038 return(count);
108106cf 2039
f6bcfd97 2040} // wxDbTable::Count()
108106cf 2041
67e9aaa3 2042
f6bcfd97
BP
2043/********** wxDbTable::Refresh() **********/
2044bool wxDbTable::Refresh(void)
108106cf 2045{
89894079
VZ
2046 bool result = TRUE;
2047
2048 // Switch to the internal cursor so any active cursors are not corrupted
2049 HSTMT currCursor = GetCursor();
2050 hstmt = hstmtInternal;
f6bcfd97 2051#if wxODBC_BACKWARD_COMPATABILITY
89894079
VZ
2052 // Save the where and order by clauses
2053 char *saveWhere = where;
2054 char *saveOrderBy = orderBy;
f6bcfd97
BP
2055#else
2056 wxString saveWhere = where;
2057 wxString saveOrderBy = orderBy;
2058#endif
89894079
VZ
2059 // Build a where clause to refetch the record with. Try and use the
2060 // ROWID if it's available, ow use the key fields.
2061 char whereClause[DB_MAX_WHERE_CLAUSE_LEN+1];
2062 wxStrcpy(whereClause, "");
2063 if (CanUpdByROWID())
2064 {
2065 SDWORD cb;
f6bcfd97 2066 char rowid[wxDB_ROWID_LEN+1];
89894079
VZ
2067
2068 // Get the ROWID value. If not successful retreiving the ROWID,
2069 // simply fall down through the code and build the WHERE clause
2070 // based on the key fields.
f6bcfd97 2071 if (SQLGetData(hstmt, noCols+1, SQL_C_CHAR, (UCHAR*) rowid, wxDB_ROWID_LEN, &cb) == SQL_SUCCESS)
89894079
VZ
2072 {
2073 wxStrcat(whereClause, queryTableName);
2074 wxStrcat(whereClause, ".ROWID = '");
2075 wxStrcat(whereClause, rowid);
2076 wxStrcat(whereClause, "'");
2077 }
2078 }
2079
2080 // If unable to use the ROWID, build a where clause from the keyfields
2081 if (wxStrlen(whereClause) == 0)
f6bcfd97 2082 BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS, queryTableName);
89894079
VZ
2083
2084 // Requery the record
2085 where = whereClause;
f6bcfd97 2086 orderBy = "";
89894079
VZ
2087 if (!Query())
2088 result = FALSE;
2089
2090 if (result && !GetNext())
2091 result = FALSE;
2092
2093 // Switch back to original cursor
2094 SetCursor(&currCursor);
2095
2096 // Free the internal cursor
2097 if (SQLFreeStmt(hstmtInternal, SQL_CLOSE) != SQL_SUCCESS)
2098 pDb->DispAllErrors(henv, hdbc, hstmtInternal);
2099
2100 // Restore the original where and order by clauses
1e92909e 2101 where = saveWhere;
89894079
VZ
2102 orderBy = saveOrderBy;
2103
2104 return(result);
108106cf 2105
f6bcfd97 2106} // wxDbTable::Refresh()
108106cf 2107
67e9aaa3 2108
f6bcfd97
BP
2109/********** wxDbTable::SetNull(int colNo) **********/
2110bool wxDbTable::SetNull(int colNo)
a2115c88 2111{
89894079
VZ
2112 if (colNo < noCols)
2113 return(colDefs[colNo].Null = TRUE);
2114 else
2115 return(FALSE);
a2115c88 2116
f6bcfd97 2117} // wxDbTable::SetNull(int colNo)
67e9aaa3 2118
a2115c88 2119
f6bcfd97
BP
2120/********** wxDbTable::SetNull(char *colName) **********/
2121bool wxDbTable::SetNull(const char *colName)
a2115c88 2122{
89894079
VZ
2123 int i;
2124 for (i = 0; i < noCols; i++)
2125 {
2126 if (!wxStricmp(colName, colDefs[i].ColName))
2127 break;
2128 }
2129
2130 if (i < noCols)
2131 return(colDefs[i].Null = TRUE);
2132 else
2133 return(FALSE);
a2115c88 2134
f6bcfd97 2135} // wxDbTable::SetNull(char *colName)
a2115c88 2136
67e9aaa3 2137
f6bcfd97
BP
2138/********** wxDbTable::GetNewCursor() **********/
2139HSTMT *wxDbTable::GetNewCursor(bool setCursor, bool bindColumns)
a2115c88 2140{
89894079
VZ
2141 HSTMT *newHSTMT = new HSTMT;
2142 assert(newHSTMT);
2143 if (!newHSTMT)
2144 return(0);
2145
2146 if (SQLAllocStmt(hdbc, newHSTMT) != SQL_SUCCESS)
2147 {
2148 pDb->DispAllErrors(henv, hdbc);
2149 delete newHSTMT;
2150 return(0);
2151 }
2152
2153 if (SQLSetStmtOption(*newHSTMT, SQL_CURSOR_TYPE, cursorType) != SQL_SUCCESS)
2154 {
2155 pDb->DispAllErrors(henv, hdbc, *newHSTMT);
2156 delete newHSTMT;
2157 return(0);
2158 }
2159
2160 if (bindColumns)
2161 {
2162 if(!bindCols(*newHSTMT))
2163 {
2164 delete newHSTMT;
2165 return(0);
2166 }
2167 }
2168
2169 if (setCursor)
2170 SetCursor(newHSTMT);
2171
2172 return(newHSTMT);
2173
f6bcfd97 2174} // wxDbTable::GetNewCursor()
67e9aaa3 2175
a2115c88 2176
f6bcfd97
BP
2177/********** wxDbTable::DeleteCursor() **********/
2178bool wxDbTable::DeleteCursor(HSTMT *hstmtDel)
a2115c88 2179{
89894079 2180 bool result = TRUE;
a2115c88 2181
89894079
VZ
2182 if (!hstmtDel) // Cursor already deleted
2183 return(result);
a2115c88 2184
89894079
VZ
2185 if (SQLFreeStmt(*hstmtDel, SQL_DROP) != SQL_SUCCESS)
2186 {
2187 pDb->DispAllErrors(henv, hdbc);
2188 result = FALSE;
2189 }
a2115c88 2190
89894079 2191 delete hstmtDel;
a2115c88 2192
89894079 2193 return(result);
a2115c88 2194
f6bcfd97 2195} // wxDbTable::DeleteCursor()
a2115c88
GT
2196
2197#endif // wxUSE_ODBC
1fc5dd6f 2198