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