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