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