]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/common/dbtable.cpp
added and documented wxApp::OnAssert
[wxWidgets.git] / src / common / dbtable.cpp
... / ...
CommitLineData
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
74ULONG lastTableID = 0;
75
76
77#ifdef __WXDEBUG__
78 wxList TablesInUse;
79#endif
80
81
82/********** wxDbColDef::wxDbColDef() Constructor **********/
83wxDbColDef::wxDbColDef()
84{
85 Initialize();
86} // Constructor
87
88
89bool 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 **********/
108wxDbTable::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 *****/
117wxDbTable::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() **********/
128wxDbTable::~wxDbTable()
129{
130 this->cleanup();
131} // wxDbTable::~wxDbTable()
132
133
134bool 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
301void 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/*
352ODBC 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/*
362ODBC 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/*
372ODBC 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() **********/
403bool 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() **********/
511bool wxDbTable::bindInsertParams(void)
512{
513 return bindParams(FALSE);
514} // wxDbTable::bindInsertParams()
515
516
517/********** wxDbTable::bindUpdateParams() **********/
518bool wxDbTable::bindUpdateParams(void)
519{
520 return bindParams(TRUE);
521} // wxDbTable::bindUpdateParams()
522
523
524/********** wxDbTable::bindCols() **********/
525bool 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() **********/
545bool 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() **********/
600bool 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() **********/
622bool 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() **********/
644bool 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() **********/
681bool 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 ("), tableName.c_str());
767 for (i = 0; i < noCols; i++)
768 {
769 if (! colDefs[i].InsertAllowed)
770 continue;
771 if (needComma)
772 sqlStmt += wxT(",");
773 sqlStmt += colDefs[i].ColName;
774 needComma = TRUE;
775 }
776 needComma = FALSE;
777 sqlStmt += wxT(") VALUES (");
778
779 int insertableCount = 0;
780
781 for (i = 0; i < noCols; i++)
782 {
783 if (! colDefs[i].InsertAllowed)
784 continue;
785 if (needComma)
786 sqlStmt += wxT(",");
787 sqlStmt += wxT("?");
788 needComma = TRUE;
789 insertableCount++;
790 }
791 sqlStmt += wxT(")");
792
793 // Prepare the insert statement for execution
794 if (insertableCount)
795 {
796 if (SQLPrepare(hstmtInsert, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
797 return(pDb->DispAllErrors(henv, hdbc, hstmtInsert));
798 }
799 else
800 insertable= FALSE;
801 }
802
803 // Completed successfully
804 return(TRUE);
805
806} // wxDbTable::Open()
807
808
809/********** wxDbTable::Query() **********/
810bool wxDbTable::Query(bool forUpdate, bool distinct)
811{
812
813 return(query(DB_SELECT_WHERE, forUpdate, distinct));
814
815} // wxDbTable::Query()
816
817
818/********** wxDbTable::QueryBySqlStmt() **********/
819bool wxDbTable::QueryBySqlStmt(const wxString &pSqlStmt)
820{
821 pDb->WriteSqlLog(pSqlStmt);
822
823 return(query(DB_SELECT_STATEMENT, FALSE, FALSE, pSqlStmt));
824
825} // wxDbTable::QueryBySqlStmt()
826
827
828/********** wxDbTable::QueryMatching() **********/
829bool wxDbTable::QueryMatching(bool forUpdate, bool distinct)
830{
831
832 return(query(DB_SELECT_MATCHING, forUpdate, distinct));
833
834} // wxDbTable::QueryMatching()
835
836
837/********** wxDbTable::QueryOnKeyFields() **********/
838bool wxDbTable::QueryOnKeyFields(bool forUpdate, bool distinct)
839{
840
841 return(query(DB_SELECT_KEYFIELDS, forUpdate, distinct));
842
843} // wxDbTable::QueryOnKeyFields()
844
845
846/********** wxDbTable::GetPrev() **********/
847bool wxDbTable::GetPrev(void)
848{
849 if (pDb->FwdOnlyCursors())
850 {
851 wxFAIL_MSG(wxT("GetPrev()::Backward scrolling cursors are not enabled for this instance of wxDbTable"));
852 return FALSE;
853 }
854 else
855 return(getRec(SQL_FETCH_PRIOR));
856
857} // wxDbTable::GetPrev()
858
859
860/********** wxDbTable::operator-- **********/
861bool wxDbTable::operator--(int)
862{
863 if (pDb->FwdOnlyCursors())
864 {
865 wxFAIL_MSG(wxT("operator--:Backward scrolling cursors are not enabled for this instance of wxDbTable"));
866 return FALSE;
867 }
868 else
869 return(getRec(SQL_FETCH_PRIOR));
870
871} // wxDbTable::operator--
872
873
874/********** wxDbTable::GetFirst() **********/
875bool wxDbTable::GetFirst(void)
876{
877 if (pDb->FwdOnlyCursors())
878 {
879 wxFAIL_MSG(wxT("GetFirst():Backward scrolling cursors are not enabled for this instance of wxDbTable"));
880 return FALSE;
881 }
882 else
883 return(getRec(SQL_FETCH_FIRST));
884
885} // wxDbTable::GetFirst()
886
887
888/********** wxDbTable::GetLast() **********/
889bool wxDbTable::GetLast(void)
890{
891 if (pDb->FwdOnlyCursors())
892 {
893 wxFAIL_MSG(wxT("GetLast()::Backward scrolling cursors are not enabled for this instance of wxDbTable"));
894 return FALSE;
895 }
896 else
897 return(getRec(SQL_FETCH_LAST));
898
899} // wxDbTable::GetLast()
900
901
902/********** wxDbTable::BuildDeleteStmt() **********/
903void wxDbTable::BuildDeleteStmt(wxString &pSqlStmt, int typeOfDel, const wxString &pWhereClause)
904{
905 wxASSERT(!queryOnly);
906 if (queryOnly)
907 return;
908
909 wxString whereClause;
910
911 whereClause.Empty();
912
913 // Handle the case of DeleteWhere() and the where clause is blank. It should
914 // delete all records from the database in this case.
915 if (typeOfDel == DB_DEL_WHERE && (pWhereClause.Length() == 0))
916 {
917 pSqlStmt.Printf(wxT("DELETE FROM %s"), tableName.c_str());
918 return;
919 }
920
921 pSqlStmt.Printf(wxT("DELETE FROM %s WHERE "), tableName.c_str());
922
923 // Append the WHERE clause to the SQL DELETE statement
924 switch(typeOfDel)
925 {
926 case DB_DEL_KEYFIELDS:
927 // If the datasource supports the ROWID column, build
928 // the where on ROWID for efficiency purposes.
929 // e.g. DELETE FROM PARTS WHERE ROWID = '111.222.333'
930 if (CanUpdByROWID())
931 {
932 SDWORD cb;
933 wxChar rowid[wxDB_ROWID_LEN+1];
934
935 // Get the ROWID value. If not successful retreiving the ROWID,
936 // simply fall down through the code and build the WHERE clause
937 // based on the key fields.
938 if (SQLGetData(hstmt, (UWORD)(noCols+1), SQL_C_CHAR, (UCHAR*) rowid, wxDB_ROWID_LEN, &cb) == SQL_SUCCESS)
939 {
940 pSqlStmt += wxT("ROWID = '");
941 pSqlStmt += rowid;
942 pSqlStmt += wxT("'");
943 break;
944 }
945 }
946 // Unable to delete by ROWID, so build a WHERE
947 // clause based on the keyfields.
948 BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS);
949 pSqlStmt += whereClause;
950 break;
951 case DB_DEL_WHERE:
952 pSqlStmt += pWhereClause;
953 break;
954 case DB_DEL_MATCHING:
955 BuildWhereClause(whereClause, DB_WHERE_MATCHING);
956 pSqlStmt += whereClause;
957 break;
958 }
959
960} // BuildDeleteStmt()
961
962
963/***** DEPRECATED: use wxDbTable::BuildDeleteStmt(wxString &....) form *****/
964void wxDbTable::BuildDeleteStmt(wxChar *pSqlStmt, int typeOfDel, const wxString &pWhereClause)
965{
966 wxString tempSqlStmt;
967 BuildDeleteStmt(tempSqlStmt, typeOfDel, pWhereClause);
968 wxStrcpy(pSqlStmt, tempSqlStmt);
969} // wxDbTable::BuildDeleteStmt()
970
971
972/********** wxDbTable::BuildSelectStmt() **********/
973void wxDbTable::BuildSelectStmt(wxString &pSqlStmt, int typeOfSelect, bool distinct)
974{
975 wxString whereClause;
976 whereClause.Empty();
977
978 // Build a select statement to query the database
979 pSqlStmt = wxT("SELECT ");
980
981 // SELECT DISTINCT values only?
982 if (distinct)
983 pSqlStmt += wxT("DISTINCT ");
984
985 // Was a FROM clause specified to join tables to the base table?
986 // Available for ::Query() only!!!
987 bool appendFromClause = FALSE;
988#if wxODBC_BACKWARD_COMPATABILITY
989 if (typeOfSelect == DB_SELECT_WHERE && from && wxStrlen(from))
990 appendFromClause = TRUE;
991#else
992 if (typeOfSelect == DB_SELECT_WHERE && from.Length())
993 appendFromClause = TRUE;
994#endif
995
996 // Add the column list
997 int i;
998 for (i = 0; i < noCols; i++)
999 {
1000 // If joining tables, the base table column names must be qualified to avoid ambiguity
1001 if (appendFromClause || pDb->Dbms() == dbmsACCESS)
1002 {
1003 pSqlStmt += queryTableName;
1004 pSqlStmt += wxT(".");
1005 }
1006 pSqlStmt += colDefs[i].ColName;
1007 if (i + 1 < noCols)
1008 pSqlStmt += wxT(",");
1009 }
1010
1011 // If the datasource supports ROWID, get this column as well. Exception: Don't retrieve
1012 // the ROWID if querying distinct records. The rowid will always be unique.
1013 if (!distinct && CanUpdByROWID())
1014 {
1015 // If joining tables, the base table column names must be qualified to avoid ambiguity
1016 if (appendFromClause || pDb->Dbms() == dbmsACCESS)
1017 {
1018 pSqlStmt += wxT(",");
1019 pSqlStmt += queryTableName;
1020 pSqlStmt += wxT(".ROWID");
1021 }
1022 else
1023 pSqlStmt += wxT(",ROWID");
1024 }
1025
1026 // Append the FROM tablename portion
1027 pSqlStmt += wxT(" FROM ");
1028 pSqlStmt += queryTableName;
1029
1030 // Sybase uses the HOLDLOCK keyword to lock a record during query.
1031 // The HOLDLOCK keyword follows the table name in the from clause.
1032 // Each table in the from clause must specify HOLDLOCK or
1033 // NOHOLDLOCK (the default). Note: The "FOR UPDATE" clause
1034 // is parsed but ignored in SYBASE Transact-SQL.
1035 if (selectForUpdate && (pDb->Dbms() == dbmsSYBASE_ASA || pDb->Dbms() == dbmsSYBASE_ASE))
1036 pSqlStmt += wxT(" HOLDLOCK");
1037
1038 if (appendFromClause)
1039 pSqlStmt += from;
1040
1041 // Append the WHERE clause. Either append the where clause for the class
1042 // or build a where clause. The typeOfSelect determines this.
1043 switch(typeOfSelect)
1044 {
1045 case DB_SELECT_WHERE:
1046#if wxODBC_BACKWARD_COMPATABILITY
1047 if (where && wxStrlen(where)) // May not want a where clause!!!
1048#else
1049 if (where.Length()) // May not want a where clause!!!
1050#endif
1051 {
1052 pSqlStmt += wxT(" WHERE ");
1053 pSqlStmt += where;
1054 }
1055 break;
1056 case DB_SELECT_KEYFIELDS:
1057 BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS);
1058 if (whereClause.Length())
1059 {
1060 pSqlStmt += wxT(" WHERE ");
1061 pSqlStmt += whereClause;
1062 }
1063 break;
1064 case DB_SELECT_MATCHING:
1065 BuildWhereClause(whereClause, DB_WHERE_MATCHING);
1066 if (whereClause.Length())
1067 {
1068 pSqlStmt += wxT(" WHERE ");
1069 pSqlStmt += whereClause;
1070 }
1071 break;
1072 }
1073
1074 // Append the ORDER BY clause
1075#if wxODBC_BACKWARD_COMPATABILITY
1076 if (orderBy && wxStrlen(orderBy))
1077#else
1078 if (orderBy.Length())
1079#endif
1080 {
1081 pSqlStmt += wxT(" ORDER BY ");
1082 pSqlStmt += orderBy;
1083 }
1084
1085 // SELECT FOR UPDATE if told to do so and the datasource is capable. Sybase
1086 // parses the FOR UPDATE clause but ignores it. See the comment above on the
1087 // HOLDLOCK for Sybase.
1088 if (selectForUpdate && CanSelectForUpdate())
1089 pSqlStmt += wxT(" FOR UPDATE");
1090
1091} // wxDbTable::BuildSelectStmt()
1092
1093
1094/***** DEPRECATED: use wxDbTable::BuildSelectStmt(wxString &....) form *****/
1095void wxDbTable::BuildSelectStmt(wxChar *pSqlStmt, int typeOfSelect, bool distinct)
1096{
1097 wxString tempSqlStmt;
1098 BuildSelectStmt(tempSqlStmt, typeOfSelect, distinct);
1099 wxStrcpy(pSqlStmt, tempSqlStmt);
1100} // wxDbTable::BuildSelectStmt()
1101
1102
1103/********** wxDbTable::BuildUpdateStmt() **********/
1104void wxDbTable::BuildUpdateStmt(wxString &pSqlStmt, int typeOfUpd, const wxString &pWhereClause)
1105{
1106 wxASSERT(!queryOnly);
1107 if (queryOnly)
1108 return;
1109
1110 wxString whereClause;
1111 whereClause.Empty();
1112
1113 bool firstColumn = TRUE;
1114
1115 pSqlStmt.Printf(wxT("UPDATE %s SET "), tableName.Upper().c_str());
1116
1117 // Append a list of columns to be updated
1118 int i;
1119 for (i = 0; i < noCols; i++)
1120 {
1121 // Only append Updateable columns
1122 if (colDefs[i].Updateable)
1123 {
1124 if (!firstColumn)
1125 pSqlStmt += wxT(",");
1126 else
1127 firstColumn = FALSE;
1128 pSqlStmt += colDefs[i].ColName;
1129 pSqlStmt += wxT(" = ?");
1130 }
1131 }
1132
1133 // Append the WHERE clause to the SQL UPDATE statement
1134 pSqlStmt += wxT(" WHERE ");
1135 switch(typeOfUpd)
1136 {
1137 case DB_UPD_KEYFIELDS:
1138 // If the datasource supports the ROWID column, build
1139 // the where on ROWID for efficiency purposes.
1140 // e.g. UPDATE PARTS SET Col1 = ?, Col2 = ? WHERE ROWID = '111.222.333'
1141 if (CanUpdByROWID())
1142 {
1143 SDWORD cb;
1144 wxChar rowid[wxDB_ROWID_LEN+1];
1145
1146 // Get the ROWID value. If not successful retreiving the ROWID,
1147 // simply fall down through the code and build the WHERE clause
1148 // based on the key fields.
1149 if (SQLGetData(hstmt, (UWORD)(noCols+1), SQL_C_CHAR, (UCHAR*) rowid, wxDB_ROWID_LEN, &cb) == SQL_SUCCESS)
1150 {
1151 pSqlStmt += wxT("ROWID = '");
1152 pSqlStmt += rowid;
1153 pSqlStmt += wxT("'");
1154 break;
1155 }
1156 }
1157 // Unable to delete by ROWID, so build a WHERE
1158 // clause based on the keyfields.
1159 BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS);
1160 pSqlStmt += whereClause;
1161 break;
1162 case DB_UPD_WHERE:
1163 pSqlStmt += pWhereClause;
1164 break;
1165 }
1166} // BuildUpdateStmt()
1167
1168
1169/***** DEPRECATED: use wxDbTable::BuildUpdateStmt(wxString &....) form *****/
1170void wxDbTable::BuildUpdateStmt(wxChar *pSqlStmt, int typeOfUpd, const wxString &pWhereClause)
1171{
1172 wxString tempSqlStmt;
1173 BuildUpdateStmt(tempSqlStmt, typeOfUpd, pWhereClause);
1174 wxStrcpy(pSqlStmt, tempSqlStmt);
1175} // BuildUpdateStmt()
1176
1177
1178/********** wxDbTable::BuildWhereClause() **********/
1179void wxDbTable::BuildWhereClause(wxString &pWhereClause, int typeOfWhere,
1180 const wxString &qualTableName, bool useLikeComparison)
1181/*
1182 * Note: BuildWhereClause() currently ignores timestamp columns.
1183 * They are not included as part of the where clause.
1184 */
1185{
1186 bool moreThanOneColumn = FALSE;
1187 wxString colValue;
1188
1189 // Loop through the columns building a where clause as you go
1190 int i;
1191 for (i = 0; i < noCols; i++)
1192 {
1193 // Determine if this column should be included in the WHERE clause
1194 if ((typeOfWhere == DB_WHERE_KEYFIELDS && colDefs[i].KeyField) ||
1195 (typeOfWhere == DB_WHERE_MATCHING && (!IsColNull(i))))
1196 {
1197 // Skip over timestamp columns
1198 if (colDefs[i].SqlCtype == SQL_C_TIMESTAMP)
1199 continue;
1200 // If there is more than 1 column, join them with the keyword "AND"
1201 if (moreThanOneColumn)
1202 pWhereClause += wxT(" AND ");
1203 else
1204 moreThanOneColumn = TRUE;
1205 // Concatenate where phrase for the column
1206 if (qualTableName.Length())
1207 {
1208 pWhereClause += qualTableName;
1209 pWhereClause += wxT(".");
1210 }
1211 pWhereClause += colDefs[i].ColName;
1212 if (useLikeComparison && (colDefs[i].SqlCtype == SQL_C_CHAR))
1213 pWhereClause += wxT(" LIKE ");
1214 else
1215 pWhereClause += wxT(" = ");
1216 switch(colDefs[i].SqlCtype)
1217 {
1218 case SQL_C_CHAR:
1219 colValue.Printf(wxT("'%s'"), (UCHAR FAR *) colDefs[i].PtrDataObj);
1220 break;
1221 case SQL_C_SSHORT:
1222 colValue.Printf(wxT("%hi"), *((SWORD *) colDefs[i].PtrDataObj));
1223 break;
1224 case SQL_C_USHORT:
1225 colValue.Printf(wxT("%hu"), *((UWORD *) colDefs[i].PtrDataObj));
1226 break;
1227 case SQL_C_SLONG:
1228 colValue.Printf(wxT("%li"), *((SDWORD *) colDefs[i].PtrDataObj));
1229 break;
1230 case SQL_C_ULONG:
1231 colValue.Printf(wxT("%lu"), *((UDWORD *) colDefs[i].PtrDataObj));
1232 break;
1233 case SQL_C_FLOAT:
1234 colValue.Printf(wxT("%.6f"), *((SFLOAT *) colDefs[i].PtrDataObj));
1235 break;
1236 case SQL_C_DOUBLE:
1237 colValue.Printf(wxT("%.6f"), *((SDOUBLE *) colDefs[i].PtrDataObj));
1238 break;
1239 }
1240 pWhereClause += colValue;
1241 }
1242 }
1243} // wxDbTable::BuildWhereClause()
1244
1245
1246/***** DEPRECATED: use wxDbTable::BuildWhereClause(wxString &....) form *****/
1247void wxDbTable::BuildWhereClause(wxChar *pWhereClause, int typeOfWhere,
1248 const wxString &qualTableName, bool useLikeComparison)
1249{
1250 wxString tempSqlStmt;
1251 BuildWhereClause(tempSqlStmt, typeOfWhere, qualTableName, useLikeComparison);
1252 wxStrcpy(pWhereClause, tempSqlStmt);
1253} // wxDbTable::BuildWhereClause()
1254
1255
1256/********** wxDbTable::GetRowNum() **********/
1257UWORD wxDbTable::GetRowNum(void)
1258{
1259 UDWORD rowNum;
1260
1261 if (SQLGetStmtOption(hstmt, SQL_ROW_NUMBER, (UCHAR*) &rowNum) != SQL_SUCCESS)
1262 {
1263 pDb->DispAllErrors(henv, hdbc, hstmt);
1264 return(0);
1265 }
1266
1267 // Completed successfully
1268 return((UWORD) rowNum);
1269
1270} // wxDbTable::GetRowNum()
1271
1272
1273/********** wxDbTable::CloseCursor() **********/
1274bool wxDbTable::CloseCursor(HSTMT cursor)
1275{
1276 if (SQLFreeStmt(cursor, SQL_CLOSE) != SQL_SUCCESS)
1277 return(pDb->DispAllErrors(henv, hdbc, cursor));
1278
1279 // Completed successfully
1280 return(TRUE);
1281
1282} // wxDbTable::CloseCursor()
1283
1284
1285/********** wxDbTable::CreateTable() **********/
1286bool wxDbTable::CreateTable(bool attemptDrop)
1287{
1288 if (!pDb)
1289 return FALSE;
1290
1291 int i, j;
1292 wxString sqlStmt;
1293
1294#ifdef DBDEBUG_CONSOLE
1295 cout << wxT("Creating Table ") << tableName << wxT("...") << endl;
1296#endif
1297
1298 // Drop table first
1299 if (attemptDrop && !DropTable())
1300 return FALSE;
1301
1302 // Create the table
1303#ifdef DBDEBUG_CONSOLE
1304 for (i = 0; i < noCols; i++)
1305 {
1306 // Exclude derived columns since they are NOT part of the base table
1307 if (colDefs[i].DerivedCol)
1308 continue;
1309 cout << i + 1 << wxT(": ") << colDefs[i].ColName << wxT("; ");
1310 switch(colDefs[i].DbDataType)
1311 {
1312 case DB_DATA_TYPE_VARCHAR:
1313 cout << pDb->GetTypeInfVarchar().TypeName << wxT("(") << colDefs[i].SzDataObj << wxT(")");
1314 break;
1315 case DB_DATA_TYPE_INTEGER:
1316 cout << pDb->GetTypeInfInteger().TypeName;
1317 break;
1318 case DB_DATA_TYPE_FLOAT:
1319 cout << pDb->GetTypeInfFloat().TypeName;
1320 break;
1321 case DB_DATA_TYPE_DATE:
1322 cout << pDb->GetTypeInfDate().TypeName;
1323 break;
1324 case DB_DATA_TYPE_BLOB:
1325 cout << pDb->GetTypeInfBlob().TypeName;
1326 break;
1327 }
1328 cout << endl;
1329 }
1330#endif
1331
1332 // Build a CREATE TABLE string from the colDefs structure.
1333 bool needComma = FALSE;
1334 sqlStmt.Printf(wxT("CREATE TABLE %s ("), tableName.c_str());
1335
1336 for (i = 0; i < noCols; i++)
1337 {
1338 // Exclude derived columns since they are NOT part of the base table
1339 if (colDefs[i].DerivedCol)
1340 continue;
1341 // Comma Delimiter
1342 if (needComma)
1343 sqlStmt += wxT(",");
1344 // Column Name
1345 sqlStmt += colDefs[i].ColName;
1346 sqlStmt += wxT(" ");
1347 // Column Type
1348 switch(colDefs[i].DbDataType)
1349 {
1350 case DB_DATA_TYPE_VARCHAR:
1351 sqlStmt += pDb->GetTypeInfVarchar().TypeName;
1352 break;
1353 case DB_DATA_TYPE_INTEGER:
1354 sqlStmt += pDb->GetTypeInfInteger().TypeName;
1355 break;
1356 case DB_DATA_TYPE_FLOAT:
1357 sqlStmt += pDb->GetTypeInfFloat().TypeName;
1358 break;
1359 case DB_DATA_TYPE_DATE:
1360 sqlStmt += pDb->GetTypeInfDate().TypeName;
1361 break;
1362 case DB_DATA_TYPE_BLOB:
1363 sqlStmt += pDb->GetTypeInfBlob().TypeName;
1364 break;
1365 }
1366 // For varchars, append the size of the string
1367 if (colDefs[i].DbDataType == DB_DATA_TYPE_VARCHAR)// ||
1368// colDefs[i].DbDataType == DB_DATA_TYPE_BLOB)
1369 {
1370 wxString s;
1371 s.Printf(wxT("(%d)"), colDefs[i].SzDataObj);
1372 sqlStmt += s;
1373 }
1374
1375 if (pDb->Dbms() == dbmsDB2 ||
1376 pDb->Dbms() == dbmsMY_SQL ||
1377 pDb->Dbms() == dbmsSYBASE_ASE ||
1378 pDb->Dbms() == dbmsINTERBASE ||
1379 pDb->Dbms() == dbmsMS_SQL_SERVER)
1380 {
1381 if (colDefs[i].KeyField)
1382 {
1383 sqlStmt += wxT(" NOT NULL");
1384 }
1385 }
1386
1387 needComma = TRUE;
1388 }
1389 // If there is a primary key defined, include it in the create statement
1390 for (i = j = 0; i < noCols; i++)
1391 {
1392 if (colDefs[i].KeyField)
1393 {
1394 j++;
1395 break;
1396 }
1397 }
1398 if (j && pDb->Dbms() != dbmsDBASE) // Found a keyfield
1399 {
1400 switch (pDb->Dbms())
1401 {
1402 case dbmsINFORMIX:
1403 case dbmsSYBASE_ASA:
1404 case dbmsSYBASE_ASE:
1405 case dbmsMY_SQL:
1406 {
1407 // MySQL goes out on this one. We also declare the relevant key NON NULL above
1408 sqlStmt += wxT(",PRIMARY KEY (");
1409 break;
1410 }
1411 default:
1412 {
1413 sqlStmt += wxT(",CONSTRAINT ");
1414 // DB2 is limited to 18 characters for index names
1415 if (pDb->Dbms() == dbmsDB2)
1416 {
1417 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."));
1418 sqlStmt += tableName.substr(0, 13);
1419 }
1420 else
1421 sqlStmt += tableName;
1422
1423 sqlStmt += wxT("_PIDX PRIMARY KEY (");
1424 break;
1425 }
1426 }
1427
1428 // List column name(s) of column(s) comprising the primary key
1429 for (i = j = 0; i < noCols; i++)
1430 {
1431 if (colDefs[i].KeyField)
1432 {
1433 if (j++) // Multi part key, comma separate names
1434 sqlStmt += wxT(",");
1435 sqlStmt += colDefs[i].ColName;
1436 }
1437 }
1438 sqlStmt += wxT(")");
1439
1440 if (pDb->Dbms() == dbmsINFORMIX ||
1441 pDb->Dbms() == dbmsSYBASE_ASA ||
1442 pDb->Dbms() == dbmsSYBASE_ASE)
1443 {
1444 sqlStmt += wxT(" CONSTRAINT ");
1445 sqlStmt += tableName;
1446 sqlStmt += wxT("_PIDX");
1447 }
1448 }
1449 // Append the closing parentheses for the create table statement
1450 sqlStmt += wxT(")");
1451
1452 pDb->WriteSqlLog(sqlStmt);
1453
1454#ifdef DBDEBUG_CONSOLE
1455 cout << endl << sqlStmt.c_str() << endl;
1456#endif
1457
1458 // Execute the CREATE TABLE statement
1459 RETCODE retcode = SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS);
1460 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
1461 {
1462 pDb->DispAllErrors(henv, hdbc, hstmt);
1463 pDb->RollbackTrans();
1464 CloseCursor(hstmt);
1465 return(FALSE);
1466 }
1467
1468 // Commit the transaction and close the cursor
1469 if (!pDb->CommitTrans())
1470 return(FALSE);
1471 if (!CloseCursor(hstmt))
1472 return(FALSE);
1473
1474 // Database table created successfully
1475 return(TRUE);
1476
1477} // wxDbTable::CreateTable()
1478
1479
1480/********** wxDbTable::DropTable() **********/
1481bool wxDbTable::DropTable()
1482{
1483 // NOTE: This function returns TRUE if the Table does not exist, but
1484 // only for identified databases. Code will need to be added
1485 // below for any other databases when those databases are defined
1486 // to handle this situation consistently
1487
1488 wxString sqlStmt;
1489
1490 sqlStmt.Printf(wxT("DROP TABLE %s"), tableName.c_str());
1491
1492 pDb->WriteSqlLog(sqlStmt);
1493
1494#ifdef DBDEBUG_CONSOLE
1495 cout << endl << sqlStmt.c_str() << endl;
1496#endif
1497
1498
1499
1500
1501 RETCODE retcode = SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS);
1502 if (retcode != SQL_SUCCESS)
1503 {
1504 // Check for "Base table not found" error and ignore
1505 pDb->GetNextError(henv, hdbc, hstmt);
1506 if (wxStrcmp(pDb->sqlState, wxT("S0002")) /*&&
1507 wxStrcmp(pDb->sqlState, wxT("S1000"))*/) // "Base table not found"
1508 {
1509 // Check for product specific error codes
1510 if (!((pDb->Dbms() == dbmsSYBASE_ASA && !wxStrcmp(pDb->sqlState,wxT("42000"))) || // 5.x (and lower?)
1511 (pDb->Dbms() == dbmsSYBASE_ASE && !wxStrcmp(pDb->sqlState,wxT("37000"))) ||
1512 (pDb->Dbms() == dbmsPERVASIVE_SQL && !wxStrcmp(pDb->sqlState,wxT("S1000"))) || // Returns an S1000 then an S0002
1513 (pDb->Dbms() == dbmsPOSTGRES && !wxStrcmp(pDb->sqlState,wxT("08S01")))))
1514 {
1515 pDb->DispNextError();
1516 pDb->DispAllErrors(henv, hdbc, hstmt);
1517 pDb->RollbackTrans();
1518// CloseCursor(hstmt);
1519 return(FALSE);
1520 }
1521 }
1522 }
1523
1524 // Commit the transaction and close the cursor
1525 if (! pDb->CommitTrans())
1526 return(FALSE);
1527 if (! CloseCursor(hstmt))
1528 return(FALSE);
1529
1530 return(TRUE);
1531} // wxDbTable::DropTable()
1532
1533
1534/********** wxDbTable::CreateIndex() **********/
1535bool wxDbTable::CreateIndex(const wxString &idxName, bool unique, UWORD noIdxCols,
1536 wxDbIdxDef *pIdxDefs, bool attemptDrop)
1537{
1538 wxString sqlStmt;
1539
1540 // Drop the index first
1541 if (attemptDrop && !DropIndex(idxName))
1542 return (FALSE);
1543
1544 // MySQL (and possibly Sybase ASE?? - gt) require that any columns which are used as portions
1545 // of an index have the columns defined as "NOT NULL". During initial table creation though,
1546 // it may not be known which columns are necessarily going to be part of an index (e.g. the
1547 // table was created, then months later you determine that an additional index while
1548 // give better performance, so you want to add an index).
1549 //
1550 // The following block of code will modify the column definition to make the column be
1551 // defined with the "NOT NULL" qualifier.
1552 if (pDb->Dbms() == dbmsMY_SQL)
1553 {
1554 wxString sqlStmt;
1555 int i;
1556 bool ok = TRUE;
1557 for (i = 0; i < noIdxCols && ok; i++)
1558 {
1559 int j = 0;
1560 bool found = FALSE;
1561 // Find the column definition that has the ColName that matches the
1562 // index column name. We need to do this to get the DB_DATA_TYPE of
1563 // the index column, as MySQL's syntax for the ALTER column requires
1564 // this information
1565 while (!found && (j < this->noCols))
1566 {
1567 if (wxStrcmp(colDefs[j].ColName,pIdxDefs[i].ColName) == 0)
1568 found = TRUE;
1569 if (!found)
1570 j++;
1571 }
1572
1573 if (found)
1574 {
1575 ok = pDb->ModifyColumn(tableName, pIdxDefs[i].ColName,
1576 colDefs[j].DbDataType, colDefs[j].SzDataObj,
1577 wxT("NOT NULL"));
1578
1579 if (!ok)
1580 {
1581 wxODBC_ERRORS retcode;
1582 // Oracle returns a DB_ERR_GENERAL_ERROR if the column is already
1583 // defined to be NOT NULL, but reportedly MySQL doesn't mind.
1584 // This line is just here for debug checking of the value
1585 retcode = (wxODBC_ERRORS)pDb->DB_STATUS;
1586 }
1587 }
1588 else
1589 ok = FALSE;
1590 }
1591 if (ok)
1592 pDb->CommitTrans();
1593 else
1594 {
1595 pDb->RollbackTrans();
1596 return(FALSE);
1597 }
1598 }
1599
1600 // Build a CREATE INDEX statement
1601 sqlStmt = wxT("CREATE ");
1602 if (unique)
1603 sqlStmt += wxT("UNIQUE ");
1604
1605 sqlStmt += wxT("INDEX ");
1606 sqlStmt += idxName;
1607 sqlStmt += wxT(" ON ");
1608 sqlStmt += tableName;
1609 sqlStmt += wxT(" (");
1610
1611 // Append list of columns making up index
1612 int i;
1613 for (i = 0; i < noIdxCols; i++)
1614 {
1615 sqlStmt += pIdxDefs[i].ColName;
1616
1617 // Postgres and SQL Server 7 do not support the ASC/DESC keywords for index columns
1618 if (!((pDb->Dbms() == dbmsMS_SQL_SERVER) && (strncmp(pDb->dbInf.dbmsVer,"07",2)==0)) &&
1619 !(pDb->Dbms() == dbmsPOSTGRES))
1620 {
1621 if (pIdxDefs[i].Ascending)
1622 sqlStmt += wxT(" ASC");
1623 else
1624 sqlStmt += wxT(" DESC");
1625 }
1626 else
1627 wxASSERT_MSG(pIdxDefs[i].Ascending, "Datasource does not support DESCending index columns");
1628
1629 if ((i + 1) < noIdxCols)
1630 sqlStmt += wxT(",");
1631 }
1632
1633 // Append closing parentheses
1634 sqlStmt += wxT(")");
1635
1636 pDb->WriteSqlLog(sqlStmt);
1637
1638#ifdef DBDEBUG_CONSOLE
1639 cout << endl << sqlStmt.c_str() << endl << endl;
1640#endif
1641
1642 // Execute the CREATE INDEX statement
1643 if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
1644 {
1645 pDb->DispAllErrors(henv, hdbc, hstmt);
1646 pDb->RollbackTrans();
1647 CloseCursor(hstmt);
1648 return(FALSE);
1649 }
1650
1651 // Commit the transaction and close the cursor
1652 if (! pDb->CommitTrans())
1653 return(FALSE);
1654 if (! CloseCursor(hstmt))
1655 return(FALSE);
1656
1657 // Index Created Successfully
1658 return(TRUE);
1659
1660} // wxDbTable::CreateIndex()
1661
1662
1663/********** wxDbTable::DropIndex() **********/
1664bool wxDbTable::DropIndex(const wxString &idxName)
1665{
1666 // NOTE: This function returns TRUE if the Index does not exist, but
1667 // only for identified databases. Code will need to be added
1668 // below for any other databases when those databases are defined
1669 // to handle this situation consistently
1670
1671 wxString sqlStmt;
1672
1673 if (pDb->Dbms() == dbmsACCESS || pDb->Dbms() == dbmsMY_SQL ||
1674 pDb->Dbms() == dbmsDBASE /*|| Paradox needs this syntax too when we add support*/)
1675 sqlStmt.Printf(wxT("DROP INDEX %s ON %s"),idxName.c_str(), tableName.c_str());
1676 else if ((pDb->Dbms() == dbmsMS_SQL_SERVER) ||
1677 (pDb->Dbms() == dbmsSYBASE_ASE))
1678 sqlStmt.Printf(wxT("DROP INDEX %s.%s"),tableName.c_str(), idxName.c_str());
1679 else
1680 sqlStmt.Printf(wxT("DROP INDEX %s"),idxName.c_str());
1681
1682 pDb->WriteSqlLog(sqlStmt);
1683
1684#ifdef DBDEBUG_CONSOLE
1685 cout << endl << sqlStmt.c_str() << endl;
1686#endif
1687
1688 if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
1689 {
1690 // Check for "Index not found" error and ignore
1691 pDb->GetNextError(henv, hdbc, hstmt);
1692 if (wxStrcmp(pDb->sqlState,wxT("S0012"))) // "Index not found"
1693 {
1694 // Check for product specific error codes
1695 if (!((pDb->Dbms() == dbmsSYBASE_ASA && !wxStrcmp(pDb->sqlState,wxT("42000"))) || // v5.x (and lower?)
1696 (pDb->Dbms() == dbmsSYBASE_ASE && !wxStrcmp(pDb->sqlState,wxT("37000"))) ||
1697 (pDb->Dbms() == dbmsMS_SQL_SERVER && !wxStrcmp(pDb->sqlState,wxT("S1000"))) ||
1698 (pDb->Dbms() == dbmsINTERBASE && !wxStrcmp(pDb->sqlState,wxT("S1000"))) ||
1699 (pDb->Dbms() == dbmsSYBASE_ASE && !wxStrcmp(pDb->sqlState,wxT("S0002"))) || // Base table not found
1700 (pDb->Dbms() == dbmsMY_SQL && !wxStrcmp(pDb->sqlState,wxT("42S12"))) || // tested by Christopher Ludwik Marino-Cebulski using v3.23.21beta
1701 (pDb->Dbms() == dbmsPOSTGRES && !wxStrcmp(pDb->sqlState,wxT("08S01")))
1702 ))
1703 {
1704 pDb->DispNextError();
1705 pDb->DispAllErrors(henv, hdbc, hstmt);
1706 pDb->RollbackTrans();
1707 CloseCursor(hstmt);
1708 return(FALSE);
1709 }
1710 }
1711 }
1712
1713 // Commit the transaction and close the cursor
1714 if (! pDb->CommitTrans())
1715 return(FALSE);
1716 if (! CloseCursor(hstmt))
1717 return(FALSE);
1718
1719 return(TRUE);
1720} // wxDbTable::DropIndex()
1721
1722
1723/********** wxDbTable::SetOrderByColNums() **********/
1724bool wxDbTable::SetOrderByColNums(UWORD first, ... )
1725{
1726 int colNo = first; // using 'int' to be able to look for wxDB_NO_MORE_COLUN_NUMBERS
1727 va_list argptr;
1728
1729 bool abort = FALSE;
1730 wxString tempStr;
1731
1732 va_start(argptr, first); /* Initialize variable arguments. */
1733 while (!abort && (colNo != wxDB_NO_MORE_COLUMN_NUMBERS))
1734 {
1735 // Make sure the passed in column number
1736 // is within the valid range of columns
1737 //
1738 // Valid columns are 0 thru noCols-1
1739 if (colNo >= noCols || colNo < 0)
1740 {
1741 abort = TRUE;
1742 continue;
1743 }
1744
1745 if (colNo != first)
1746 tempStr += wxT(",");
1747
1748 tempStr += colDefs[colNo].ColName;
1749 colNo = va_arg (argptr, int);
1750 }
1751 va_end (argptr); /* Reset variable arguments. */
1752
1753 SetOrderByClause(tempStr);
1754
1755 return (!abort);
1756} // wxDbTable::SetOrderByColNums()
1757
1758
1759/********** wxDbTable::Insert() **********/
1760int wxDbTable::Insert(void)
1761{
1762 wxASSERT(!queryOnly);
1763 if (queryOnly || !insertable)
1764 return(DB_FAILURE);
1765
1766 bindInsertParams();
1767
1768 // Insert the record by executing the already prepared insert statement
1769 RETCODE retcode;
1770 retcode=SQLExecute(hstmtInsert);
1771 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
1772 {
1773 // Check to see if integrity constraint was violated
1774 pDb->GetNextError(henv, hdbc, hstmtInsert);
1775 if (! wxStrcmp(pDb->sqlState, wxT("23000"))) // Integrity constraint violated
1776 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL);
1777 else
1778 {
1779 pDb->DispNextError();
1780 pDb->DispAllErrors(henv, hdbc, hstmtInsert);
1781 return(DB_FAILURE);
1782 }
1783 }
1784
1785 // Record inserted into the datasource successfully
1786 return(DB_SUCCESS);
1787
1788} // wxDbTable::Insert()
1789
1790
1791/********** wxDbTable::Update() **********/
1792bool wxDbTable::Update(void)
1793{
1794 wxASSERT(!queryOnly);
1795 if (queryOnly)
1796 return(FALSE);
1797
1798 wxString sqlStmt;
1799
1800 // Build the SQL UPDATE statement
1801 BuildUpdateStmt(sqlStmt, DB_UPD_KEYFIELDS);
1802
1803 pDb->WriteSqlLog(sqlStmt);
1804
1805#ifdef DBDEBUG_CONSOLE
1806 cout << endl << sqlStmt.c_str() << endl << endl;
1807#endif
1808
1809 // Execute the SQL UPDATE statement
1810 return(execUpdate(sqlStmt));
1811
1812} // wxDbTable::Update()
1813
1814
1815/********** wxDbTable::Update(pSqlStmt) **********/
1816bool wxDbTable::Update(const wxString &pSqlStmt)
1817{
1818 wxASSERT(!queryOnly);
1819 if (queryOnly)
1820 return(FALSE);
1821
1822 pDb->WriteSqlLog(pSqlStmt);
1823
1824 return(execUpdate(pSqlStmt));
1825
1826} // wxDbTable::Update(pSqlStmt)
1827
1828
1829/********** wxDbTable::UpdateWhere() **********/
1830bool wxDbTable::UpdateWhere(const wxString &pWhereClause)
1831{
1832 wxASSERT(!queryOnly);
1833 if (queryOnly)
1834 return(FALSE);
1835
1836 wxString sqlStmt;
1837
1838 // Build the SQL UPDATE statement
1839 BuildUpdateStmt(sqlStmt, DB_UPD_WHERE, pWhereClause);
1840
1841 pDb->WriteSqlLog(sqlStmt);
1842
1843#ifdef DBDEBUG_CONSOLE
1844 cout << endl << sqlStmt.c_str() << endl << endl;
1845#endif
1846
1847 // Execute the SQL UPDATE statement
1848 return(execUpdate(sqlStmt));
1849
1850} // wxDbTable::UpdateWhere()
1851
1852
1853/********** wxDbTable::Delete() **********/
1854bool wxDbTable::Delete(void)
1855{
1856 wxASSERT(!queryOnly);
1857 if (queryOnly)
1858 return(FALSE);
1859
1860 wxString sqlStmt;
1861 sqlStmt.Empty();
1862
1863 // Build the SQL DELETE statement
1864 BuildDeleteStmt(sqlStmt, DB_DEL_KEYFIELDS);
1865
1866 pDb->WriteSqlLog(sqlStmt);
1867
1868 // Execute the SQL DELETE statement
1869 return(execDelete(sqlStmt));
1870
1871} // wxDbTable::Delete()
1872
1873
1874/********** wxDbTable::DeleteWhere() **********/
1875bool wxDbTable::DeleteWhere(const wxString &pWhereClause)
1876{
1877 wxASSERT(!queryOnly);
1878 if (queryOnly)
1879 return(FALSE);
1880
1881 wxString sqlStmt;
1882 sqlStmt.Empty();
1883
1884 // Build the SQL DELETE statement
1885 BuildDeleteStmt(sqlStmt, DB_DEL_WHERE, pWhereClause);
1886
1887 pDb->WriteSqlLog(sqlStmt);
1888
1889 // Execute the SQL DELETE statement
1890 return(execDelete(sqlStmt));
1891
1892} // wxDbTable::DeleteWhere()
1893
1894
1895/********** wxDbTable::DeleteMatching() **********/
1896bool wxDbTable::DeleteMatching(void)
1897{
1898 wxASSERT(!queryOnly);
1899 if (queryOnly)
1900 return(FALSE);
1901
1902 wxString sqlStmt;
1903 sqlStmt.Empty();
1904
1905 // Build the SQL DELETE statement
1906 BuildDeleteStmt(sqlStmt, DB_DEL_MATCHING);
1907
1908 pDb->WriteSqlLog(sqlStmt);
1909
1910 // Execute the SQL DELETE statement
1911 return(execDelete(sqlStmt));
1912
1913} // wxDbTable::DeleteMatching()
1914
1915
1916/********** wxDbTable::IsColNull() **********/
1917bool wxDbTable::IsColNull(UWORD colNo) const
1918{
1919/*
1920 This logic is just not right. It would indicate TRUE
1921 if a numeric field were set to a value of 0.
1922
1923 switch(colDefs[colNo].SqlCtype)
1924 {
1925 case SQL_C_CHAR:
1926 return(((UCHAR FAR *) colDefs[colNo].PtrDataObj)[0] == 0);
1927 case SQL_C_SSHORT:
1928 return(( *((SWORD *) colDefs[colNo].PtrDataObj)) == 0);
1929 case SQL_C_USHORT:
1930 return(( *((UWORD*) colDefs[colNo].PtrDataObj)) == 0);
1931 case SQL_C_SLONG:
1932 return(( *((SDWORD *) colDefs[colNo].PtrDataObj)) == 0);
1933 case SQL_C_ULONG:
1934 return(( *((UDWORD *) colDefs[colNo].PtrDataObj)) == 0);
1935 case SQL_C_FLOAT:
1936 return(( *((SFLOAT *) colDefs[colNo].PtrDataObj)) == 0);
1937 case SQL_C_DOUBLE:
1938 return((*((SDOUBLE *) colDefs[colNo].PtrDataObj)) == 0);
1939 case SQL_C_TIMESTAMP:
1940 TIMESTAMP_STRUCT *pDt;
1941 pDt = (TIMESTAMP_STRUCT *) colDefs[colNo].PtrDataObj;
1942 if (pDt->year == 0 && pDt->month == 0 && pDt->day == 0)
1943 return(TRUE);
1944 else
1945 return(FALSE);
1946 default:
1947 return(TRUE);
1948 }
1949*/
1950 return (colDefs[colNo].Null);
1951} // wxDbTable::IsColNull()
1952
1953
1954/********** wxDbTable::CanSelectForUpdate() **********/
1955bool wxDbTable::CanSelectForUpdate(void)
1956{
1957 if (queryOnly)
1958 return FALSE;
1959
1960 if (pDb->Dbms() == dbmsMY_SQL)
1961 return FALSE;
1962
1963 if ((pDb->Dbms() == dbmsORACLE) ||
1964 (pDb->dbInf.posStmts & SQL_PS_SELECT_FOR_UPDATE))
1965 return(TRUE);
1966 else
1967 return(FALSE);
1968
1969} // wxDbTable::CanSelectForUpdate()
1970
1971
1972/********** wxDbTable::CanUpdByROWID() **********/
1973bool wxDbTable::CanUpdByROWID(void)
1974{
1975/*
1976 * NOTE: Returning FALSE for now until this can be debugged,
1977 * as the ROWID is not getting updated correctly
1978 */
1979 return FALSE;
1980/*
1981 if (pDb->Dbms() == dbmsORACLE)
1982 return(TRUE);
1983 else
1984 return(FALSE);
1985*/
1986} // wxDbTable::CanUpdByROWID()
1987
1988
1989/********** wxDbTable::IsCursorClosedOnCommit() **********/
1990bool wxDbTable::IsCursorClosedOnCommit(void)
1991{
1992 if (pDb->dbInf.cursorCommitBehavior == SQL_CB_PRESERVE)
1993 return(FALSE);
1994 else
1995 return(TRUE);
1996
1997} // wxDbTable::IsCursorClosedOnCommit()
1998
1999
2000
2001/********** wxDbTable::ClearMemberVar() **********/
2002void wxDbTable::ClearMemberVar(UWORD colNo, bool setToNull)
2003{
2004 wxASSERT(colNo < noCols);
2005
2006 switch(colDefs[colNo].SqlCtype)
2007 {
2008 case SQL_C_CHAR:
2009 ((UCHAR FAR *) colDefs[colNo].PtrDataObj)[0] = 0;
2010 break;
2011 case SQL_C_SSHORT:
2012 *((SWORD *) colDefs[colNo].PtrDataObj) = 0;
2013 break;
2014 case SQL_C_USHORT:
2015 *((UWORD*) colDefs[colNo].PtrDataObj) = 0;
2016 break;
2017 case SQL_C_SLONG:
2018 *((SDWORD *) colDefs[colNo].PtrDataObj) = 0;
2019 break;
2020 case SQL_C_ULONG:
2021 *((UDWORD *) colDefs[colNo].PtrDataObj) = 0;
2022 break;
2023 case SQL_C_FLOAT:
2024 *((SFLOAT *) colDefs[colNo].PtrDataObj) = 0.0f;
2025 break;
2026 case SQL_C_DOUBLE:
2027 *((SDOUBLE *) colDefs[colNo].PtrDataObj) = 0.0f;
2028 break;
2029 case SQL_C_TIMESTAMP:
2030 TIMESTAMP_STRUCT *pDt;
2031 pDt = (TIMESTAMP_STRUCT *) colDefs[colNo].PtrDataObj;
2032 pDt->year = 0;
2033 pDt->month = 0;
2034 pDt->day = 0;
2035 pDt->hour = 0;
2036 pDt->minute = 0;
2037 pDt->second = 0;
2038 pDt->fraction = 0;
2039 break;
2040 }
2041
2042 if (setToNull)
2043 SetColNull(colNo);
2044} // wxDbTable::ClearMemberVar()
2045
2046
2047/********** wxDbTable::ClearMemberVars() **********/
2048void wxDbTable::ClearMemberVars(bool setToNull)
2049{
2050 int i;
2051
2052 // Loop through the columns setting each member variable to zero
2053 for (i=0; i < noCols; i++)
2054 ClearMemberVar(i,setToNull);
2055
2056} // wxDbTable::ClearMemberVars()
2057
2058
2059/********** wxDbTable::SetQueryTimeout() **********/
2060bool wxDbTable::SetQueryTimeout(UDWORD nSeconds)
2061{
2062 if (SQLSetStmtOption(hstmtInsert, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
2063 return(pDb->DispAllErrors(henv, hdbc, hstmtInsert));
2064 if (SQLSetStmtOption(hstmtUpdate, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
2065 return(pDb->DispAllErrors(henv, hdbc, hstmtUpdate));
2066 if (SQLSetStmtOption(hstmtDelete, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
2067 return(pDb->DispAllErrors(henv, hdbc, hstmtDelete));
2068 if (SQLSetStmtOption(hstmtInternal, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
2069 return(pDb->DispAllErrors(henv, hdbc, hstmtInternal));
2070
2071 // Completed Successfully
2072 return(TRUE);
2073
2074} // wxDbTable::SetQueryTimeout()
2075
2076
2077/********** wxDbTable::SetColDefs() **********/
2078void wxDbTable::SetColDefs(UWORD index, const wxString &fieldName, int dataType, void *pData,
2079 SWORD cType, int size, bool keyField, bool upd,
2080 bool insAllow, bool derivedCol)
2081{
2082 if (!colDefs) // May happen if the database connection fails
2083 return;
2084
2085 if (fieldName.Length() > (unsigned int) DB_MAX_COLUMN_NAME_LEN)
2086 {
2087 wxStrncpy(colDefs[index].ColName, fieldName, DB_MAX_COLUMN_NAME_LEN);
2088 colDefs[index].ColName[DB_MAX_COLUMN_NAME_LEN] = 0;
2089
2090#ifdef __WXDEBUG__
2091 wxString tmpMsg;
2092 tmpMsg.Printf(_T("Column name '%s' is too long. Truncated to '%s'."),
2093 fieldName.c_str(),colDefs[index].ColName);
2094 wxFAIL_MSG(tmpMsg);
2095#endif // __WXDEBUG__
2096 }
2097 else
2098 wxStrcpy(colDefs[index].ColName, fieldName);
2099
2100 colDefs[index].DbDataType = dataType;
2101 colDefs[index].PtrDataObj = pData;
2102 colDefs[index].SqlCtype = cType;
2103 colDefs[index].SzDataObj = size;
2104 colDefs[index].KeyField = keyField;
2105 colDefs[index].DerivedCol = derivedCol;
2106 // Derived columns by definition would NOT be "Insertable" or "Updateable"
2107 if (derivedCol)
2108 {
2109 colDefs[index].Updateable = FALSE;
2110 colDefs[index].InsertAllowed = FALSE;
2111 }
2112 else
2113 {
2114 colDefs[index].Updateable = upd;
2115 colDefs[index].InsertAllowed = insAllow;
2116 }
2117
2118 colDefs[index].Null = FALSE;
2119
2120} // wxDbTable::SetColDefs()
2121
2122
2123/********** wxDbTable::SetColDefs() **********/
2124wxDbColDataPtr* wxDbTable::SetColDefs(wxDbColInf *pColInfs, UWORD numCols)
2125{
2126 wxASSERT(pColInfs);
2127 wxDbColDataPtr *pColDataPtrs = NULL;
2128
2129 if (pColInfs)
2130 {
2131 UWORD index;
2132
2133 pColDataPtrs = new wxDbColDataPtr[numCols+1];
2134
2135 for (index = 0; index < numCols; index++)
2136 {
2137 // Process the fields
2138 switch (pColInfs[index].dbDataType)
2139 {
2140 case DB_DATA_TYPE_VARCHAR:
2141 pColDataPtrs[index].PtrDataObj = new wxChar[pColInfs[index].bufferLength+1];
2142 pColDataPtrs[index].SzDataObj = pColInfs[index].columnSize;
2143 pColDataPtrs[index].SqlCtype = SQL_C_CHAR;
2144 break;
2145 case DB_DATA_TYPE_INTEGER:
2146 // Can be long or short
2147 if (pColInfs[index].bufferLength == sizeof(long))
2148 {
2149 pColDataPtrs[index].PtrDataObj = new long;
2150 pColDataPtrs[index].SzDataObj = sizeof(long);
2151 pColDataPtrs[index].SqlCtype = SQL_C_SLONG;
2152 }
2153 else
2154 {
2155 pColDataPtrs[index].PtrDataObj = new short;
2156 pColDataPtrs[index].SzDataObj = sizeof(short);
2157 pColDataPtrs[index].SqlCtype = SQL_C_SSHORT;
2158 }
2159 break;
2160 case DB_DATA_TYPE_FLOAT:
2161 // Can be float or double
2162 if (pColInfs[index].bufferLength == sizeof(float))
2163 {
2164 pColDataPtrs[index].PtrDataObj = new float;
2165 pColDataPtrs[index].SzDataObj = sizeof(float);
2166 pColDataPtrs[index].SqlCtype = SQL_C_FLOAT;
2167 }
2168 else
2169 {
2170 pColDataPtrs[index].PtrDataObj = new double;
2171 pColDataPtrs[index].SzDataObj = sizeof(double);
2172 pColDataPtrs[index].SqlCtype = SQL_C_DOUBLE;
2173 }
2174 break;
2175 case DB_DATA_TYPE_DATE:
2176 pColDataPtrs[index].PtrDataObj = new TIMESTAMP_STRUCT;
2177 pColDataPtrs[index].SzDataObj = sizeof(TIMESTAMP_STRUCT);
2178 pColDataPtrs[index].SqlCtype = SQL_C_TIMESTAMP;
2179 break;
2180 case DB_DATA_TYPE_BLOB:
2181 wxFAIL_MSG(wxT("This form of ::SetColDefs() cannot be used with BLOB columns"));
2182 pColDataPtrs[index].PtrDataObj = /*BLOB ADDITION NEEDED*/NULL;
2183 pColDataPtrs[index].SzDataObj = /*BLOB ADDITION NEEDED*/sizeof(void *);
2184 pColDataPtrs[index].SqlCtype = SQL_VARBINARY;
2185 break;
2186 }
2187 if (pColDataPtrs[index].PtrDataObj != NULL)
2188 SetColDefs (index,pColInfs[index].colName,pColInfs[index].dbDataType, pColDataPtrs[index].PtrDataObj, pColDataPtrs[index].SqlCtype, pColDataPtrs[index].SzDataObj);
2189 else
2190 {
2191 // Unable to build all the column definitions, as either one of
2192 // the calls to "new" failed above, or there was a BLOB field
2193 // to have a column definition for. If BLOBs are to be used,
2194 // the other form of ::SetColDefs() must be used, as it is impossible
2195 // to know the maximum size to create the PtrDataObj to be.
2196 delete [] pColDataPtrs;
2197 return NULL;
2198 }
2199 }
2200 }
2201
2202 return (pColDataPtrs);
2203
2204} // wxDbTable::SetColDefs()
2205
2206
2207/********** wxDbTable::SetCursor() **********/
2208void wxDbTable::SetCursor(HSTMT *hstmtActivate)
2209{
2210 if (hstmtActivate == wxDB_DEFAULT_CURSOR)
2211 hstmt = *hstmtDefault;
2212 else
2213 hstmt = *hstmtActivate;
2214
2215} // wxDbTable::SetCursor()
2216
2217
2218/********** wxDbTable::Count(const wxString &) **********/
2219ULONG wxDbTable::Count(const wxString &args)
2220{
2221 ULONG count;
2222 wxString sqlStmt;
2223 SDWORD cb;
2224
2225 // Build a "SELECT COUNT(*) FROM queryTableName [WHERE whereClause]" SQL Statement
2226 sqlStmt = wxT("SELECT COUNT(");
2227 sqlStmt += args;
2228 sqlStmt += wxT(") FROM ");
2229 sqlStmt += queryTableName;
2230#if wxODBC_BACKWARD_COMPATABILITY
2231 if (from && wxStrlen(from))
2232#else
2233 if (from.Length())
2234#endif
2235 sqlStmt += from;
2236
2237 // Add the where clause if one is provided
2238#if wxODBC_BACKWARD_COMPATABILITY
2239 if (where && wxStrlen(where))
2240#else
2241 if (where.Length())
2242#endif
2243 {
2244 sqlStmt += wxT(" WHERE ");
2245 sqlStmt += where;
2246 }
2247
2248 pDb->WriteSqlLog(sqlStmt);
2249
2250 // Initialize the Count cursor if it's not already initialized
2251 if (!hstmtCount)
2252 {
2253 hstmtCount = GetNewCursor(FALSE,FALSE);
2254 wxASSERT(hstmtCount);
2255 if (!hstmtCount)
2256 return(0);
2257 }
2258
2259 // Execute the SQL statement
2260 if (SQLExecDirect(*hstmtCount, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
2261 {
2262 pDb->DispAllErrors(henv, hdbc, *hstmtCount);
2263 return(0);
2264 }
2265
2266 // Fetch the record
2267 if (SQLFetch(*hstmtCount) != SQL_SUCCESS)
2268 {
2269 pDb->DispAllErrors(henv, hdbc, *hstmtCount);
2270 return(0);
2271 }
2272
2273 // Obtain the result
2274 if (SQLGetData(*hstmtCount, (UWORD)1, SQL_C_ULONG, &count, sizeof(count), &cb) != SQL_SUCCESS)
2275 {
2276 pDb->DispAllErrors(henv, hdbc, *hstmtCount);
2277 return(0);
2278 }
2279
2280 // Free the cursor
2281 if (SQLFreeStmt(*hstmtCount, SQL_CLOSE) != SQL_SUCCESS)
2282 pDb->DispAllErrors(henv, hdbc, *hstmtCount);
2283
2284 // Return the record count
2285 return(count);
2286
2287} // wxDbTable::Count()
2288
2289
2290/********** wxDbTable::Refresh() **********/
2291bool wxDbTable::Refresh(void)
2292{
2293 bool result = TRUE;
2294
2295 // Switch to the internal cursor so any active cursors are not corrupted
2296 HSTMT currCursor = GetCursor();
2297 hstmt = hstmtInternal;
2298#if wxODBC_BACKWARD_COMPATABILITY
2299 // Save the where and order by clauses
2300 char *saveWhere = where;
2301 char *saveOrderBy = orderBy;
2302#else
2303 wxString saveWhere = where;
2304 wxString saveOrderBy = orderBy;
2305#endif
2306 // Build a where clause to refetch the record with. Try and use the
2307 // ROWID if it's available, ow use the key fields.
2308 wxString whereClause;
2309 whereClause.Empty();
2310
2311 if (CanUpdByROWID())
2312 {
2313 SDWORD cb;
2314 wxChar rowid[wxDB_ROWID_LEN+1];
2315
2316 // Get the ROWID value. If not successful retreiving the ROWID,
2317 // simply fall down through the code and build the WHERE clause
2318 // based on the key fields.
2319 if (SQLGetData(hstmt, (UWORD)(noCols+1), SQL_C_CHAR, (UCHAR*) rowid, wxDB_ROWID_LEN, &cb) == SQL_SUCCESS)
2320 {
2321 whereClause += queryTableName;
2322 whereClause += wxT(".ROWID = '");
2323 whereClause += rowid;
2324 whereClause += wxT("'");
2325 }
2326 }
2327
2328 // If unable to use the ROWID, build a where clause from the keyfields
2329 if (wxStrlen(whereClause) == 0)
2330 BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS, queryTableName);
2331
2332 // Requery the record
2333 where = whereClause;
2334 orderBy.Empty();
2335 if (!Query())
2336 result = FALSE;
2337
2338 if (result && !GetNext())
2339 result = FALSE;
2340
2341 // Switch back to original cursor
2342 SetCursor(&currCursor);
2343
2344 // Free the internal cursor
2345 if (SQLFreeStmt(hstmtInternal, SQL_CLOSE) != SQL_SUCCESS)
2346 pDb->DispAllErrors(henv, hdbc, hstmtInternal);
2347
2348 // Restore the original where and order by clauses
2349 where = saveWhere;
2350 orderBy = saveOrderBy;
2351
2352 return(result);
2353
2354} // wxDbTable::Refresh()
2355
2356
2357/********** wxDbTable::SetColNull() **********/
2358bool wxDbTable::SetColNull(UWORD colNo, bool set)
2359{
2360 if (colNo < noCols)
2361 {
2362 colDefs[colNo].Null = set;
2363 if (set) // Blank out the values in the member variable
2364 ClearMemberVar(colNo,FALSE); // Must call with FALSE, or infinite recursion will happen
2365 return(TRUE);
2366 }
2367 else
2368 return(FALSE);
2369
2370} // wxDbTable::SetColNull()
2371
2372
2373/********** wxDbTable::SetColNull() **********/
2374bool wxDbTable::SetColNull(const wxString &colName, bool set)
2375{
2376 int i;
2377 for (i = 0; i < noCols; i++)
2378 {
2379 if (!wxStricmp(colName, colDefs[i].ColName))
2380 break;
2381 }
2382
2383 if (i < noCols)
2384 {
2385 colDefs[i].Null = set;
2386 if (set) // Blank out the values in the member variable
2387 ClearMemberVar(i,FALSE); // Must call with FALSE, or infinite recursion will happen
2388 return(TRUE);
2389 }
2390 else
2391 return(FALSE);
2392
2393} // wxDbTable::SetColNull()
2394
2395
2396/********** wxDbTable::GetNewCursor() **********/
2397HSTMT *wxDbTable::GetNewCursor(bool setCursor, bool bindColumns)
2398{
2399 HSTMT *newHSTMT = new HSTMT;
2400 wxASSERT(newHSTMT);
2401 if (!newHSTMT)
2402 return(0);
2403
2404 if (SQLAllocStmt(hdbc, newHSTMT) != SQL_SUCCESS)
2405 {
2406 pDb->DispAllErrors(henv, hdbc);
2407 delete newHSTMT;
2408 return(0);
2409 }
2410
2411 if (SQLSetStmtOption(*newHSTMT, SQL_CURSOR_TYPE, cursorType) != SQL_SUCCESS)
2412 {
2413 pDb->DispAllErrors(henv, hdbc, *newHSTMT);
2414 delete newHSTMT;
2415 return(0);
2416 }
2417
2418 if (bindColumns)
2419 {
2420 if (!bindCols(*newHSTMT))
2421 {
2422 delete newHSTMT;
2423 return(0);
2424 }
2425 }
2426
2427 if (setCursor)
2428 SetCursor(newHSTMT);
2429
2430 return(newHSTMT);
2431
2432} // wxDbTable::GetNewCursor()
2433
2434
2435/********** wxDbTable::DeleteCursor() **********/
2436bool wxDbTable::DeleteCursor(HSTMT *hstmtDel)
2437{
2438 bool result = TRUE;
2439
2440 if (!hstmtDel) // Cursor already deleted
2441 return(result);
2442
2443/*
2444ODBC 3.0 says to use this form
2445 if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS)
2446
2447*/
2448 if (SQLFreeStmt(*hstmtDel, SQL_DROP) != SQL_SUCCESS)
2449 {
2450 pDb->DispAllErrors(henv, hdbc);
2451 result = FALSE;
2452 }
2453
2454 delete hstmtDel;
2455
2456 return(result);
2457
2458} // wxDbTable::DeleteCursor()
2459
2460//////////////////////////////////////////////////////////////
2461// wxDbGrid support functions
2462//////////////////////////////////////////////////////////////
2463
2464void wxDbTable::SetRowMode(const rowmode_t rowmode)
2465{
2466 if (!m_hstmtGridQuery)
2467 {
2468 m_hstmtGridQuery = GetNewCursor(FALSE,FALSE);
2469 if (!bindCols(*m_hstmtGridQuery))
2470 return;
2471 }
2472
2473 m_rowmode = rowmode;
2474 switch (m_rowmode)
2475 {
2476 case WX_ROW_MODE_QUERY:
2477 SetCursor(m_hstmtGridQuery);
2478 break;
2479 case WX_ROW_MODE_INDIVIDUAL:
2480 SetCursor(hstmtDefault);
2481 break;
2482 default:
2483 assert(0);
2484 }
2485} // wxDbTable::SetRowMode()
2486
2487
2488wxVariant wxDbTable::GetCol(const int col) const
2489{
2490 wxVariant val;
2491 if ((col < noCols) && (!IsColNull(col)))
2492 {
2493 switch (colDefs[col].SqlCtype)
2494 {
2495 case SQL_CHAR:
2496 case SQL_VARCHAR:
2497 val = (char *)(colDefs[col].PtrDataObj);
2498 break;
2499 case SQL_C_LONG:
2500 case SQL_C_SLONG:
2501 val = *(long *)(colDefs[col].PtrDataObj);
2502 break;
2503 case SQL_C_SHORT:
2504 case SQL_C_SSHORT:
2505 val = (long int )(*(short *)(colDefs[col].PtrDataObj));
2506 break;
2507 case SQL_C_ULONG:
2508 val = (long)(*(unsigned long *)(colDefs[col].PtrDataObj));
2509 break;
2510 case SQL_C_TINYINT:
2511 val = (long)(*(char *)(colDefs[col].PtrDataObj));
2512 break;
2513 case SQL_C_UTINYINT:
2514 val = (long)(*(unsigned char *)(colDefs[col].PtrDataObj));
2515 break;
2516 case SQL_C_USHORT:
2517 val = (long)(*(UWORD *)(colDefs[col].PtrDataObj));
2518 break;
2519 case SQL_C_DATE:
2520 val = (DATE_STRUCT *)(colDefs[col].PtrDataObj);
2521 break;
2522 case SQL_C_TIME:
2523 val = (TIME_STRUCT *)(colDefs[col].PtrDataObj);
2524 break;
2525 case SQL_C_TIMESTAMP:
2526 val = (TIMESTAMP_STRUCT *)(colDefs[col].PtrDataObj);
2527 break;
2528 case SQL_C_DOUBLE:
2529 val = *(double *)(colDefs[col].PtrDataObj);
2530 break;
2531 default:
2532 assert(0);
2533 }
2534 }
2535 return val;
2536} // wxDbTable::GetCol()
2537
2538
2539void csstrncpyt(char *s, const char *t, int n)
2540{
2541 while ((*s++ = *t++) && --n)
2542 {};
2543
2544 *s = '\0';
2545}
2546
2547void wxDbTable::SetCol(const int col, const wxVariant val)
2548{
2549 //FIXME: Add proper wxDateTime support to wxVariant..
2550 wxDateTime dateval;
2551
2552 SetColNull(col, val.IsNull());
2553
2554 if (!val.IsNull())
2555 {
2556 if ((colDefs[col].SqlCtype == SQL_C_DATE)
2557 || (colDefs[col].SqlCtype == SQL_C_TIME)
2558 || (colDefs[col].SqlCtype == SQL_C_TIMESTAMP))
2559 {
2560 //Returns null if invalid!
2561 if (!dateval.ParseDate(val.GetString()))
2562 SetColNull(col,TRUE);
2563 }
2564
2565 switch (colDefs[col].SqlCtype)
2566 {
2567 case SQL_CHAR:
2568 case SQL_VARCHAR:
2569 csstrncpyt((char *)(colDefs[col].PtrDataObj),
2570 val.GetString().c_str(),
2571 colDefs[col].SzDataObj-1);
2572 break;
2573 case SQL_C_LONG:
2574 case SQL_C_SLONG:
2575 *(long *)(colDefs[col].PtrDataObj) = val;
2576 break;
2577 case SQL_C_SHORT:
2578 case SQL_C_SSHORT:
2579 *(short *)(colDefs[col].PtrDataObj) = val.GetLong();
2580 break;
2581 case SQL_C_ULONG:
2582 *(unsigned long *)(colDefs[col].PtrDataObj) = val.GetLong();
2583 break;
2584 case SQL_C_TINYINT:
2585 *(char *)(colDefs[col].PtrDataObj) = val.GetChar();
2586 break;
2587 case SQL_C_UTINYINT:
2588 *(unsigned char *)(colDefs[col].PtrDataObj) = val.GetChar();
2589 break;
2590 case SQL_C_USHORT:
2591 *(unsigned short *)(colDefs[col].PtrDataObj) = val.GetLong();
2592 break;
2593 //FIXME: Add proper wxDateTime support to wxVariant..
2594 case SQL_C_DATE:
2595 {
2596 DATE_STRUCT *dataptr =
2597 (DATE_STRUCT *)colDefs[col].PtrDataObj;
2598
2599 dataptr->year = dateval.GetYear();
2600 dataptr->month = dateval.GetMonth()+1;
2601 dataptr->day = dateval.GetDay();
2602 }
2603 break;
2604 case SQL_C_TIME:
2605 {
2606 TIME_STRUCT *dataptr =
2607 (TIME_STRUCT *)colDefs[col].PtrDataObj;
2608
2609 dataptr->hour = dateval.GetHour();
2610 dataptr->minute = dateval.GetMinute();
2611 dataptr->second = dateval.GetSecond();
2612 }
2613 break;
2614 case SQL_C_TIMESTAMP:
2615 {
2616 TIMESTAMP_STRUCT *dataptr =
2617 (TIMESTAMP_STRUCT *)colDefs[col].PtrDataObj;
2618 dataptr->year = dateval.GetYear();
2619 dataptr->month = dateval.GetMonth()+1;
2620 dataptr->day = dateval.GetDay();
2621
2622 dataptr->hour = dateval.GetHour();
2623 dataptr->minute = dateval.GetMinute();
2624 dataptr->second = dateval.GetSecond();
2625 }
2626 break;
2627 case SQL_C_DOUBLE:
2628 *(double *)(colDefs[col].PtrDataObj) = val;
2629 break;
2630 default:
2631 assert(0);
2632 } // switch
2633 } // if (!val.IsNull())
2634} // wxDbTable::SetCol()
2635
2636
2637GenericKey wxDbTable::GetKey()
2638{
2639 void *blk;
2640 char *blkptr;
2641
2642 blk = malloc(m_keysize);
2643 blkptr = (char *) blk;
2644
2645 int i;
2646 for (i=0; i < noCols; i++)
2647 {
2648 if (colDefs[i].KeyField)
2649 {
2650 memcpy(blkptr,colDefs[i].PtrDataObj, colDefs[i].SzDataObj);
2651 blkptr += colDefs[i].SzDataObj;
2652 }
2653 }
2654
2655 GenericKey k = GenericKey(blk, m_keysize);
2656 free(blk);
2657
2658 return k;
2659} // wxDbTable::GetKey()
2660
2661
2662void wxDbTable::SetKey(const GenericKey& k)
2663{
2664 void *blk;
2665 char *blkptr;
2666
2667 blk = k.GetBlk();
2668 blkptr = (char *)blk;
2669
2670 int i;
2671 for (i=0; i < noCols; i++)
2672 {
2673 if (colDefs[i].KeyField)
2674 {
2675 SetColNull(i, FALSE);
2676 memcpy(colDefs[i].PtrDataObj, blkptr, colDefs[i].SzDataObj);
2677 blkptr += colDefs[i].SzDataObj;
2678 }
2679 }
2680} // wxDbTable::SetKey()
2681
2682
2683#endif // wxUSE_ODBC
2684