/[pcsx2_0.9.7]/trunk/3rdparty/wxWidgets/src/common/dbtable.cpp
ViewVC logotype

Contents of /trunk/3rdparty/wxWidgets/src/common/dbtable.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 31 - (show annotations) (download)
Tue Sep 7 03:24:11 2010 UTC (10 years, 2 months ago) by william
File size: 92239 byte(s)
committing r3113 initial commit again...
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/dbtable.cpp
3 // Purpose: Implementation of the wxDbTable class.
4 // Author: Doug Card
5 // Modified by: George Tasker
6 // Bart Jourquin
7 // Mark Johnson
8 // Created: 9.96
9 // RCS-ID: $Id: dbtable.cpp 48685 2007-09-14 19:02:28Z VZ $
10 // Copyright: (c) 1996 Remstar International, Inc.
11 // Licence: wxWindows licence
12 ///////////////////////////////////////////////////////////////////////////////
13
14 #include "wx/wxprec.h"
15
16 #ifdef __BORLANDC__
17 #pragma hdrstop
18 #endif
19
20 #if wxUSE_ODBC
21
22 #ifndef WX_PRECOMP
23 #include "wx/object.h"
24 #include "wx/list.h"
25 #include "wx/string.h"
26 #include "wx/utils.h"
27 #include "wx/log.h"
28 #endif
29
30 #ifdef DBDEBUG_CONSOLE
31 #if wxUSE_IOSTREAMH
32 #include <iostream.h>
33 #else
34 #include <iostream>
35 #endif
36 #include "wx/ioswrap.h"
37 #endif
38
39 #include "wx/filefn.h"
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44
45 #include "wx/dbtable.h"
46
47 #ifdef __UNIX__
48 // The HPUX preprocessor lines below were commented out on 8/20/97
49 // because macros.h currently redefines DEBUG and is unneeded.
50 // # ifdef HPUX
51 // # include <macros.h>
52 // # endif
53 # ifdef LINUX
54 # include <sys/minmax.h>
55 # endif
56 #endif
57
58 ULONG lastTableID = 0;
59
60
61 #ifdef __WXDEBUG__
62 #include "wx/thread.h"
63
64 wxList TablesInUse;
65 #if wxUSE_THREADS
66 wxCriticalSection csTablesInUse;
67 #endif // wxUSE_THREADS
68 #endif
69
70
71 void csstrncpyt(wxChar *target, const wxChar *source, int n)
72 {
73 while ( (*target++ = *source++) != '\0' && --n != 0 )
74 ;
75
76 *target = '\0';
77 }
78
79
80
81 /********** wxDbColDef::wxDbColDef() Constructor **********/
82 wxDbColDef::wxDbColDef()
83 {
84 Initialize();
85 } // Constructor
86
87
88 bool wxDbColDef::Initialize()
89 {
90 ColName[0] = 0;
91 DbDataType = DB_DATA_TYPE_INTEGER;
92 SqlCtype = SQL_C_LONG;
93 PtrDataObj = NULL;
94 SzDataObj = 0;
95 KeyField = false;
96 Updateable = false;
97 InsertAllowed = false;
98 DerivedCol = false;
99 CbValue = 0;
100 Null = false;
101
102 return true;
103 } // wxDbColDef::Initialize()
104
105
106 /********** wxDbTable::wxDbTable() Constructor **********/
107 wxDbTable::wxDbTable(wxDb *pwxDb, const wxString &tblName, const UWORD numColumns,
108 const wxString &qryTblName, bool qryOnly, const wxString &tblPath)
109 {
110 if (!initialize(pwxDb, tblName, numColumns, qryTblName, qryOnly, tblPath))
111 cleanup();
112 } // wxDbTable::wxDbTable()
113
114
115 /***** DEPRECATED: use wxDbTable::wxDbTable() format above *****/
116 #if WXWIN_COMPATIBILITY_2_4
117 wxDbTable::wxDbTable(wxDb *pwxDb, const wxString &tblName, const UWORD numColumns,
118 const wxChar *qryTblName, bool qryOnly, const wxString &tblPath)
119 {
120 wxString tempQryTblName;
121 tempQryTblName = qryTblName;
122 if (!initialize(pwxDb, tblName, numColumns, tempQryTblName, qryOnly, tblPath))
123 cleanup();
124 } // wxDbTable::wxDbTable()
125 #endif // WXWIN_COMPATIBILITY_2_4
126
127
128 /********** wxDbTable::~wxDbTable() **********/
129 wxDbTable::~wxDbTable()
130 {
131 this->cleanup();
132 } // wxDbTable::~wxDbTable()
133
134
135 bool wxDbTable::initialize(wxDb *pwxDb, const wxString &tblName, const UWORD numColumns,
136 const wxString &qryTblName, bool qryOnly, const wxString &tblPath)
137 {
138 // Initializing member variables
139 pDb = pwxDb; // Pointer to the wxDb object
140 henv = 0;
141 hdbc = 0;
142 hstmt = 0;
143 m_hstmtGridQuery = 0;
144 hstmtDefault = 0; // Initialized below
145 hstmtCount = 0; // Initialized first time it is needed
146 hstmtInsert = 0;
147 hstmtDelete = 0;
148 hstmtUpdate = 0;
149 hstmtInternal = 0;
150 colDefs = 0;
151 tableID = 0;
152 m_numCols = numColumns; // Number of columns in the table
153 where.Empty(); // Where clause
154 orderBy.Empty(); // Order By clause
155 from.Empty(); // From clause
156 selectForUpdate = false; // SELECT ... FOR UPDATE; Indicates whether to include the FOR UPDATE phrase
157 queryOnly = qryOnly;
158 insertable = true;
159 tablePath.Empty();
160 tableName.Empty();
161 queryTableName.Empty();
162
163 wxASSERT(tblName.length());
164 wxASSERT(pDb);
165
166 if (!pDb)
167 return false;
168
169 tableName = tblName; // Table Name
170 if ((pDb->Dbms() == dbmsORACLE) ||
171 (pDb->Dbms() == dbmsFIREBIRD) ||
172 (pDb->Dbms() == dbmsINTERBASE))
173 tableName = tableName.Upper();
174
175 if (tblPath.length())
176 tablePath = tblPath; // Table Path - used for dBase files
177 else
178 tablePath.Empty();
179
180 if (qryTblName.length()) // Name of the table/view to query
181 queryTableName = qryTblName;
182 else
183 queryTableName = tblName;
184
185 if ((pDb->Dbms() == dbmsORACLE) ||
186 (pDb->Dbms() == dbmsFIREBIRD) ||
187 (pDb->Dbms() == dbmsINTERBASE))
188 queryTableName = queryTableName.Upper();
189
190 pDb->incrementTableCount();
191
192 wxString s;
193 tableID = ++lastTableID;
194 s.Printf(wxT("wxDbTable constructor (%-20s) tableID:[%6lu] pDb:[%p]"),
195 tblName.c_str(), tableID, wx_static_cast(void*, pDb));
196
197 #ifdef __WXDEBUG__
198 wxTablesInUse *tableInUse;
199 tableInUse = new wxTablesInUse();
200 tableInUse->tableName = tblName;
201 tableInUse->tableID = tableID;
202 tableInUse->pDb = pDb;
203 {
204 #if wxUSE_THREADS
205 wxCriticalSectionLocker lock(csTablesInUse);
206 #endif // wxUSE_THREADS
207 TablesInUse.Append(tableInUse);
208 }
209 #endif
210
211 pDb->WriteSqlLog(s);
212
213 // Grab the HENV and HDBC from the wxDb object
214 henv = pDb->GetHENV();
215 hdbc = pDb->GetHDBC();
216
217 // Allocate space for column definitions
218 if (m_numCols)
219 colDefs = new wxDbColDef[m_numCols]; // Points to the first column definition
220
221 // Allocate statement handles for the table
222 if (!queryOnly)
223 {
224 // Allocate a separate statement handle for performing inserts
225 if (SQLAllocStmt(hdbc, &hstmtInsert) != SQL_SUCCESS)
226 pDb->DispAllErrors(henv, hdbc);
227 // Allocate a separate statement handle for performing deletes
228 if (SQLAllocStmt(hdbc, &hstmtDelete) != SQL_SUCCESS)
229 pDb->DispAllErrors(henv, hdbc);
230 // Allocate a separate statement handle for performing updates
231 if (SQLAllocStmt(hdbc, &hstmtUpdate) != SQL_SUCCESS)
232 pDb->DispAllErrors(henv, hdbc);
233 }
234 // Allocate a separate statement handle for internal use
235 if (SQLAllocStmt(hdbc, &hstmtInternal) != SQL_SUCCESS)
236 pDb->DispAllErrors(henv, hdbc);
237
238 // Set the cursor type for the statement handles
239 cursorType = SQL_CURSOR_STATIC;
240
241 if (SQLSetStmtOption(hstmtInternal, SQL_CURSOR_TYPE, cursorType) != SQL_SUCCESS)
242 {
243 // Check to see if cursor type is supported
244 pDb->GetNextError(henv, hdbc, hstmtInternal);
245 if (! wxStrcmp(pDb->sqlState, wxT("01S02"))) // Option Value Changed
246 {
247 // Datasource does not support static cursors. Driver
248 // will substitute a cursor type. Call SQLGetStmtOption()
249 // to determine which cursor type was selected.
250 if (SQLGetStmtOption(hstmtInternal, SQL_CURSOR_TYPE, &cursorType) != SQL_SUCCESS)
251 pDb->DispAllErrors(henv, hdbc, hstmtInternal);
252 #ifdef DBDEBUG_CONSOLE
253 cout << wxT("Static cursor changed to: ");
254 switch(cursorType)
255 {
256 case SQL_CURSOR_FORWARD_ONLY:
257 cout << wxT("Forward Only");
258 break;
259 case SQL_CURSOR_STATIC:
260 cout << wxT("Static");
261 break;
262 case SQL_CURSOR_KEYSET_DRIVEN:
263 cout << wxT("Keyset Driven");
264 break;
265 case SQL_CURSOR_DYNAMIC:
266 cout << wxT("Dynamic");
267 break;
268 }
269 cout << endl << endl;
270 #endif
271 // BJO20000425
272 if (pDb->FwdOnlyCursors() && cursorType != SQL_CURSOR_FORWARD_ONLY)
273 {
274 // Force the use of a forward only cursor...
275 cursorType = SQL_CURSOR_FORWARD_ONLY;
276 if (SQLSetStmtOption(hstmtInternal, SQL_CURSOR_TYPE, cursorType) != SQL_SUCCESS)
277 {
278 // Should never happen
279 pDb->GetNextError(henv, hdbc, hstmtInternal);
280 return false;
281 }
282 }
283 }
284 else
285 {
286 pDb->DispNextError();
287 pDb->DispAllErrors(henv, hdbc, hstmtInternal);
288 }
289 }
290 #ifdef DBDEBUG_CONSOLE
291 else
292 cout << wxT("Cursor Type set to STATIC") << endl << endl;
293 #endif
294
295 if (!queryOnly)
296 {
297 // Set the cursor type for the INSERT statement handle
298 if (SQLSetStmtOption(hstmtInsert, SQL_CURSOR_TYPE, SQL_CURSOR_FORWARD_ONLY) != SQL_SUCCESS)
299 pDb->DispAllErrors(henv, hdbc, hstmtInsert);
300 // Set the cursor type for the DELETE statement handle
301 if (SQLSetStmtOption(hstmtDelete, SQL_CURSOR_TYPE, SQL_CURSOR_FORWARD_ONLY) != SQL_SUCCESS)
302 pDb->DispAllErrors(henv, hdbc, hstmtDelete);
303 // Set the cursor type for the UPDATE statement handle
304 if (SQLSetStmtOption(hstmtUpdate, SQL_CURSOR_TYPE, SQL_CURSOR_FORWARD_ONLY) != SQL_SUCCESS)
305 pDb->DispAllErrors(henv, hdbc, hstmtUpdate);
306 }
307
308 // Make the default cursor the active cursor
309 hstmtDefault = GetNewCursor(false,false);
310 wxASSERT(hstmtDefault);
311 hstmt = *hstmtDefault;
312
313 return true;
314
315 } // wxDbTable::initialize()
316
317
318 void wxDbTable::cleanup()
319 {
320 wxString s;
321 if (pDb)
322 {
323 s.Printf(wxT("wxDbTable destructor (%-20s) tableID:[%6lu] pDb:[%p]"),
324 tableName.c_str(), tableID, wx_static_cast(void*, pDb));
325 pDb->WriteSqlLog(s);
326 }
327
328 #ifdef __WXDEBUG__
329 if (tableID)
330 {
331 bool found = false;
332
333 wxList::compatibility_iterator pNode;
334 {
335 #if wxUSE_THREADS
336 wxCriticalSectionLocker lock(csTablesInUse);
337 #endif // wxUSE_THREADS
338 pNode = TablesInUse.GetFirst();
339 while (!found && pNode)
340 {
341 if (((wxTablesInUse *)pNode->GetData())->tableID == tableID)
342 {
343 found = true;
344 delete (wxTablesInUse *)pNode->GetData();
345 TablesInUse.Erase(pNode);
346 }
347 else
348 pNode = pNode->GetNext();
349 }
350 }
351 if (!found)
352 {
353 wxString msg;
354 msg.Printf(wxT("Unable to find the tableID in the linked\nlist of tables in use.\n\n%s"),s.c_str());
355 wxLogDebug (msg,wxT("NOTICE..."));
356 }
357 }
358 #endif
359
360 // Decrement the wxDb table count
361 if (pDb)
362 pDb->decrementTableCount();
363
364 // Delete memory allocated for column definitions
365 if (colDefs)
366 delete [] colDefs;
367
368 // Free statement handles
369 if (!queryOnly)
370 {
371 if (hstmtInsert)
372 {
373 /*
374 ODBC 3.0 says to use this form
375 if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS)
376 */
377 if (SQLFreeStmt(hstmtInsert, SQL_DROP) != SQL_SUCCESS)
378 pDb->DispAllErrors(henv, hdbc);
379 }
380
381 if (hstmtDelete)
382 {
383 /*
384 ODBC 3.0 says to use this form
385 if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS)
386 */
387 if (SQLFreeStmt(hstmtDelete, SQL_DROP) != SQL_SUCCESS)
388 pDb->DispAllErrors(henv, hdbc);
389 }
390
391 if (hstmtUpdate)
392 {
393 /*
394 ODBC 3.0 says to use this form
395 if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS)
396 */
397 if (SQLFreeStmt(hstmtUpdate, SQL_DROP) != SQL_SUCCESS)
398 pDb->DispAllErrors(henv, hdbc);
399 }
400 }
401
402 if (hstmtInternal)
403 {
404 if (SQLFreeStmt(hstmtInternal, SQL_DROP) != SQL_SUCCESS)
405 pDb->DispAllErrors(henv, hdbc);
406 }
407
408 // Delete dynamically allocated cursors
409 if (hstmtDefault)
410 DeleteCursor(hstmtDefault);
411
412 if (hstmtCount)
413 DeleteCursor(hstmtCount);
414
415 if (m_hstmtGridQuery)
416 DeleteCursor(m_hstmtGridQuery);
417
418 } // wxDbTable::cleanup()
419
420
421 /***************************** PRIVATE FUNCTIONS *****************************/
422
423
424 void wxDbTable::setCbValueForColumn(int columnIndex)
425 {
426 switch(colDefs[columnIndex].DbDataType)
427 {
428 case DB_DATA_TYPE_VARCHAR:
429 case DB_DATA_TYPE_MEMO:
430 if (colDefs[columnIndex].Null)
431 colDefs[columnIndex].CbValue = SQL_NULL_DATA;
432 else
433 colDefs[columnIndex].CbValue = SQL_NTS;
434 break;
435 case DB_DATA_TYPE_INTEGER:
436 if (colDefs[columnIndex].Null)
437 colDefs[columnIndex].CbValue = SQL_NULL_DATA;
438 else
439 colDefs[columnIndex].CbValue = 0;
440 break;
441 case DB_DATA_TYPE_FLOAT:
442 if (colDefs[columnIndex].Null)
443 colDefs[columnIndex].CbValue = SQL_NULL_DATA;
444 else
445 colDefs[columnIndex].CbValue = 0;
446 break;
447 case DB_DATA_TYPE_DATE:
448 if (colDefs[columnIndex].Null)
449 colDefs[columnIndex].CbValue = SQL_NULL_DATA;
450 else
451 colDefs[columnIndex].CbValue = 0;
452 break;
453 case DB_DATA_TYPE_BLOB:
454 if (colDefs[columnIndex].Null)
455 colDefs[columnIndex].CbValue = SQL_NULL_DATA;
456 else
457 if (colDefs[columnIndex].SqlCtype == SQL_C_WXCHAR)
458 colDefs[columnIndex].CbValue = SQL_NTS;
459 else
460 colDefs[columnIndex].CbValue = SQL_LEN_DATA_AT_EXEC(colDefs[columnIndex].SzDataObj);
461 break;
462 }
463 }
464
465 /********** wxDbTable::bindParams() **********/
466 bool wxDbTable::bindParams(bool forUpdate)
467 {
468 wxASSERT(!queryOnly);
469 if (queryOnly)
470 return false;
471
472 SWORD fSqlType = 0;
473 SDWORD precision = 0;
474 SWORD scale = 0;
475
476 // Bind each column of the table that should be bound
477 // to a parameter marker
478 int i;
479 UWORD colNumber;
480
481 for (i=0, colNumber=1; i < m_numCols; i++)
482 {
483 if (forUpdate)
484 {
485 if (!colDefs[i].Updateable)
486 continue;
487 }
488 else
489 {
490 if (!colDefs[i].InsertAllowed)
491 continue;
492 }
493
494 switch(colDefs[i].DbDataType)
495 {
496 case DB_DATA_TYPE_VARCHAR:
497 fSqlType = pDb->GetTypeInfVarchar().FsqlType;
498 precision = colDefs[i].SzDataObj;
499 scale = 0;
500 break;
501 case DB_DATA_TYPE_MEMO:
502 fSqlType = pDb->GetTypeInfMemo().FsqlType;
503 precision = colDefs[i].SzDataObj;
504 scale = 0;
505 break;
506 case DB_DATA_TYPE_INTEGER:
507 fSqlType = pDb->GetTypeInfInteger().FsqlType;
508 precision = pDb->GetTypeInfInteger().Precision;
509 scale = 0;
510 break;
511 case DB_DATA_TYPE_FLOAT:
512 fSqlType = pDb->GetTypeInfFloat().FsqlType;
513 precision = pDb->GetTypeInfFloat().Precision;
514 scale = pDb->GetTypeInfFloat().MaximumScale;
515 // SQL Sybase Anywhere v5.5 returned a negative number for the
516 // MaxScale. This caused ODBC to kick out an error on ibscale.
517 // I check for this here and set the scale = precision.
518 //if (scale < 0)
519 // scale = (short) precision;
520 break;
521 case DB_DATA_TYPE_DATE:
522 fSqlType = pDb->GetTypeInfDate().FsqlType;
523 precision = pDb->GetTypeInfDate().Precision;
524 scale = 0;
525 break;
526 case DB_DATA_TYPE_BLOB:
527 fSqlType = pDb->GetTypeInfBlob().FsqlType;
528 precision = colDefs[i].SzDataObj;
529 scale = 0;
530 break;
531 }
532
533 setCbValueForColumn(i);
534
535 if (forUpdate)
536 {
537 if (SQLBindParameter(hstmtUpdate, colNumber++, SQL_PARAM_INPUT, colDefs[i].SqlCtype,
538 fSqlType, precision, scale, (UCHAR*) colDefs[i].PtrDataObj,
539 precision+1, &colDefs[i].CbValue) != SQL_SUCCESS)
540 {
541 return(pDb->DispAllErrors(henv, hdbc, hstmtUpdate));
542 }
543 }
544 else
545 {
546 if (SQLBindParameter(hstmtInsert, colNumber++, SQL_PARAM_INPUT, colDefs[i].SqlCtype,
547 fSqlType, precision, scale, (UCHAR*) colDefs[i].PtrDataObj,
548 precision+1, &colDefs[i].CbValue) != SQL_SUCCESS)
549 {
550 return(pDb->DispAllErrors(henv, hdbc, hstmtInsert));
551 }
552 }
553 }
554
555 // Completed successfully
556 return true;
557
558 } // wxDbTable::bindParams()
559
560
561 /********** wxDbTable::bindInsertParams() **********/
562 bool wxDbTable::bindInsertParams(void)
563 {
564 return bindParams(false);
565 } // wxDbTable::bindInsertParams()
566
567
568 /********** wxDbTable::bindUpdateParams() **********/
569 bool wxDbTable::bindUpdateParams(void)
570 {
571 return bindParams(true);
572 } // wxDbTable::bindUpdateParams()
573
574
575 /********** wxDbTable::bindCols() **********/
576 bool wxDbTable::bindCols(HSTMT cursor)
577 {
578 // Bind each column of the table to a memory address for fetching data
579 UWORD i;
580 for (i = 0; i < m_numCols; i++)
581 {
582 if (SQLBindCol(cursor, (UWORD)(i+1), colDefs[i].SqlCtype, (UCHAR*) colDefs[i].PtrDataObj,
583 colDefs[i].SzDataObj, &colDefs[i].CbValue ) != SQL_SUCCESS)
584 return (pDb->DispAllErrors(henv, hdbc, cursor));
585 }
586
587 // Completed successfully
588 return true;
589 } // wxDbTable::bindCols()
590
591
592 /********** wxDbTable::getRec() **********/
593 bool wxDbTable::getRec(UWORD fetchType)
594 {
595 RETCODE retcode;
596
597 if (!pDb->FwdOnlyCursors())
598 {
599 // Fetch the NEXT, PREV, FIRST or LAST record, depending on fetchType
600 SQLULEN cRowsFetched;
601 UWORD rowStatus;
602
603 retcode = SQLExtendedFetch(hstmt, fetchType, 0, &cRowsFetched, &rowStatus);
604 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
605 {
606 if (retcode == SQL_NO_DATA_FOUND)
607 return false;
608 else
609 return(pDb->DispAllErrors(henv, hdbc, hstmt));
610 }
611 else
612 {
613 // Set the Null member variable to indicate the Null state
614 // of each column just read in.
615 int i;
616 for (i = 0; i < m_numCols; i++)
617 colDefs[i].Null = (colDefs[i].CbValue == SQL_NULL_DATA);
618 }
619 }
620 else
621 {
622 // Fetch the next record from the record set
623 retcode = SQLFetch(hstmt);
624 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
625 {
626 if (retcode == SQL_NO_DATA_FOUND)
627 return false;
628 else
629 return(pDb->DispAllErrors(henv, hdbc, hstmt));
630 }
631 else
632 {
633 // Set the Null member variable to indicate the Null state
634 // of each column just read in.
635 int i;
636 for (i = 0; i < m_numCols; i++)
637 colDefs[i].Null = (colDefs[i].CbValue == SQL_NULL_DATA);
638 }
639 }
640
641 // Completed successfully
642 return true;
643
644 } // wxDbTable::getRec()
645
646
647 /********** wxDbTable::execDelete() **********/
648 bool wxDbTable::execDelete(const wxString &pSqlStmt)
649 {
650 RETCODE retcode;
651
652 // Execute the DELETE statement
653 retcode = SQLExecDirect(hstmtDelete, (SQLTCHAR FAR *) pSqlStmt.c_str(), SQL_NTS);
654
655 if (retcode == SQL_SUCCESS ||
656 retcode == SQL_NO_DATA_FOUND ||
657 retcode == SQL_SUCCESS_WITH_INFO)
658 {
659 // Record deleted successfully
660 return true;
661 }
662
663 // Problem deleting record
664 return(pDb->DispAllErrors(henv, hdbc, hstmtDelete));
665
666 } // wxDbTable::execDelete()
667
668
669 /********** wxDbTable::execUpdate() **********/
670 bool wxDbTable::execUpdate(const wxString &pSqlStmt)
671 {
672 RETCODE retcode;
673
674 // Execute the UPDATE statement
675 retcode = SQLExecDirect(hstmtUpdate, (SQLTCHAR FAR *) pSqlStmt.c_str(), SQL_NTS);
676
677 if (retcode == SQL_SUCCESS ||
678 retcode == SQL_NO_DATA_FOUND ||
679 retcode == SQL_SUCCESS_WITH_INFO)
680 {
681 // Record updated successfully
682 return true;
683 }
684 else if (retcode == SQL_NEED_DATA)
685 {
686 PTR pParmID;
687 retcode = SQLParamData(hstmtUpdate, &pParmID);
688 while (retcode == SQL_NEED_DATA)
689 {
690 // Find the parameter
691 int i;
692 for (i=0; i < m_numCols; i++)
693 {
694 if (colDefs[i].PtrDataObj == pParmID)
695 {
696 // We found it. Store the parameter.
697 retcode = SQLPutData(hstmtUpdate, pParmID, colDefs[i].SzDataObj);
698 if (retcode != SQL_SUCCESS)
699 {
700 pDb->DispNextError();
701 return pDb->DispAllErrors(henv, hdbc, hstmtUpdate);
702 }
703 break;
704 }
705 }
706 retcode = SQLParamData(hstmtUpdate, &pParmID);
707 }
708 if (retcode == SQL_SUCCESS ||
709 retcode == SQL_NO_DATA_FOUND ||
710 retcode == SQL_SUCCESS_WITH_INFO)
711 {
712 // Record updated successfully
713 return true;
714 }
715 }
716
717 // Problem updating record
718 return(pDb->DispAllErrors(henv, hdbc, hstmtUpdate));
719
720 } // wxDbTable::execUpdate()
721
722
723 /********** wxDbTable::query() **********/
724 bool wxDbTable::query(int queryType, bool forUpdate, bool distinct, const wxString &pSqlStmt)
725 {
726 wxString sqlStmt;
727
728 if (forUpdate)
729 // The user may wish to select for update, but the DBMS may not be capable
730 selectForUpdate = CanSelectForUpdate();
731 else
732 selectForUpdate = false;
733
734 // Set the SQL SELECT string
735 if (queryType != DB_SELECT_STATEMENT) // A select statement was not passed in,
736 { // so generate a select statement.
737 BuildSelectStmt(sqlStmt, queryType, distinct);
738 pDb->WriteSqlLog(sqlStmt);
739 }
740
741 // Make sure the cursor is closed first
742 if (!CloseCursor(hstmt))
743 return false;
744
745 // Execute the SQL SELECT statement
746 int retcode;
747 retcode = SQLExecDirect(hstmt, (SQLTCHAR FAR *) (queryType == DB_SELECT_STATEMENT ? pSqlStmt.c_str() : sqlStmt.c_str()), SQL_NTS);
748 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
749 return(pDb->DispAllErrors(henv, hdbc, hstmt));
750
751 // Completed successfully
752 return true;
753
754 } // wxDbTable::query()
755
756
757 /***************************** PUBLIC FUNCTIONS *****************************/
758
759
760 /********** wxDbTable::Open() **********/
761 bool wxDbTable::Open(bool checkPrivileges, bool checkTableExists)
762 {
763 if (!pDb)
764 return false;
765
766 int i;
767 wxString sqlStmt;
768 wxString s;
769
770 // Calculate the maximum size of the concatenated
771 // keys for use with wxDbGrid
772 m_keysize = 0;
773 for (i=0; i < m_numCols; i++)
774 {
775 if (colDefs[i].KeyField)
776 {
777 m_keysize += colDefs[i].SzDataObj;
778 }
779 }
780
781 s.Empty();
782
783 bool exists = true;
784 if (checkTableExists)
785 {
786 if (pDb->Dbms() == dbmsPOSTGRES)
787 exists = pDb->TableExists(tableName, NULL, tablePath);
788 else
789 exists = pDb->TableExists(tableName, pDb->GetUsername(), tablePath);
790 }
791
792 // Verify that the table exists in the database
793 if (!exists)
794 {
795 s = wxT("Table/view does not exist in the database");
796 if ( *(pDb->dbInf.accessibleTables) == wxT('Y'))
797 s += wxT(", or you have no permissions.\n");
798 else
799 s += wxT(".\n");
800 }
801 else if (checkPrivileges)
802 {
803 // Verify the user has rights to access the table.
804 bool hasPrivs wxDUMMY_INITIALIZE(true);
805
806 if (pDb->Dbms() == dbmsPOSTGRES)
807 hasPrivs = pDb->TablePrivileges(tableName, wxT("SELECT"), pDb->GetUsername(), NULL, tablePath);
808 else
809 hasPrivs = pDb->TablePrivileges(tableName, wxT("SELECT"), pDb->GetUsername(), pDb->GetUsername(), tablePath);
810
811 if (!hasPrivs)
812 s = wxT("Connecting user does not have sufficient privileges to access this table.\n");
813 }
814
815 if (!s.empty())
816 {
817 wxString p;
818
819 if (!tablePath.empty())
820 p.Printf(wxT("Error opening '%s/%s'.\n"),tablePath.c_str(),tableName.c_str());
821 else
822 p.Printf(wxT("Error opening '%s'.\n"), tableName.c_str());
823
824 p += s;
825 pDb->LogError(p.GetData());
826
827 return false;
828 }
829
830 // Bind the member variables for field exchange between
831 // the wxDbTable object and the ODBC record.
832 if (!queryOnly)
833 {
834 if (!bindInsertParams()) // Inserts
835 return false;
836
837 if (!bindUpdateParams()) // Updates
838 return false;
839 }
840
841 if (!bindCols(*hstmtDefault)) // Selects
842 return false;
843
844 if (!bindCols(hstmtInternal)) // Internal use only
845 return false;
846
847 /*
848 * Do NOT bind the hstmtCount cursor!!!
849 */
850
851 // Build an insert statement using parameter markers
852 if (!queryOnly && m_numCols > 0)
853 {
854 bool needComma = false;
855 sqlStmt.Printf(wxT("INSERT INTO %s ("),
856 pDb->SQLTableName(tableName.c_str()).c_str());
857 for (i = 0; i < m_numCols; i++)
858 {
859 if (! colDefs[i].InsertAllowed)
860 continue;
861 if (needComma)
862 sqlStmt += wxT(",");
863 sqlStmt += pDb->SQLColumnName(colDefs[i].ColName);
864 needComma = true;
865 }
866 needComma = false;
867 sqlStmt += wxT(") VALUES (");
868
869 int insertableCount = 0;
870
871 for (i = 0; i < m_numCols; i++)
872 {
873 if (! colDefs[i].InsertAllowed)
874 continue;
875 if (needComma)
876 sqlStmt += wxT(",");
877 sqlStmt += wxT("?");
878 needComma = true;
879 insertableCount++;
880 }
881 sqlStmt += wxT(")");
882
883 // Prepare the insert statement for execution
884 if (insertableCount)
885 {
886 if (SQLPrepare(hstmtInsert, (SQLTCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
887 return(pDb->DispAllErrors(henv, hdbc, hstmtInsert));
888 }
889 else
890 insertable = false;
891 }
892
893 // Completed successfully
894 return true;
895
896 } // wxDbTable::Open()
897
898
899 /********** wxDbTable::Query() **********/
900 bool wxDbTable::Query(bool forUpdate, bool distinct)
901 {
902
903 return(query(DB_SELECT_WHERE, forUpdate, distinct));
904
905 } // wxDbTable::Query()
906
907
908 /********** wxDbTable::QueryBySqlStmt() **********/
909 bool wxDbTable::QueryBySqlStmt(const wxString &pSqlStmt)
910 {
911 pDb->WriteSqlLog(pSqlStmt);
912
913 return(query(DB_SELECT_STATEMENT, false, false, pSqlStmt));
914
915 } // wxDbTable::QueryBySqlStmt()
916
917
918 /********** wxDbTable::QueryMatching() **********/
919 bool wxDbTable::QueryMatching(bool forUpdate, bool distinct)
920 {
921
922 return(query(DB_SELECT_MATCHING, forUpdate, distinct));
923
924 } // wxDbTable::QueryMatching()
925
926
927 /********** wxDbTable::QueryOnKeyFields() **********/
928 bool wxDbTable::QueryOnKeyFields(bool forUpdate, bool distinct)
929 {
930
931 return(query(DB_SELECT_KEYFIELDS, forUpdate, distinct));
932
933 } // wxDbTable::QueryOnKeyFields()
934
935
936 /********** wxDbTable::GetPrev() **********/
937 bool wxDbTable::GetPrev(void)
938 {
939 if (pDb->FwdOnlyCursors())
940 {
941 wxFAIL_MSG(wxT("GetPrev()::Backward scrolling cursors are not enabled for this instance of wxDbTable"));
942 return false;
943 }
944 else
945 return(getRec(SQL_FETCH_PRIOR));
946
947 } // wxDbTable::GetPrev()
948
949
950 /********** wxDbTable::operator-- **********/
951 bool wxDbTable::operator--(int)
952 {
953 if (pDb->FwdOnlyCursors())
954 {
955 wxFAIL_MSG(wxT("operator--:Backward scrolling cursors are not enabled for this instance of wxDbTable"));
956 return false;
957 }
958 else
959 return(getRec(SQL_FETCH_PRIOR));
960
961 } // wxDbTable::operator--
962
963
964 /********** wxDbTable::GetFirst() **********/
965 bool wxDbTable::GetFirst(void)
966 {
967 if (pDb->FwdOnlyCursors())
968 {
969 wxFAIL_MSG(wxT("GetFirst():Backward scrolling cursors are not enabled for this instance of wxDbTable"));
970 return false;
971 }
972 else
973 return(getRec(SQL_FETCH_FIRST));
974
975 } // wxDbTable::GetFirst()
976
977
978 /********** wxDbTable::GetLast() **********/
979 bool wxDbTable::GetLast(void)
980 {
981 if (pDb->FwdOnlyCursors())
982 {
983 wxFAIL_MSG(wxT("GetLast()::Backward scrolling cursors are not enabled for this instance of wxDbTable"));
984 return false;
985 }
986 else
987 return(getRec(SQL_FETCH_LAST));
988
989 } // wxDbTable::GetLast()
990
991
992 /********** wxDbTable::BuildDeleteStmt() **********/
993 void wxDbTable::BuildDeleteStmt(wxString &pSqlStmt, int typeOfDel, const wxString &pWhereClause)
994 {
995 wxASSERT(!queryOnly);
996 if (queryOnly)
997 return;
998
999 wxString whereClause;
1000
1001 whereClause.Empty();
1002
1003 // Handle the case of DeleteWhere() and the where clause is blank. It should
1004 // delete all records from the database in this case.
1005 if (typeOfDel == DB_DEL_WHERE && (pWhereClause.length() == 0))
1006 {
1007 pSqlStmt.Printf(wxT("DELETE FROM %s"),
1008 pDb->SQLTableName(tableName.c_str()).c_str());
1009 return;
1010 }
1011
1012 pSqlStmt.Printf(wxT("DELETE FROM %s WHERE "),
1013 pDb->SQLTableName(tableName.c_str()).c_str());
1014
1015 // Append the WHERE clause to the SQL DELETE statement
1016 switch(typeOfDel)
1017 {
1018 case DB_DEL_KEYFIELDS:
1019 // If the datasource supports the ROWID column, build
1020 // the where on ROWID for efficiency purposes.
1021 // e.g. DELETE FROM PARTS WHERE ROWID = '111.222.333'
1022 if (CanUpdateByROWID())
1023 {
1024 SQLLEN cb;
1025 wxChar rowid[wxDB_ROWID_LEN+1];
1026
1027 // Get the ROWID value. If not successful retreiving the ROWID,
1028 // simply fall down through the code and build the WHERE clause
1029 // based on the key fields.
1030 if (SQLGetData(hstmt, (UWORD)(m_numCols+1), SQL_C_WXCHAR, (UCHAR*) rowid, sizeof(rowid), &cb) == SQL_SUCCESS)
1031 {
1032 pSqlStmt += wxT("ROWID = '");
1033 pSqlStmt += rowid;
1034 pSqlStmt += wxT("'");
1035 break;
1036 }
1037 }
1038 // Unable to delete by ROWID, so build a WHERE
1039 // clause based on the keyfields.
1040 BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS);
1041 pSqlStmt += whereClause;
1042 break;
1043 case DB_DEL_WHERE:
1044 pSqlStmt += pWhereClause;
1045 break;
1046 case DB_DEL_MATCHING:
1047 BuildWhereClause(whereClause, DB_WHERE_MATCHING);
1048 pSqlStmt += whereClause;
1049 break;
1050 }
1051
1052 } // BuildDeleteStmt()
1053
1054
1055 /***** DEPRECATED: use wxDbTable::BuildDeleteStmt(wxString &....) form *****/
1056 void wxDbTable::BuildDeleteStmt(wxChar *pSqlStmt, int typeOfDel, const wxString &pWhereClause)
1057 {
1058 wxString tempSqlStmt;
1059 BuildDeleteStmt(tempSqlStmt, typeOfDel, pWhereClause);
1060 wxStrcpy(pSqlStmt, tempSqlStmt);
1061 } // wxDbTable::BuildDeleteStmt()
1062
1063
1064 /********** wxDbTable::BuildSelectStmt() **********/
1065 void wxDbTable::BuildSelectStmt(wxString &pSqlStmt, int typeOfSelect, bool distinct)
1066 {
1067 wxString whereClause;
1068 whereClause.Empty();
1069
1070 // Build a select statement to query the database
1071 pSqlStmt = wxT("SELECT ");
1072
1073 // SELECT DISTINCT values only?
1074 if (distinct)
1075 pSqlStmt += wxT("DISTINCT ");
1076
1077 // Was a FROM clause specified to join tables to the base table?
1078 // Available for ::Query() only!!!
1079 bool appendFromClause = false;
1080 #if wxODBC_BACKWARD_COMPATABILITY
1081 if (typeOfSelect == DB_SELECT_WHERE && from && wxStrlen(from))
1082 appendFromClause = true;
1083 #else
1084 if (typeOfSelect == DB_SELECT_WHERE && from.length())
1085 appendFromClause = true;
1086 #endif
1087
1088 // Add the column list
1089 int i;
1090 wxString tStr;
1091 for (i = 0; i < m_numCols; i++)
1092 {
1093 tStr = colDefs[i].ColName;
1094 // If joining tables, the base table column names must be qualified to avoid ambiguity
1095 if ((appendFromClause || pDb->Dbms() == dbmsACCESS) && tStr.Find(wxT('.')) == wxNOT_FOUND)
1096 {
1097 pSqlStmt += pDb->SQLTableName(queryTableName.c_str());
1098 pSqlStmt += wxT(".");
1099 }
1100 pSqlStmt += pDb->SQLColumnName(colDefs[i].ColName);
1101 if (i + 1 < m_numCols)
1102 pSqlStmt += wxT(",");
1103 }
1104
1105 // If the datasource supports ROWID, get this column as well. Exception: Don't retrieve
1106 // the ROWID if querying distinct records. The rowid will always be unique.
1107 if (!distinct && CanUpdateByROWID())
1108 {
1109 // If joining tables, the base table column names must be qualified to avoid ambiguity
1110 if (appendFromClause || pDb->Dbms() == dbmsACCESS)
1111 {
1112 pSqlStmt += wxT(",");
1113 pSqlStmt += pDb->SQLTableName(queryTableName);
1114 pSqlStmt += wxT(".ROWID");
1115 }
1116 else
1117 pSqlStmt += wxT(",ROWID");
1118 }
1119
1120 // Append the FROM tablename portion
1121 pSqlStmt += wxT(" FROM ");
1122 pSqlStmt += pDb->SQLTableName(queryTableName);
1123 // pSqlStmt += queryTableName;
1124
1125 // Sybase uses the HOLDLOCK keyword to lock a record during query.
1126 // The HOLDLOCK keyword follows the table name in the from clause.
1127 // Each table in the from clause must specify HOLDLOCK or
1128 // NOHOLDLOCK (the default). Note: The "FOR UPDATE" clause
1129 // is parsed but ignored in SYBASE Transact-SQL.
1130 if (selectForUpdate && (pDb->Dbms() == dbmsSYBASE_ASA || pDb->Dbms() == dbmsSYBASE_ASE))
1131 pSqlStmt += wxT(" HOLDLOCK");
1132
1133 if (appendFromClause)
1134 pSqlStmt += from;
1135
1136 // Append the WHERE clause. Either append the where clause for the class
1137 // or build a where clause. The typeOfSelect determines this.
1138 switch(typeOfSelect)
1139 {
1140 case DB_SELECT_WHERE:
1141 #if wxODBC_BACKWARD_COMPATABILITY
1142 if (where && wxStrlen(where)) // May not want a where clause!!!
1143 #else
1144 if (where.length()) // May not want a where clause!!!
1145 #endif
1146 {
1147 pSqlStmt += wxT(" WHERE ");
1148 pSqlStmt += where;
1149 }
1150 break;
1151 case DB_SELECT_KEYFIELDS:
1152 BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS);
1153 if (whereClause.length())
1154 {
1155 pSqlStmt += wxT(" WHERE ");
1156 pSqlStmt += whereClause;
1157 }
1158 break;
1159 case DB_SELECT_MATCHING:
1160 BuildWhereClause(whereClause, DB_WHERE_MATCHING);
1161 if (whereClause.length())
1162 {
1163 pSqlStmt += wxT(" WHERE ");
1164 pSqlStmt += whereClause;
1165 }
1166 break;
1167 }
1168
1169 // Append the ORDER BY clause
1170 #if wxODBC_BACKWARD_COMPATABILITY
1171 if (orderBy && wxStrlen(orderBy))
1172 #else
1173 if (orderBy.length())
1174 #endif
1175 {
1176 pSqlStmt += wxT(" ORDER BY ");
1177 pSqlStmt += orderBy;
1178 }
1179
1180 // SELECT FOR UPDATE if told to do so and the datasource is capable. Sybase
1181 // parses the FOR UPDATE clause but ignores it. See the comment above on the
1182 // HOLDLOCK for Sybase.
1183 if (selectForUpdate && CanSelectForUpdate())
1184 pSqlStmt += wxT(" FOR UPDATE");
1185
1186 } // wxDbTable::BuildSelectStmt()
1187
1188
1189 /***** DEPRECATED: use wxDbTable::BuildSelectStmt(wxString &....) form *****/
1190 void wxDbTable::BuildSelectStmt(wxChar *pSqlStmt, int typeOfSelect, bool distinct)
1191 {
1192 wxString tempSqlStmt;
1193 BuildSelectStmt(tempSqlStmt, typeOfSelect, distinct);
1194 wxStrcpy(pSqlStmt, tempSqlStmt);
1195 } // wxDbTable::BuildSelectStmt()
1196
1197
1198 /********** wxDbTable::BuildUpdateStmt() **********/
1199 void wxDbTable::BuildUpdateStmt(wxString &pSqlStmt, int typeOfUpdate, const wxString &pWhereClause)
1200 {
1201 wxASSERT(!queryOnly);
1202 if (queryOnly)
1203 return;
1204
1205 wxString whereClause;
1206 whereClause.Empty();
1207
1208 bool firstColumn = true;
1209
1210 pSqlStmt.Printf(wxT("UPDATE %s SET "),
1211 pDb->SQLTableName(tableName.c_str()).c_str());
1212
1213 // Append a list of columns to be updated
1214 int i;
1215 for (i = 0; i < m_numCols; i++)
1216 {
1217 // Only append Updateable columns
1218 if (colDefs[i].Updateable)
1219 {
1220 if (!firstColumn)
1221 pSqlStmt += wxT(",");
1222 else
1223 firstColumn = false;
1224
1225 pSqlStmt += pDb->SQLColumnName(colDefs[i].ColName);
1226 // pSqlStmt += colDefs[i].ColName;
1227 pSqlStmt += wxT(" = ?");
1228 }
1229 }
1230
1231 // Append the WHERE clause to the SQL UPDATE statement
1232 pSqlStmt += wxT(" WHERE ");
1233 switch(typeOfUpdate)
1234 {
1235 case DB_UPD_KEYFIELDS:
1236 // If the datasource supports the ROWID column, build
1237 // the where on ROWID for efficiency purposes.
1238 // e.g. UPDATE PARTS SET Col1 = ?, Col2 = ? WHERE ROWID = '111.222.333'
1239 if (CanUpdateByROWID())
1240 {
1241 SQLLEN cb;
1242 wxChar rowid[wxDB_ROWID_LEN+1];
1243
1244 // Get the ROWID value. If not successful retreiving the ROWID,
1245 // simply fall down through the code and build the WHERE clause
1246 // based on the key fields.
1247 if (SQLGetData(hstmt, (UWORD)(m_numCols+1), SQL_C_WXCHAR, (UCHAR*) rowid, sizeof(rowid), &cb) == SQL_SUCCESS)
1248 {
1249 pSqlStmt += wxT("ROWID = '");
1250 pSqlStmt += rowid;
1251 pSqlStmt += wxT("'");
1252 break;
1253 }
1254 }
1255 // Unable to delete by ROWID, so build a WHERE
1256 // clause based on the keyfields.
1257 BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS);
1258 pSqlStmt += whereClause;
1259 break;
1260 case DB_UPD_WHERE:
1261 pSqlStmt += pWhereClause;
1262 break;
1263 }
1264 } // BuildUpdateStmt()
1265
1266
1267 /***** DEPRECATED: use wxDbTable::BuildUpdateStmt(wxString &....) form *****/
1268 void wxDbTable::BuildUpdateStmt(wxChar *pSqlStmt, int typeOfUpdate, const wxString &pWhereClause)
1269 {
1270 wxString tempSqlStmt;
1271 BuildUpdateStmt(tempSqlStmt, typeOfUpdate, pWhereClause);
1272 wxStrcpy(pSqlStmt, tempSqlStmt);
1273 } // BuildUpdateStmt()
1274
1275
1276 /********** wxDbTable::BuildWhereClause() **********/
1277 void wxDbTable::BuildWhereClause(wxString &pWhereClause, int typeOfWhere,
1278 const wxString &qualTableName, bool useLikeComparison)
1279 /*
1280 * Note: BuildWhereClause() currently ignores timestamp columns.
1281 * They are not included as part of the where clause.
1282 */
1283 {
1284 bool moreThanOneColumn = false;
1285 wxString colValue;
1286
1287 // Loop through the columns building a where clause as you go
1288 int colNumber;
1289 for (colNumber = 0; colNumber < m_numCols; colNumber++)
1290 {
1291 // Determine if this column should be included in the WHERE clause
1292 if ((typeOfWhere == DB_WHERE_KEYFIELDS && colDefs[colNumber].KeyField) ||
1293 (typeOfWhere == DB_WHERE_MATCHING && (!IsColNull((UWORD)colNumber))))
1294 {
1295 // Skip over timestamp columns
1296 if (colDefs[colNumber].SqlCtype == SQL_C_TIMESTAMP)
1297 continue;
1298 // If there is more than 1 column, join them with the keyword "AND"
1299 if (moreThanOneColumn)
1300 pWhereClause += wxT(" AND ");
1301 else
1302 moreThanOneColumn = true;
1303
1304 // Concatenate where phrase for the column
1305 wxString tStr = colDefs[colNumber].ColName;
1306
1307 if (qualTableName.length() && tStr.Find(wxT('.')) == wxNOT_FOUND)
1308 {
1309 pWhereClause += pDb->SQLTableName(qualTableName);
1310 pWhereClause += wxT(".");
1311 }
1312 pWhereClause += pDb->SQLColumnName(colDefs[colNumber].ColName);
1313
1314 if (useLikeComparison && (colDefs[colNumber].SqlCtype == SQL_C_WXCHAR))
1315 pWhereClause += wxT(" LIKE ");
1316 else
1317 pWhereClause += wxT(" = ");
1318
1319 switch(colDefs[colNumber].SqlCtype)
1320 {
1321 case SQL_C_CHAR:
1322 #ifdef SQL_C_WCHAR
1323 case SQL_C_WCHAR:
1324 #endif
1325 //case SQL_C_WXCHAR: SQL_C_WXCHAR is covered by either SQL_C_CHAR or SQL_C_WCHAR
1326 colValue.Printf(wxT("'%s'"), GetDb()->EscapeSqlChars((wxChar *)colDefs[colNumber].PtrDataObj).c_str());
1327 break;
1328 case SQL_C_SHORT:
1329 case SQL_C_SSHORT:
1330 colValue.Printf(wxT("%hi"), *((SWORD *) colDefs[colNumber].PtrDataObj));
1331 break;
1332 case SQL_C_USHORT:
1333 colValue.Printf(wxT("%hu"), *((UWORD *) colDefs[colNumber].PtrDataObj));
1334 break;
1335 case SQL_C_LONG:
1336 case SQL_C_SLONG:
1337 colValue.Printf(wxT("%li"), *((SDWORD *) colDefs[colNumber].PtrDataObj));
1338 break;
1339 case SQL_C_ULONG:
1340 colValue.Printf(wxT("%lu"), *((UDWORD *) colDefs[colNumber].PtrDataObj));
1341 break;
1342 case SQL_C_FLOAT:
1343 colValue.Printf(wxT("%.6f"), *((SFLOAT *) colDefs[colNumber].PtrDataObj));
1344 break;
1345 case SQL_C_DOUBLE:
1346 colValue.Printf(wxT("%.6f"), *((SDOUBLE *) colDefs[colNumber].PtrDataObj));
1347 break;
1348 default:
1349 {
1350 wxString strMsg;
1351 strMsg.Printf(wxT("wxDbTable::bindParams(): Unknown column type for colDefs %d colName %s"),
1352 colNumber,colDefs[colNumber].ColName);
1353 wxFAIL_MSG(strMsg.c_str());
1354 }
1355 break;
1356 }
1357 pWhereClause += colValue;
1358 }
1359 }
1360 } // wxDbTable::BuildWhereClause()
1361
1362
1363 /***** DEPRECATED: use wxDbTable::BuildWhereClause(wxString &....) form *****/
1364 void wxDbTable::BuildWhereClause(wxChar *pWhereClause, int typeOfWhere,
1365 const wxString &qualTableName, bool useLikeComparison)
1366 {
1367 wxString tempSqlStmt;
1368 BuildWhereClause(tempSqlStmt, typeOfWhere, qualTableName, useLikeComparison);
1369 wxStrcpy(pWhereClause, tempSqlStmt);
1370 } // wxDbTable::BuildWhereClause()
1371
1372
1373 /********** wxDbTable::GetRowNum() **********/
1374 UWORD wxDbTable::GetRowNum(void)
1375 {
1376 UDWORD rowNum;
1377
1378 if (SQLGetStmtOption(hstmt, SQL_ROW_NUMBER, (UCHAR*) &rowNum) != SQL_SUCCESS)
1379 {
1380 pDb->DispAllErrors(henv, hdbc, hstmt);
1381 return(0);
1382 }
1383
1384 // Completed successfully
1385 return((UWORD) rowNum);
1386
1387 } // wxDbTable::GetRowNum()
1388
1389
1390 /********** wxDbTable::CloseCursor() **********/
1391 bool wxDbTable::CloseCursor(HSTMT cursor)
1392 {
1393 if (SQLFreeStmt(cursor, SQL_CLOSE) != SQL_SUCCESS)
1394 return(pDb->DispAllErrors(henv, hdbc, cursor));
1395
1396 // Completed successfully
1397 return true;
1398
1399 } // wxDbTable::CloseCursor()
1400
1401
1402 /********** wxDbTable::CreateTable() **********/
1403 bool wxDbTable::CreateTable(bool attemptDrop)
1404 {
1405 if (!pDb)
1406 return false;
1407
1408 int i, j;
1409 wxString sqlStmt;
1410
1411 #ifdef DBDEBUG_CONSOLE
1412 cout << wxT("Creating Table ") << tableName << wxT("...") << endl;
1413 #endif
1414
1415 // Drop table first
1416 if (attemptDrop && !DropTable())
1417 return false;
1418
1419 // Create the table
1420 #ifdef DBDEBUG_CONSOLE
1421 for (i = 0; i < m_numCols; i++)
1422 {
1423 // Exclude derived columns since they are NOT part of the base table
1424 if (colDefs[i].DerivedCol)
1425 continue;
1426 cout << i + 1 << wxT(": ") << colDefs[i].ColName << wxT("; ");
1427 switch(colDefs[i].DbDataType)
1428 {
1429 case DB_DATA_TYPE_VARCHAR:
1430 cout << pDb->GetTypeInfVarchar().TypeName << wxT("(") << (int)(colDefs[i].SzDataObj / sizeof(wxChar)) << wxT(")");
1431 break;
1432 case DB_DATA_TYPE_MEMO:
1433 cout << pDb->GetTypeInfMemo().TypeName;
1434 break;
1435 case DB_DATA_TYPE_INTEGER:
1436 cout << pDb->GetTypeInfInteger().TypeName;
1437 break;
1438 case DB_DATA_TYPE_FLOAT:
1439 cout << pDb->GetTypeInfFloat().TypeName;
1440 break;
1441 case DB_DATA_TYPE_DATE:
1442 cout << pDb->GetTypeInfDate().TypeName;
1443 break;
1444 case DB_DATA_TYPE_BLOB:
1445 cout << pDb->GetTypeInfBlob().TypeName;
1446 break;
1447 }
1448 cout << endl;
1449 }
1450 #endif
1451
1452 // Build a CREATE TABLE string from the colDefs structure.
1453 bool needComma = false;
1454
1455 sqlStmt.Printf(wxT("CREATE TABLE %s ("),
1456 pDb->SQLTableName(tableName.c_str()).c_str());
1457
1458 for (i = 0; i < m_numCols; i++)
1459 {
1460 // Exclude derived columns since they are NOT part of the base table
1461 if (colDefs[i].DerivedCol)
1462 continue;
1463 // Comma Delimiter
1464 if (needComma)
1465 sqlStmt += wxT(",");
1466 // Column Name
1467 sqlStmt += pDb->SQLColumnName(colDefs[i].ColName);
1468 // sqlStmt += colDefs[i].ColName;
1469 sqlStmt += wxT(" ");
1470 // Column Type
1471 switch(colDefs[i].DbDataType)
1472 {
1473 case DB_DATA_TYPE_VARCHAR:
1474 sqlStmt += pDb->GetTypeInfVarchar().TypeName;
1475 break;
1476 case DB_DATA_TYPE_MEMO:
1477 sqlStmt += pDb->GetTypeInfMemo().TypeName;
1478 break;
1479 case DB_DATA_TYPE_INTEGER:
1480 sqlStmt += pDb->GetTypeInfInteger().TypeName;
1481 break;
1482 case DB_DATA_TYPE_FLOAT:
1483 sqlStmt += pDb->GetTypeInfFloat().TypeName;
1484 break;
1485 case DB_DATA_TYPE_DATE:
1486 sqlStmt += pDb->GetTypeInfDate().TypeName;
1487 break;
1488 case DB_DATA_TYPE_BLOB:
1489 sqlStmt += pDb->GetTypeInfBlob().TypeName;
1490 break;
1491 }
1492 // For varchars, append the size of the string
1493 if (colDefs[i].DbDataType == DB_DATA_TYPE_VARCHAR &&
1494 (pDb->Dbms() != dbmsMY_SQL || pDb->GetTypeInfVarchar().TypeName != _T("text")))// ||
1495 // colDefs[i].DbDataType == DB_DATA_TYPE_BLOB)
1496 {
1497 wxString s;
1498 s.Printf(wxT("(%d)"), (int)(colDefs[i].SzDataObj / sizeof(wxChar)));
1499 sqlStmt += s;
1500 }
1501
1502 if (pDb->Dbms() == dbmsDB2 ||
1503 pDb->Dbms() == dbmsMY_SQL ||
1504 pDb->Dbms() == dbmsSYBASE_ASE ||
1505 pDb->Dbms() == dbmsINTERBASE ||
1506 pDb->Dbms() == dbmsFIREBIRD ||
1507 pDb->Dbms() == dbmsMS_SQL_SERVER)
1508 {
1509 if (colDefs[i].KeyField)
1510 {
1511 sqlStmt += wxT(" NOT NULL");
1512 }
1513 }
1514
1515 needComma = true;
1516 }
1517 // If there is a primary key defined, include it in the create statement
1518 for (i = j = 0; i < m_numCols; i++)
1519 {
1520 if (colDefs[i].KeyField)
1521 {
1522 j++;
1523 break;
1524 }
1525 }
1526 if ( j && (pDb->Dbms() != dbmsDBASE)
1527 && (pDb->Dbms() != dbmsXBASE_SEQUITER) ) // Found a keyfield
1528 {
1529 switch (pDb->Dbms())
1530 {
1531 case dbmsACCESS:
1532 case dbmsINFORMIX:
1533 case dbmsSYBASE_ASA:
1534 case dbmsSYBASE_ASE:
1535 case dbmsMY_SQL:
1536 case dbmsFIREBIRD:
1537 {
1538 // MySQL goes out on this one. We also declare the relevant key NON NULL above
1539 sqlStmt += wxT(",PRIMARY KEY (");
1540 break;
1541 }
1542 default:
1543 {
1544 sqlStmt += wxT(",CONSTRAINT ");
1545 // DB2 is limited to 18 characters for index names
1546 if (pDb->Dbms() == dbmsDB2)
1547 {
1548 wxASSERT_MSG((tableName && wxStrlen(tableName) <= 13), wxT("DB2 table/index names must be no longer than 13 characters in length.\n\nTruncating table name to 13 characters."));
1549 sqlStmt += pDb->SQLTableName(tableName.substr(0, 13).c_str());
1550 // sqlStmt += tableName.substr(0, 13);
1551 }
1552 else
1553 sqlStmt += pDb->SQLTableName(tableName.c_str());
1554 // sqlStmt += tableName;
1555
1556 sqlStmt += wxT("_PIDX PRIMARY KEY (");
1557 break;
1558 }
1559 }
1560
1561 // List column name(s) of column(s) comprising the primary key
1562 for (i = j = 0; i < m_numCols; i++)
1563 {
1564 if (colDefs[i].KeyField)
1565 {
1566 if (j++) // Multi part key, comma separate names
1567 sqlStmt += wxT(",");
1568 sqlStmt += pDb->SQLColumnName(colDefs[i].ColName);
1569
1570 if (pDb->Dbms() == dbmsMY_SQL &&
1571 colDefs[i].DbDataType == DB_DATA_TYPE_VARCHAR)
1572 {
1573 wxString s;
1574 s.Printf(wxT("(%d)"), (int)(colDefs[i].SzDataObj / sizeof(wxChar)));
1575 sqlStmt += s;
1576 }
1577 }
1578 }
1579 sqlStmt += wxT(")");
1580
1581 if (pDb->Dbms() == dbmsINFORMIX ||
1582 pDb->Dbms() == dbmsSYBASE_ASA ||
1583 pDb->Dbms() == dbmsSYBASE_ASE)
1584 {
1585 sqlStmt += wxT(" CONSTRAINT ");
1586 sqlStmt += pDb->SQLTableName(tableName);
1587 // sqlStmt += tableName;
1588 sqlStmt += wxT("_PIDX");
1589 }
1590 }
1591 // Append the closing parentheses for the create table statement
1592 sqlStmt += wxT(")");
1593
1594 pDb->WriteSqlLog(sqlStmt);
1595
1596 #ifdef DBDEBUG_CONSOLE
1597 cout << endl << sqlStmt.c_str() << endl;
1598 #endif
1599
1600 // Execute the CREATE TABLE statement
1601 RETCODE retcode = SQLExecDirect(hstmt, (SQLTCHAR FAR *) sqlStmt.c_str(), SQL_NTS);
1602 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
1603 {
1604 pDb->DispAllErrors(henv, hdbc, hstmt);
1605 pDb->RollbackTrans();
1606 CloseCursor(hstmt);
1607 return false;
1608 }
1609
1610 // Commit the transaction and close the cursor
1611 if (!pDb->CommitTrans())
1612 return false;
1613 if (!CloseCursor(hstmt))
1614 return false;
1615
1616 // Database table created successfully
1617 return true;
1618
1619 } // wxDbTable::CreateTable()
1620
1621
1622 /********** wxDbTable::DropTable() **********/
1623 bool wxDbTable::DropTable()
1624 {
1625 // NOTE: This function returns true if the Table does not exist, but
1626 // only for identified databases. Code will need to be added
1627 // below for any other databases when those databases are defined
1628 // to handle this situation consistently
1629
1630 wxString sqlStmt;
1631
1632 sqlStmt.Printf(wxT("DROP TABLE %s"),
1633 pDb->SQLTableName(tableName.c_str()).c_str());
1634
1635 pDb->WriteSqlLog(sqlStmt);
1636
1637 #ifdef DBDEBUG_CONSOLE
1638 cout << endl << sqlStmt.c_str() << endl;
1639 #endif
1640
1641 RETCODE retcode = SQLExecDirect(hstmt, (SQLTCHAR FAR *) sqlStmt.c_str(), SQL_NTS);
1642 if (retcode != SQL_SUCCESS)
1643 {
1644 // Check for "Base table not found" error and ignore
1645 pDb->GetNextError(henv, hdbc, hstmt);
1646 if (wxStrcmp(pDb->sqlState, wxT("S0002")) /*&&
1647 wxStrcmp(pDb->sqlState, wxT("S1000"))*/) // "Base table not found"
1648 {
1649 // Check for product specific error codes
1650 if (!((pDb->Dbms() == dbmsSYBASE_ASA && !wxStrcmp(pDb->sqlState,wxT("42000"))) || // 5.x (and lower?)
1651 (pDb->Dbms() == dbmsSYBASE_ASE && !wxStrcmp(pDb->sqlState,wxT("37000"))) ||
1652 (pDb->Dbms() == dbmsPERVASIVE_SQL && !wxStrcmp(pDb->sqlState,wxT("S1000"))) || // Returns an S1000 then an S0002
1653 (pDb->Dbms() == dbmsPOSTGRES && !wxStrcmp(pDb->sqlState,wxT("08S01")))))
1654 {
1655 pDb->DispNextError();
1656 pDb->DispAllErrors(henv, hdbc, hstmt);
1657 pDb->RollbackTrans();
1658 // CloseCursor(hstmt);
1659 return false;
1660 }
1661 }
1662 }
1663
1664 // Commit the transaction and close the cursor
1665 if (! pDb->CommitTrans())
1666 return false;
1667 if (! CloseCursor(hstmt))
1668 return false;
1669
1670 return true;
1671 } // wxDbTable::DropTable()
1672
1673
1674 /********** wxDbTable::CreateIndex() **********/
1675 bool wxDbTable::CreateIndex(const wxString &indexName, bool unique, UWORD numIndexColumns,
1676 wxDbIdxDef *pIndexDefs, bool attemptDrop)
1677 {
1678 wxString sqlStmt;
1679
1680 // Drop the index first
1681 if (attemptDrop && !DropIndex(indexName))
1682 return false;
1683
1684 // MySQL (and possibly Sybase ASE?? - gt) require that any columns which are used as portions
1685 // of an index have the columns defined as "NOT NULL". During initial table creation though,
1686 // it may not be known which columns are necessarily going to be part of an index (e.g. the
1687 // table was created, then months later you determine that an additional index while
1688 // give better performance, so you want to add an index).
1689 //
1690 // The following block of code will modify the column definition to make the column be
1691 // defined with the "NOT NULL" qualifier.
1692 if (pDb->Dbms() == dbmsMY_SQL)
1693 {
1694 wxString sqlStmt;
1695 int i;
1696 bool ok = true;
1697 for (i = 0; i < numIndexColumns && ok; i++)
1698 {
1699 int j = 0;
1700 bool found = false;
1701 // Find the column definition that has the ColName that matches the
1702 // index column name. We need to do this to get the DB_DATA_TYPE of
1703 // the index column, as MySQL's syntax for the ALTER column requires
1704 // this information
1705 while (!found && (j < this->m_numCols))
1706 {
1707 if (wxStrcmp(colDefs[j].ColName,pIndexDefs[i].ColName) == 0)
1708 found = true;
1709 if (!found)
1710 j++;
1711 }
1712
1713 if (found)
1714 {
1715 ok = pDb->ModifyColumn(tableName, pIndexDefs[i].ColName,
1716 colDefs[j].DbDataType, (int)(colDefs[j].SzDataObj / sizeof(wxChar)),
1717 wxT("NOT NULL"));
1718
1719 if (!ok)
1720 {
1721 #if 0
1722 // retcode is not used
1723 wxODBC_ERRORS retcode;
1724 // Oracle returns a DB_ERR_GENERAL_ERROR if the column is already
1725 // defined to be NOT NULL, but reportedly MySQL doesn't mind.
1726 // This line is just here for debug checking of the value
1727 retcode = (wxODBC_ERRORS)pDb->DB_STATUS;
1728 #endif
1729 }
1730 }
1731 else
1732 ok = false;
1733 }
1734 if (ok)
1735 pDb->CommitTrans();
1736 else
1737 {
1738 pDb->RollbackTrans();
1739 return false;
1740 }
1741 }
1742
1743 // Build a CREATE INDEX statement
1744 sqlStmt = wxT("CREATE ");
1745 if (unique)
1746 sqlStmt += wxT("UNIQUE ");
1747
1748 sqlStmt += wxT("INDEX ");
1749 sqlStmt += pDb->SQLTableName(indexName);
1750 sqlStmt += wxT(" ON ");
1751
1752 sqlStmt += pDb->SQLTableName(tableName);
1753 // sqlStmt += tableName;
1754 sqlStmt += wxT(" (");
1755
1756 // Append list of columns making up index
1757 int i;
1758 for (i = 0; i < numIndexColumns; i++)
1759 {
1760 sqlStmt += pDb->SQLColumnName(pIndexDefs[i].ColName);
1761 // sqlStmt += pIndexDefs[i].ColName;
1762
1763 // MySQL requires a key length on VARCHAR keys
1764 if ( pDb->Dbms() == dbmsMY_SQL )
1765 {
1766 // Find the details on this column
1767 int j;
1768 for ( j = 0; j < m_numCols; ++j )
1769 {
1770 if ( wxStrcmp( pIndexDefs[i].ColName, colDefs[j].ColName ) == 0 )
1771 {
1772 break;
1773 }
1774 }
1775 if ( colDefs[j].DbDataType == DB_DATA_TYPE_VARCHAR)
1776 {
1777 wxString s;
1778 s.Printf(wxT("(%d)"), (int)(colDefs[i].SzDataObj / sizeof(wxChar)));
1779 sqlStmt += s;
1780 }
1781 }
1782
1783 // Postgres and SQL Server 7 do not support the ASC/DESC keywords for index columns
1784 if (!((pDb->Dbms() == dbmsMS_SQL_SERVER) && (wxStrncmp(pDb->dbInf.dbmsVer,_T("07"),2)==0)) &&
1785 !(pDb->Dbms() == dbmsFIREBIRD) &&
1786 !(pDb->Dbms() == dbmsPOSTGRES))
1787 {
1788 if (pIndexDefs[i].Ascending)
1789 sqlStmt += wxT(" ASC");
1790 else
1791 sqlStmt += wxT(" DESC");
1792 }
1793 else
1794 wxASSERT_MSG(pIndexDefs[i].Ascending, _T("Datasource does not support DESCending index columns"));
1795
1796 if ((i + 1) < numIndexColumns)
1797 sqlStmt += wxT(",");
1798 }
1799
1800 // Append closing parentheses
1801 sqlStmt += wxT(")");
1802
1803 pDb->WriteSqlLog(sqlStmt);
1804
1805 #ifdef DBDEBUG_CONSOLE
1806 cout << endl << sqlStmt.c_str() << endl << endl;
1807 #endif
1808
1809 // Execute the CREATE INDEX statement
1810 RETCODE retcode = SQLExecDirect(hstmt, (SQLTCHAR FAR *) sqlStmt.c_str(), SQL_NTS);
1811 if (retcode != SQL_SUCCESS)
1812 {
1813 pDb->DispAllErrors(henv, hdbc, hstmt);
1814 pDb->RollbackTrans();
1815 CloseCursor(hstmt);
1816 return false;
1817 }
1818
1819 // Commit the transaction and close the cursor
1820 if (! pDb->CommitTrans())
1821 return false;
1822 if (! CloseCursor(hstmt))
1823 return false;
1824
1825 // Index Created Successfully
1826 return true;
1827
1828 } // wxDbTable::CreateIndex()
1829
1830
1831 /********** wxDbTable::DropIndex() **********/
1832 bool wxDbTable::DropIndex(const wxString &indexName)
1833 {
1834 // NOTE: This function returns true if the Index does not exist, but
1835 // only for identified databases. Code will need to be added
1836 // below for any other databases when those databases are defined
1837 // to handle this situation consistently
1838
1839 wxString sqlStmt;
1840
1841 if (pDb->Dbms() == dbmsACCESS || pDb->Dbms() == dbmsMY_SQL ||
1842 pDb->Dbms() == dbmsDBASE /*|| Paradox needs this syntax too when we add support*/)
1843 sqlStmt.Printf(wxT("DROP INDEX %s ON %s"),
1844 pDb->SQLTableName(indexName.c_str()).c_str(),
1845 pDb->SQLTableName(tableName.c_str()).c_str());
1846 else if ((pDb->Dbms() == dbmsMS_SQL_SERVER) ||
1847 (pDb->Dbms() == dbmsSYBASE_ASE) ||
1848 (pDb->Dbms() == dbmsXBASE_SEQUITER))
1849 sqlStmt.Printf(wxT("DROP INDEX %s.%s"),
1850 pDb->SQLTableName(tableName.c_str()).c_str(),
1851 pDb->SQLTableName(indexName.c_str()).c_str());
1852 else
1853 sqlStmt.Printf(wxT("DROP INDEX %s"),
1854 pDb->SQLTableName(indexName.c_str()).c_str());
1855
1856 pDb->WriteSqlLog(sqlStmt);
1857
1858 #ifdef DBDEBUG_CONSOLE
1859 cout << endl << sqlStmt.c_str() << endl;
1860 #endif
1861 RETCODE retcode = SQLExecDirect(hstmt, (SQLTCHAR FAR *) sqlStmt.c_str(), SQL_NTS);
1862 if (retcode != SQL_SUCCESS)
1863 {
1864 // Check for "Index not found" error and ignore
1865 pDb->GetNextError(henv, hdbc, hstmt);
1866 if (wxStrcmp(pDb->sqlState,wxT("S0012"))) // "Index not found"
1867 {
1868 // Check for product specific error codes
1869 if (!((pDb->Dbms() == dbmsSYBASE_ASA && !wxStrcmp(pDb->sqlState,wxT("42000"))) || // v5.x (and lower?)
1870 (pDb->Dbms() == dbmsSYBASE_ASE && !wxStrcmp(pDb->sqlState,wxT("37000"))) ||
1871 (pDb->Dbms() == dbmsMS_SQL_SERVER && !wxStrcmp(pDb->sqlState,wxT("S1000"))) ||
1872 (pDb->Dbms() == dbmsINTERBASE && !wxStrcmp(pDb->sqlState,wxT("S1000"))) ||
1873 (pDb->Dbms() == dbmsMAXDB && !wxStrcmp(pDb->sqlState,wxT("S1000"))) ||
1874 (pDb->Dbms() == dbmsFIREBIRD && !wxStrcmp(pDb->sqlState,wxT("HY000"))) ||
1875 (pDb->Dbms() == dbmsSYBASE_ASE && !wxStrcmp(pDb->sqlState,wxT("S0002"))) || // Base table not found
1876 (pDb->Dbms() == dbmsMY_SQL && !wxStrcmp(pDb->sqlState,wxT("42S12"))) || // tested by Christopher Ludwik Marino-Cebulski using v3.23.21beta
1877 (pDb->Dbms() == dbmsPOSTGRES && !wxStrcmp(pDb->sqlState,wxT("08S01")))
1878 ))
1879 {
1880 pDb->DispNextError();
1881 pDb->DispAllErrors(henv, hdbc, hstmt);
1882 pDb->RollbackTrans();
1883 CloseCursor(hstmt);
1884 return false;
1885 }
1886 }
1887 }
1888
1889 // Commit the transaction and close the cursor
1890 if (! pDb->CommitTrans())
1891 return false;
1892 if (! CloseCursor(hstmt))
1893 return false;
1894
1895 return true;
1896 } // wxDbTable::DropIndex()
1897
1898
1899 /********** wxDbTable::SetOrderByColNums() **********/
1900 bool wxDbTable::SetOrderByColNums(UWORD first, ... )
1901 {
1902 int colNumber = first; // using 'int' to be able to look for wxDB_NO_MORE_COLUN_NUMBERS
1903 va_list argptr;
1904
1905 bool abort = false;
1906 wxString tempStr;
1907
1908 va_start(argptr, first); /* Initialize variable arguments. */
1909 while (!abort && (colNumber != wxDB_NO_MORE_COLUMN_NUMBERS))
1910 {
1911 // Make sure the passed in column number
1912 // is within the valid range of columns
1913 //
1914 // Valid columns are 0 thru m_numCols-1
1915 if (colNumber >= m_numCols || colNumber < 0)
1916 {
1917 abort = true;
1918 continue;
1919 }
1920
1921 if (colNumber != first)
1922 tempStr += wxT(",");
1923
1924 tempStr += colDefs[colNumber].ColName;
1925 colNumber = va_arg (argptr, int);
1926 }
1927 va_end (argptr); /* Reset variable arguments. */
1928
1929 SetOrderByClause(tempStr);
1930
1931 return (!abort);
1932 } // wxDbTable::SetOrderByColNums()
1933
1934
1935 /********** wxDbTable::Insert() **********/
1936 int wxDbTable::Insert(void)
1937 {
1938 wxASSERT(!queryOnly);
1939 if (queryOnly || !insertable)
1940 return(DB_FAILURE);
1941
1942 bindInsertParams();
1943
1944 // Insert the record by executing the already prepared insert statement
1945 RETCODE retcode;
1946 retcode = SQLExecute(hstmtInsert);
1947 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO &&
1948 retcode != SQL_NEED_DATA)
1949 {
1950 // Check to see if integrity constraint was violated
1951 pDb->GetNextError(henv, hdbc, hstmtInsert);
1952 if (! wxStrcmp(pDb->sqlState, wxT("23000"))) // Integrity constraint violated
1953 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL);
1954 else
1955 {
1956 pDb->DispNextError();
1957 pDb->DispAllErrors(henv, hdbc, hstmtInsert);
1958 return(DB_FAILURE);
1959 }
1960 }
1961 if (retcode == SQL_NEED_DATA)
1962 {
1963 PTR pParmID;
1964 retcode = SQLParamData(hstmtInsert, &pParmID);
1965 while (retcode == SQL_NEED_DATA)
1966 {
1967 // Find the parameter
1968 int i;
1969 for (i=0; i < m_numCols; i++)
1970 {
1971 if (colDefs[i].PtrDataObj == pParmID)
1972 {
1973 // We found it. Store the parameter.
1974 retcode = SQLPutData(hstmtInsert, pParmID, colDefs[i].SzDataObj);
1975 if (retcode != SQL_SUCCESS)
1976 {
1977 pDb->DispNextError();
1978 pDb->DispAllErrors(henv, hdbc, hstmtInsert);
1979 return(DB_FAILURE);
1980 }
1981 break;
1982 }
1983 }
1984 retcode = SQLParamData(hstmtInsert, &pParmID);
1985 if (retcode != SQL_SUCCESS &&
1986 retcode != SQL_SUCCESS_WITH_INFO)
1987 {
1988 // record was not inserted
1989 pDb->DispNextError();
1990 pDb->DispAllErrors(henv, hdbc, hstmtInsert);
1991 return(DB_FAILURE);
1992 }
1993 }
1994 }
1995
1996 // Record inserted into the datasource successfully
1997 return(DB_SUCCESS);
1998
1999 } // wxDbTable::Insert()
2000
2001
2002 /********** wxDbTable::Update() **********/
2003 bool wxDbTable::Update(void)
2004 {
2005 wxASSERT(!queryOnly);
2006 if (queryOnly)
2007 return false;
2008
2009 wxString sqlStmt;
2010
2011 // Build the SQL UPDATE statement
2012 BuildUpdateStmt(sqlStmt, DB_UPD_KEYFIELDS);
2013
2014 pDb->WriteSqlLog(sqlStmt);
2015
2016 #ifdef DBDEBUG_CONSOLE
2017 cout << endl << sqlStmt.c_str() << endl << endl;
2018 #endif
2019
2020 // Execute the SQL UPDATE statement
2021 return(execUpdate(sqlStmt));
2022
2023 } // wxDbTable::Update()
2024
2025
2026 /********** wxDbTable::Update(pSqlStmt) **********/
2027 bool wxDbTable::Update(const wxString &pSqlStmt)
2028 {
2029 wxASSERT(!queryOnly);
2030 if (queryOnly)
2031 return false;
2032
2033 pDb->WriteSqlLog(pSqlStmt);
2034
2035 return(execUpdate(pSqlStmt));
2036
2037 } // wxDbTable::Update(pSqlStmt)
2038
2039
2040 /********** wxDbTable::UpdateWhere() **********/
2041 bool wxDbTable::UpdateWhere(const wxString &pWhereClause)
2042 {
2043 wxASSERT(!queryOnly);
2044 if (queryOnly)
2045 return false;
2046
2047 wxString sqlStmt;
2048
2049 // Build the SQL UPDATE statement
2050 BuildUpdateStmt(sqlStmt, DB_UPD_WHERE, pWhereClause);
2051
2052 pDb->WriteSqlLog(sqlStmt);
2053
2054 #ifdef DBDEBUG_CONSOLE
2055 cout << endl << sqlStmt.c_str() << endl << endl;
2056 #endif
2057
2058 // Execute the SQL UPDATE statement
2059 return(execUpdate(sqlStmt));
2060
2061 } // wxDbTable::UpdateWhere()
2062
2063
2064 /********** wxDbTable::Delete() **********/
2065 bool wxDbTable::Delete(void)
2066 {
2067 wxASSERT(!queryOnly);
2068 if (queryOnly)
2069 return false;
2070
2071 wxString sqlStmt;
2072 sqlStmt.Empty();
2073
2074 // Build the SQL DELETE statement
2075 BuildDeleteStmt(sqlStmt, DB_DEL_KEYFIELDS);
2076
2077 pDb->WriteSqlLog(sqlStmt);
2078
2079 // Execute the SQL DELETE statement
2080 return(execDelete(sqlStmt));
2081
2082 } // wxDbTable::Delete()
2083
2084
2085 /********** wxDbTable::DeleteWhere() **********/
2086 bool wxDbTable::DeleteWhere(const wxString &pWhereClause)
2087 {
2088 wxASSERT(!queryOnly);
2089 if (queryOnly)
2090 return false;
2091
2092 wxString sqlStmt;
2093 sqlStmt.Empty();
2094
2095 // Build the SQL DELETE statement
2096 BuildDeleteStmt(sqlStmt, DB_DEL_WHERE, pWhereClause);
2097
2098 pDb->WriteSqlLog(sqlStmt);
2099
2100 // Execute the SQL DELETE statement
2101 return(execDelete(sqlStmt));
2102
2103 } // wxDbTable::DeleteWhere()
2104
2105
2106 /********** wxDbTable::DeleteMatching() **********/
2107 bool wxDbTable::DeleteMatching(void)
2108 {
2109 wxASSERT(!queryOnly);
2110 if (queryOnly)
2111 return false;
2112
2113 wxString sqlStmt;
2114 sqlStmt.Empty();
2115
2116 // Build the SQL DELETE statement
2117 BuildDeleteStmt(sqlStmt, DB_DEL_MATCHING);
2118
2119 pDb->WriteSqlLog(sqlStmt);
2120
2121 // Execute the SQL DELETE statement
2122 return(execDelete(sqlStmt));
2123
2124 } // wxDbTable::DeleteMatching()
2125
2126
2127 /********** wxDbTable::IsColNull() **********/
2128 bool wxDbTable::IsColNull(UWORD colNumber) const
2129 {
2130 /*
2131 This logic is just not right. It would indicate true
2132 if a numeric field were set to a value of 0.
2133
2134 switch(colDefs[colNumber].SqlCtype)
2135 {
2136 case SQL_C_CHAR:
2137 case SQL_C_WCHAR:
2138 //case SQL_C_WXCHAR: SQL_C_WXCHAR is covered by either SQL_C_CHAR or SQL_C_WCHAR
2139 return(((UCHAR FAR *) colDefs[colNumber].PtrDataObj)[0] == 0);
2140 case SQL_C_SSHORT:
2141 return(( *((SWORD *) colDefs[colNumber].PtrDataObj)) == 0);
2142 case SQL_C_USHORT:
2143 return(( *((UWORD*) colDefs[colNumber].PtrDataObj)) == 0);
2144 case SQL_C_SLONG:
2145 return(( *((SDWORD *) colDefs[colNumber].PtrDataObj)) == 0);
2146 case SQL_C_ULONG:
2147 return(( *((UDWORD *) colDefs[colNumber].PtrDataObj)) == 0);
2148 case SQL_C_FLOAT:
2149 return(( *((SFLOAT *) colDefs[colNumber].PtrDataObj)) == 0);
2150 case SQL_C_DOUBLE:
2151 return((*((SDOUBLE *) colDefs[colNumber].PtrDataObj)) == 0);
2152 case SQL_C_TIMESTAMP:
2153 TIMESTAMP_STRUCT *pDt;
2154 pDt = (TIMESTAMP_STRUCT *) colDefs[colNumber].PtrDataObj;
2155 if (pDt->year == 0 && pDt->month == 0 && pDt->day == 0)
2156 return true;
2157 else
2158 return false;
2159 default:
2160 return true;
2161 }
2162 */
2163 return (colDefs[colNumber].Null);
2164 } // wxDbTable::IsColNull()
2165
2166
2167 /********** wxDbTable::CanSelectForUpdate() **********/
2168 bool wxDbTable::CanSelectForUpdate(void)
2169 {
2170 if (queryOnly)
2171 return false;
2172
2173 if (pDb->Dbms() == dbmsMY_SQL)
2174 return false;
2175
2176 if ((pDb->Dbms() == dbmsORACLE) ||
2177 (pDb->dbInf.posStmts & SQL_PS_SELECT_FOR_UPDATE))
2178 return true;
2179 else
2180 return false;
2181
2182 } // wxDbTable::CanSelectForUpdate()
2183
2184
2185 /********** wxDbTable::CanUpdateByROWID() **********/
2186 bool wxDbTable::CanUpdateByROWID(void)
2187 {
2188 /*
2189 * NOTE: Returning false for now until this can be debugged,
2190 * as the ROWID is not getting updated correctly
2191 */
2192 return false;
2193 /*
2194 if (pDb->Dbms() == dbmsORACLE)
2195 return true;
2196 else
2197 return false;
2198 */
2199 } // wxDbTable::CanUpdateByROWID()
2200
2201
2202 /********** wxDbTable::IsCursorClosedOnCommit() **********/
2203 bool wxDbTable::IsCursorClosedOnCommit(void)
2204 {
2205 if (pDb->dbInf.cursorCommitBehavior == SQL_CB_PRESERVE)
2206 return false;
2207 else
2208 return true;
2209
2210 } // wxDbTable::IsCursorClosedOnCommit()
2211
2212
2213
2214 /********** wxDbTable::ClearMemberVar() **********/
2215 void wxDbTable::ClearMemberVar(UWORD colNumber, bool setToNull)
2216 {
2217 wxASSERT(colNumber < m_numCols);
2218
2219 switch(colDefs[colNumber].SqlCtype)
2220 {
2221 case SQL_C_CHAR:
2222 #ifdef SQL_C_WCHAR
2223 case SQL_C_WCHAR:
2224 #endif
2225 //case SQL_C_WXCHAR: SQL_C_WXCHAR is covered by either SQL_C_CHAR or SQL_C_WCHAR
2226 ((UCHAR FAR *) colDefs[colNumber].PtrDataObj)[0] = 0;
2227 break;
2228 case SQL_C_SSHORT:
2229 *((SWORD *) colDefs[colNumber].PtrDataObj) = 0;
2230 break;
2231 case SQL_C_USHORT:
2232 *((UWORD*) colDefs[colNumber].PtrDataObj) = 0;
2233 break;
2234 case SQL_C_LONG:
2235 case SQL_C_SLONG:
2236 *((SDWORD *) colDefs[colNumber].PtrDataObj) = 0;
2237 break;
2238 case SQL_C_ULONG:
2239 *((UDWORD *) colDefs[colNumber].PtrDataObj) = 0;
2240 break;
2241 case SQL_C_FLOAT:
2242 *((SFLOAT *) colDefs[colNumber].PtrDataObj) = 0.0f;
2243 break;
2244 case SQL_C_DOUBLE:
2245 *((SDOUBLE *) colDefs[colNumber].PtrDataObj) = 0.0f;
2246 break;
2247 case SQL_C_TIMESTAMP:
2248 TIMESTAMP_STRUCT *pDt;
2249 pDt = (TIMESTAMP_STRUCT *) colDefs[colNumber].PtrDataObj;
2250 pDt->year = 0;
2251 pDt->month = 0;
2252 pDt->day = 0;
2253 pDt->hour = 0;
2254 pDt->minute = 0;
2255 pDt->second = 0;
2256 pDt->fraction = 0;
2257 break;
2258 case SQL_C_DATE:
2259 DATE_STRUCT *pDtd;
2260 pDtd = (DATE_STRUCT *) colDefs[colNumber].PtrDataObj;
2261 pDtd->year = 0;
2262 pDtd->month = 0;
2263 pDtd->day = 0;
2264 break;
2265 case SQL_C_TIME:
2266 TIME_STRUCT *pDtt;
2267 pDtt = (TIME_STRUCT *) colDefs[colNumber].PtrDataObj;
2268 pDtt->hour = 0;
2269 pDtt->minute = 0;
2270 pDtt->second = 0;
2271 break;
2272 }
2273
2274 if (setToNull)
2275 SetColNull(colNumber);
2276 } // wxDbTable::ClearMemberVar()
2277
2278
2279 /********** wxDbTable::ClearMemberVars() **********/
2280 void wxDbTable::ClearMemberVars(bool setToNull)
2281 {
2282 int i;
2283
2284 // Loop through the columns setting each member variable to zero
2285 for (i=0; i < m_numCols; i++)
2286 ClearMemberVar((UWORD)i,setToNull);
2287
2288 } // wxDbTable::ClearMemberVars()
2289
2290
2291 /********** wxDbTable::SetQueryTimeout() **********/
2292 bool wxDbTable::SetQueryTimeout(UDWORD nSeconds)
2293 {
2294 if (SQLSetStmtOption(hstmtInsert, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
2295 return(pDb->DispAllErrors(henv, hdbc, hstmtInsert));
2296 if (SQLSetStmtOption(hstmtUpdate, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
2297 return(pDb->DispAllErrors(henv, hdbc, hstmtUpdate));
2298 if (SQLSetStmtOption(hstmtDelete, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
2299 return(pDb->DispAllErrors(henv, hdbc, hstmtDelete));
2300 if (SQLSetStmtOption(hstmtInternal, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
2301 return(pDb->DispAllErrors(henv, hdbc, hstmtInternal));
2302
2303 // Completed Successfully
2304 return true;
2305
2306 } // wxDbTable::SetQueryTimeout()
2307
2308
2309 /********** wxDbTable::SetColDefs() **********/
2310 bool wxDbTable::SetColDefs(UWORD index, const wxString &fieldName, int dataType, void *pData,
2311 SWORD cType, int size, bool keyField, bool updateable,
2312 bool insertAllowed, bool derivedColumn)
2313 {
2314 wxString tmpStr;
2315
2316 if (index >= m_numCols) // Columns numbers are zero based....
2317 {
2318 tmpStr.Printf(wxT("Specified column index (%d) exceeds the maximum number of columns (%d) registered for this table definition. Column definition not added."), index, m_numCols);
2319 wxFAIL_MSG(tmpStr);
2320 wxLogDebug(tmpStr);
2321 return false;
2322 }
2323
2324 if (!colDefs) // May happen if the database connection fails
2325 return false;
2326
2327 if (fieldName.length() > (unsigned int) DB_MAX_COLUMN_NAME_LEN)
2328 {
2329 wxStrncpy(colDefs[index].ColName, fieldName, DB_MAX_COLUMN_NAME_LEN);
2330 colDefs[index].ColName[DB_MAX_COLUMN_NAME_LEN] = 0; // Prevent buffer overrun
2331
2332 tmpStr.Printf(wxT("Column name '%s' is too long. Truncated to '%s'."),
2333 fieldName.c_str(),colDefs[index].ColName);
2334 wxFAIL_MSG(tmpStr);
2335 wxLogDebug(tmpStr);
2336 }
2337 else
2338 wxStrcpy(colDefs[index].ColName, fieldName);
2339
2340 colDefs[index].DbDataType = dataType;
2341 colDefs[index].PtrDataObj = pData;
2342 colDefs[index].SqlCtype = cType;
2343 colDefs[index].SzDataObj = size; //TODO: glt ??? * sizeof(wxChar) ???
2344 colDefs[index].KeyField = keyField;
2345 colDefs[index].DerivedCol = derivedColumn;
2346 // Derived columns by definition would NOT be "Insertable" or "Updateable"
2347 if (derivedColumn)
2348 {
2349 colDefs[index].Updateable = false;
2350 colDefs[index].InsertAllowed = false;
2351 }
2352 else
2353 {
2354 colDefs[index].Updateable = updateable;
2355 colDefs[index].InsertAllowed = insertAllowed;
2356 }
2357
2358 colDefs[index].Null = false;
2359
2360 return true;
2361
2362 } // wxDbTable::SetColDefs()
2363
2364
2365 /********** wxDbTable::SetColDefs() **********/
2366 wxDbColDataPtr* wxDbTable::SetColDefs(wxDbColInf *pColInfs, UWORD numCols)
2367 {
2368 wxASSERT(pColInfs);
2369 wxDbColDataPtr *pColDataPtrs = NULL;
2370
2371 if (pColInfs)
2372 {
2373 UWORD index;
2374
2375 pColDataPtrs = new wxDbColDataPtr[numCols+1];
2376
2377 for (index = 0; index < numCols; index++)
2378 {
2379 // Process the fields
2380 switch (pColInfs[index].dbDataType)
2381 {
2382 case DB_DATA_TYPE_VARCHAR:
2383 pColDataPtrs[index].PtrDataObj = new wxChar[pColInfs[index].bufferSize+(1*sizeof(wxChar))];
2384 pColDataPtrs[index].SzDataObj = pColInfs[index].bufferSize+(1*sizeof(wxChar));
2385 pColDataPtrs[index].SqlCtype = SQL_C_WXCHAR;
2386 break;
2387 case DB_DATA_TYPE_MEMO:
2388 pColDataPtrs[index].PtrDataObj = new wxChar[pColInfs[index].bufferSize+(1*sizeof(wxChar))];
2389 pColDataPtrs[index].SzDataObj = pColInfs[index].bufferSize+(1*sizeof(wxChar));
2390 pColDataPtrs[index].SqlCtype = SQL_C_WXCHAR;
2391 break;
2392 case DB_DATA_TYPE_INTEGER:
2393 // Can be long or short
2394 if (pColInfs[index].bufferSize == sizeof(long))
2395 {
2396 pColDataPtrs[index].PtrDataObj = new long;
2397 pColDataPtrs[index].SzDataObj = sizeof(long);
2398 pColDataPtrs[index].SqlCtype = SQL_C_SLONG;
2399 }
2400 else
2401 {
2402 pColDataPtrs[index].PtrDataObj = new short;
2403 pColDataPtrs[index].SzDataObj = sizeof(short);
2404 pColDataPtrs[index].SqlCtype = SQL_C_SSHORT;
2405 }
2406 break;
2407 case DB_DATA_TYPE_FLOAT:
2408 // Can be float or double
2409 if (pColInfs[index].bufferSize == sizeof(float))
2410 {
2411 pColDataPtrs[index].PtrDataObj = new float;
2412 pColDataPtrs[index].SzDataObj = sizeof(float);
2413 pColDataPtrs[index].SqlCtype = SQL_C_FLOAT;
2414 }
2415 else
2416 {
2417 pColDataPtrs[index].PtrDataObj = new double;
2418 pColDataPtrs[index].SzDataObj = sizeof(double);
2419 pColDataPtrs[index].SqlCtype = SQL_C_DOUBLE;
2420 }
2421 break;
2422 case DB_DATA_TYPE_DATE:
2423 pColDataPtrs[index].PtrDataObj = new TIMESTAMP_STRUCT;
2424 pColDataPtrs[index].SzDataObj = sizeof(TIMESTAMP_STRUCT);
2425 pColDataPtrs[index].SqlCtype = SQL_C_TIMESTAMP;
2426 break;
2427 case DB_DATA_TYPE_BLOB:
2428 wxFAIL_MSG(wxT("This form of ::SetColDefs() cannot be used with BLOB columns"));
2429 pColDataPtrs[index].PtrDataObj = /*BLOB ADDITION NEEDED*/NULL;
2430 pColDataPtrs[index].SzDataObj = /*BLOB ADDITION NEEDED*/sizeof(void *);
2431 pColDataPtrs[index].SqlCtype = SQL_VARBINARY;
2432 break;
2433 }
2434 if (pColDataPtrs[index].PtrDataObj != NULL)
2435 SetColDefs (index,pColInfs[index].colName,pColInfs[index].dbDataType, pColDataPtrs[index].PtrDataObj, pColDataPtrs[index].SqlCtype, pColDataPtrs[index].SzDataObj);
2436 else
2437 {
2438 // Unable to build all the column definitions, as either one of
2439 // the calls to "new" failed above, or there was a BLOB field
2440 // to have a column definition for. If BLOBs are to be used,
2441 // the other form of ::SetColDefs() must be used, as it is impossible
2442 // to know the maximum size to create the PtrDataObj to be.
2443 delete [] pColDataPtrs;
2444 return NULL;
2445 }
2446 }
2447 }
2448
2449 return (pColDataPtrs);
2450
2451 } // wxDbTable::SetColDefs()
2452
2453
2454 /********** wxDbTable::SetCursor() **********/
2455 void wxDbTable::SetCursor(HSTMT *hstmtActivate)
2456 {
2457 if (hstmtActivate == wxDB_DEFAULT_CURSOR)
2458 hstmt = *hstmtDefault;
2459 else
2460 hstmt = *hstmtActivate;
2461
2462 } // wxDbTable::SetCursor()
2463
2464
2465 /********** wxDbTable::Count(const wxString &) **********/
2466 ULONG wxDbTable::Count(const wxString &args)
2467 {
2468 ULONG count;
2469 wxString sqlStmt;
2470 SQLLEN cb;
2471
2472 // Build a "SELECT COUNT(*) FROM queryTableName [WHERE whereClause]" SQL Statement
2473 sqlStmt = wxT("SELECT COUNT(");
2474 sqlStmt += args;
2475 sqlStmt += wxT(") FROM ");
2476 sqlStmt += pDb->SQLTableName(queryTableName);
2477 // sqlStmt += queryTableName;
2478 #if wxODBC_BACKWARD_COMPATABILITY
2479 if (from && wxStrlen(from))
2480 #else
2481 if (from.length())
2482 #endif
2483 sqlStmt += from;
2484
2485 // Add the where clause if one is provided
2486 #if wxODBC_BACKWARD_COMPATABILITY
2487 if (where && wxStrlen(where))
2488 #else
2489 if (where.length())
2490 #endif
2491 {
2492 sqlStmt += wxT(" WHERE ");
2493 sqlStmt += where;
2494 }
2495
2496 pDb->WriteSqlLog(sqlStmt);
2497
2498 // Initialize the Count cursor if it's not already initialized
2499 if (!hstmtCount)
2500 {
2501 hstmtCount = GetNewCursor(false,false);
2502 wxASSERT(hstmtCount);
2503 if (!hstmtCount)
2504 return(0);
2505 }
2506
2507 // Execute the SQL statement
2508 if (SQLExecDirect(*hstmtCount, (SQLTCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
2509 {
2510 pDb->DispAllErrors(henv, hdbc, *hstmtCount);
2511 return(0);
2512 }
2513
2514 // Fetch the record
2515 if (SQLFetch(*hstmtCount) != SQL_SUCCESS)
2516 {
2517 pDb->DispAllErrors(henv, hdbc, *hstmtCount);
2518 return(0);
2519 }
2520
2521 // Obtain the result
2522 if (SQLGetData(*hstmtCount, (UWORD)1, SQL_C_ULONG, &count, sizeof(count), &cb) != SQL_SUCCESS)
2523 {
2524 pDb->DispAllErrors(henv, hdbc, *hstmtCount);
2525 return(0);
2526 }
2527
2528 // Free the cursor
2529 if (SQLFreeStmt(*hstmtCount, SQL_CLOSE) != SQL_SUCCESS)
2530 pDb->DispAllErrors(henv, hdbc, *hstmtCount);
2531
2532 // Return the record count
2533 return(count);
2534
2535 } // wxDbTable::Count()
2536
2537
2538 /********** wxDbTable::Refresh() **********/
2539 bool wxDbTable::Refresh(void)
2540 {
2541 bool result = true;
2542
2543 // Switch to the internal cursor so any active cursors are not corrupted
2544 HSTMT currCursor = GetCursor();
2545 hstmt = hstmtInternal;
2546 #if wxODBC_BACKWARD_COMPATABILITY
2547 // Save the where and order by clauses
2548 wxChar *saveWhere = where;
2549 wxChar *saveOrderBy = orderBy;
2550 #else
2551 wxString saveWhere = where;
2552 wxString saveOrderBy = orderBy;
2553 #endif
2554 // Build a where clause to refetch the record with. Try and use the
2555 // ROWID if it's available, ow use the key fields.
2556 wxString whereClause;
2557 whereClause.Empty();
2558
2559 if (CanUpdateByROWID())
2560 {
2561 SQLLEN cb;
2562 wxChar rowid[wxDB_ROWID_LEN+1];
2563
2564 // Get the ROWID value. If not successful retreiving the ROWID,
2565 // simply fall down through the code and build the WHERE clause
2566 // based on the key fields.
2567 if (SQLGetData(hstmt, (UWORD)(m_numCols+1), SQL_C_WXCHAR, (UCHAR*) rowid, sizeof(rowid), &cb) == SQL_SUCCESS)
2568 {
2569 whereClause += pDb->SQLTableName(queryTableName);
2570 // whereClause += queryTableName;
2571 whereClause += wxT(".ROWID = '");
2572 whereClause += rowid;
2573 whereClause += wxT("'");
2574 }
2575 }
2576
2577 // If unable to use the ROWID, build a where clause from the keyfields
2578 if (wxStrlen(whereClause) == 0)
2579 BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS, queryTableName);
2580
2581 // Requery the record
2582 where = whereClause;
2583 orderBy.Empty();
2584 if (!Query())
2585 result = false;
2586
2587 if (result && !GetNext())
2588 result = false;
2589
2590 // Switch back to original cursor
2591 SetCursor(&currCursor);
2592
2593 // Free the internal cursor
2594 if (SQLFreeStmt(hstmtInternal, SQL_CLOSE) != SQL_SUCCESS)
2595 pDb->DispAllErrors(henv, hdbc, hstmtInternal);
2596
2597 // Restore the original where and order by clauses
2598 where = saveWhere;
2599 orderBy = saveOrderBy;
2600
2601 return(result);
2602
2603 } // wxDbTable::Refresh()
2604
2605
2606 /********** wxDbTable::SetColNull() **********/
2607 bool wxDbTable::SetColNull(UWORD colNumber, bool set)
2608 {
2609 if (colNumber < m_numCols)
2610 {
2611 colDefs[colNumber].Null = set;
2612 if (set) // Blank out the values in the member variable
2613 ClearMemberVar(colNumber, false); // Must call with false here, or infinite recursion will happen
2614
2615 setCbValueForColumn(colNumber);
2616
2617 return true;
2618 }
2619 else
2620 return false;
2621
2622 } // wxDbTable::SetColNull()
2623
2624
2625 /********** wxDbTable::SetColNull() **********/
2626 bool wxDbTable::SetColNull(const wxString &colName, bool set)
2627 {
2628 int colNumber;
2629 for (colNumber = 0; colNumber < m_numCols; colNumber++)
2630 {
2631 if (!wxStricmp(colName, colDefs[colNumber].ColName))
2632 break;
2633 }
2634
2635 if (colNumber < m_numCols)
2636 {
2637 colDefs[colNumber].Null = set;
2638 if (set) // Blank out the values in the member variable
2639 ClearMemberVar((UWORD)colNumber,false); // Must call with false here, or infinite recursion will happen
2640
2641 setCbValueForColumn(colNumber);
2642
2643 return true;
2644 }
2645 else
2646 return false;
2647
2648 } // wxDbTable::SetColNull()
2649
2650
2651 /********** wxDbTable::GetNewCursor() **********/
2652 HSTMT *wxDbTable::GetNewCursor(bool setCursor, bool bindColumns)
2653 {
2654 HSTMT *newHSTMT = new HSTMT;
2655 wxASSERT(newHSTMT);
2656 if (!newHSTMT)
2657 return(0);
2658
2659 if (SQLAllocStmt(hdbc, newHSTMT) != SQL_SUCCESS)
2660 {
2661 pDb->DispAllErrors(henv, hdbc);
2662 delete newHSTMT;
2663 return(0);
2664 }
2665
2666 if (SQLSetStmtOption(*newHSTMT, SQL_CURSOR_TYPE, cursorType) != SQL_SUCCESS)
2667 {
2668 pDb->DispAllErrors(henv, hdbc, *newHSTMT);
2669 delete newHSTMT;
2670 return(0);
2671 }
2672
2673 if (bindColumns)
2674 {
2675 if (!bindCols(*newHSTMT))
2676 {
2677 delete newHSTMT;
2678 return(0);
2679 }
2680 }
2681
2682 if (setCursor)
2683 SetCursor(newHSTMT);
2684
2685 return(newHSTMT);
2686
2687 } // wxDbTable::GetNewCursor()
2688
2689
2690 /********** wxDbTable::DeleteCursor() **********/
2691 bool wxDbTable::DeleteCursor(HSTMT *hstmtDel)
2692 {
2693 bool result = true;
2694
2695 if (!hstmtDel) // Cursor already deleted
2696 return(result);
2697
2698 /*
2699 ODBC 3.0 says to use this form
2700 if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS)
2701
2702 */
2703 if (SQLFreeStmt(*hstmtDel, SQL_DROP) != SQL_SUCCESS)
2704 {
2705 pDb->DispAllErrors(henv, hdbc);
2706 result = false;
2707 }
2708
2709 delete hstmtDel;
2710
2711 return(result);
2712
2713 } // wxDbTable::DeleteCursor()
2714
2715 //////////////////////////////////////////////////////////////
2716 // wxDbGrid support functions
2717 //////////////////////////////////////////////////////////////
2718
2719 void wxDbTable::SetRowMode(const rowmode_t rowmode)
2720 {
2721 if (!m_hstmtGridQuery)
2722 {
2723 m_hstmtGridQuery = GetNewCursor(false,false);
2724 if (!bindCols(*m_hstmtGridQuery))
2725 return;
2726 }
2727
2728 m_rowmode = rowmode;
2729 switch (m_rowmode)
2730 {
2731 case WX_ROW_MODE_QUERY:
2732 SetCursor(m_hstmtGridQuery);
2733 break;
2734 case WX_ROW_MODE_INDIVIDUAL:
2735 SetCursor(hstmtDefault);
2736 break;
2737 default:
2738 wxASSERT(0);
2739 }
2740 } // wxDbTable::SetRowMode()
2741
2742
2743 wxVariant wxDbTable::GetColumn(const int colNumber) const
2744 {
2745 wxVariant val;
2746 if ((colNumber < m_numCols) && (!IsColNull((UWORD)colNumber)))
2747 {
2748 switch (colDefs[colNumber].SqlCtype)
2749 {
2750 #if wxUSE_UNICODE
2751 #if defined(SQL_WCHAR)
2752 case SQL_WCHAR:
2753 #endif
2754 #if defined(SQL_WVARCHAR)
2755 case SQL_WVARCHAR:
2756 #endif
2757 #endif
2758 case SQL_CHAR:
2759 case SQL_VARCHAR:
2760 val = (wxChar *)(colDefs[colNumber].PtrDataObj);
2761 break;
2762 case SQL_C_LONG:
2763 case SQL_C_SLONG:
2764 val = *(long *)(colDefs[colNumber].PtrDataObj);
2765 break;
2766 case SQL_C_SHORT:
2767 case SQL_C_SSHORT:
2768 val = (long int )(*(short *)(colDefs[colNumber].PtrDataObj));
2769 break;
2770 case SQL_C_ULONG:
2771 val = (long)(*(unsigned long *)(colDefs[colNumber].PtrDataObj));
2772 break;
2773 case SQL_C_TINYINT:
2774 val = (long)(*(wxChar *)(colDefs[colNumber].PtrDataObj));
2775 break;
2776 case SQL_C_UTINYINT:
2777 val = (long)(*(wxChar *)(colDefs[colNumber].PtrDataObj));
2778 break;
2779 case SQL_C_USHORT:
2780 val = (long)(*(UWORD *)(colDefs[colNumber].PtrDataObj));
2781 break;
2782 case SQL_C_DATE:
2783 val = (DATE_STRUCT *)(colDefs[colNumber].PtrDataObj);
2784 break;
2785 case SQL_C_TIME:
2786 val = (TIME_STRUCT *)(colDefs[colNumber].PtrDataObj);
2787 break;
2788 case SQL_C_TIMESTAMP:
2789 val = (TIMESTAMP_STRUCT *)(colDefs[colNumber].PtrDataObj);
2790 break;
2791 case SQL_C_DOUBLE:
2792 val = *(double *)(colDefs[colNumber].PtrDataObj);
2793 break;
2794 default:
2795 assert(0);
2796 }
2797 }
2798 return val;
2799 } // wxDbTable::GetCol()
2800
2801
2802 void wxDbTable::SetColumn(const int colNumber, const wxVariant val)
2803 {
2804 //FIXME: Add proper wxDateTime support to wxVariant..
2805 wxDateTime dateval;
2806
2807 SetColNull((UWORD)colNumber, val.IsNull());
2808
2809 if (!val.IsNull())
2810 {
2811 if ((colDefs[colNumber].SqlCtype == SQL_C_DATE)
2812 || (colDefs[colNumber].SqlCtype == SQL_C_TIME)
2813 || (colDefs[colNumber].SqlCtype == SQL_C_TIMESTAMP))
2814 {
2815 //Returns null if invalid!
2816 if (!dateval.ParseDate(val.GetString()))
2817 SetColNull((UWORD)colNumber, true);
2818 }
2819
2820 switch (colDefs[colNumber].SqlCtype)
2821 {
2822 #if wxUSE_UNICODE
2823 #if defined(SQL_WCHAR)
2824 case SQL_WCHAR:
2825 #endif
2826 #if defined(SQL_WVARCHAR)
2827 case SQL_WVARCHAR:
2828 #endif
2829 #endif
2830 case SQL_CHAR:
2831 case SQL_VARCHAR:
2832 csstrncpyt((wxChar *)(colDefs[colNumber].PtrDataObj),
2833 val.GetString().c_str(),
2834 colDefs[colNumber].SzDataObj-1); //TODO: glt ??? * sizeof(wxChar) ???
2835 break;
2836 case SQL_C_LONG:
2837 case SQL_C_SLONG:
2838 *(long *)(colDefs[colNumber].PtrDataObj) = val;
2839 break;
2840 case SQL_C_SHORT:
2841 case SQL_C_SSHORT:
2842 *(short *)(colDefs[colNumber].PtrDataObj) = (short)val.GetLong();
2843 break;
2844 case SQL_C_ULONG:
2845 *(unsigned long *)(colDefs[colNumber].PtrDataObj) = val.GetLong();
2846 break;
2847 case SQL_C_TINYINT:
2848 *(wxChar *)(colDefs[colNumber].PtrDataObj) = val.GetChar();
2849 break;
2850 case SQL_C_UTINYINT:
2851 *(wxChar *)(colDefs[colNumber].PtrDataObj) = val.GetChar();
2852 break;
2853 case SQL_C_USHORT:
2854 *(unsigned short *)(colDefs[colNumber].PtrDataObj) = (unsigned short)val.GetLong();
2855 break;
2856 //FIXME: Add proper wxDateTime support to wxVariant..
2857 case SQL_C_DATE:
2858 {
2859 DATE_STRUCT *dataptr =
2860 (DATE_STRUCT *)colDefs[colNumber].PtrDataObj;
2861
2862 dataptr->year = (SWORD)dateval.GetYear();
2863 dataptr->month = (UWORD)(dateval.GetMonth()+1);
2864 dataptr->day = (UWORD)dateval.GetDay();
2865 }
2866 break;
2867 case SQL_C_TIME:
2868 {
2869 TIME_STRUCT *dataptr =
2870 (TIME_STRUCT *)colDefs[colNumber].PtrDataObj;
2871
2872 dataptr->hour = dateval.GetHour();
2873 dataptr->minute = dateval.GetMinute();
2874 dataptr->second = dateval.GetSecond();
2875 }
2876 break;
2877 case SQL_C_TIMESTAMP:
2878 {
2879 TIMESTAMP_STRUCT *dataptr =
2880 (TIMESTAMP_STRUCT *)colDefs[colNumber].PtrDataObj;
2881 dataptr->year = (SWORD)dateval.GetYear();
2882 dataptr->month = (UWORD)(dateval.GetMonth()+1);
2883 dataptr->day = (UWORD)dateval.GetDay();
2884
2885 dataptr->hour = dateval.GetHour();
2886 dataptr->minute = dateval.GetMinute();
2887 dataptr->second = dateval.GetSecond();
2888 }
2889 break;
2890 case SQL_C_DOUBLE:
2891 *(double *)(colDefs[colNumber].PtrDataObj) = val;
2892 break;
2893 default:
2894 assert(0);
2895 } // switch
2896 } // if (!val.IsNull())
2897 } // wxDbTable::SetCol()
2898
2899
2900 GenericKey wxDbTable::GetKey()
2901 {
2902 void *blk;
2903 wxChar *blkptr;
2904
2905 blk = malloc(m_keysize);
2906 blkptr = (wxChar *) blk;
2907
2908 int i;
2909 for (i=0; i < m_numCols; i++)
2910 {
2911 if (colDefs[i].KeyField)
2912 {
2913 memcpy(blkptr,colDefs[i].PtrDataObj, colDefs[i].SzDataObj);
2914 blkptr += colDefs[i].SzDataObj;
2915 }
2916 }
2917
2918 GenericKey k = GenericKey(blk, m_keysize);
2919 free(blk);
2920
2921 return k;
2922 } // wxDbTable::GetKey()
2923
2924
2925 void wxDbTable::SetKey(const GenericKey& k)
2926 {
2927 void *blk;
2928 wxChar *blkptr;
2929
2930 blk = k.GetBlk();
2931 blkptr = (wxChar *)blk;
2932
2933 int i;
2934 for (i=0; i < m_numCols; i++)
2935 {
2936 if (colDefs[i].KeyField)
2937 {
2938 SetColNull((UWORD)i, false);
2939 memcpy(colDefs[i].PtrDataObj, blkptr, colDefs[i].SzDataObj);
2940 blkptr += colDefs[i].SzDataObj;
2941 }
2942 }
2943 } // wxDbTable::SetKey()
2944
2945
2946 #endif // wxUSE_ODBC

  ViewVC Help
Powered by ViewVC 1.1.22