]> git.saurik.com Git - wxWidgets.git/blame - src/common/dbtable.cpp
removed incorrect 'static' keyword from var declarations
[wxWidgets.git] / src / common / dbtable.cpp
CommitLineData
108106cf 1///////////////////////////////////////////////////////////////////////////////
1fc5dd6f 2// Name: dbtable.cpp
f6bcfd97 3// Purpose: Implementation of the wxDbTable class.
108106cf 4// Author: Doug Card
67e9aaa3 5// Modified by: George Tasker
3ca6a5f0
BP
6// Bart Jourquin
7// Mark Johnson
108106cf
JS
8// Created: 9.96
9// RCS-ID: $Id$
10// Copyright: (c) 1996 Remstar International, Inc.
11// Licence: wxWindows licence, plus:
1e92909e 12// Notice: This class library and its intellectual design are free of charge for use,
108106cf
JS
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*/
882fc8a9
GT
27#ifdef __GNUG__
28 #pragma implementation "dbtable.h"
29#endif
108106cf 30
a2115c88
GT
31#include "wx/wxprec.h"
32
882fc8a9
GT
33#ifdef __BORLANDC__
34 #pragma hdrstop
108106cf 35#endif
108106cf 36
0b8410f3 37#ifdef DBDEBUG_CONSOLE
882fc8a9 38 #include "iostream.h"
0b8410f3
GT
39 #include "wx/ioswrap.h"
40#endif
108106cf 41
882fc8a9
GT
42#ifndef WX_PRECOMP
43 #include "wx/string.h"
44 #include "wx/object.h"
45 #include "wx/list.h"
46 #include "wx/utils.h"
d1f5f7a4
VZ
47 #if wxUSE_GUI
48 #include "wx/msgdlg.h"
49 #endif
882fc8a9 50 #include "wx/log.h"
a2115c88 51#endif
882fc8a9 52#include "wx/filefn.h"
f6bcfd97 53
a2115c88 54#if wxUSE_ODBC
108106cf
JS
55
56#include <stdio.h>
57#include <stdlib.h>
58#include <string.h>
4fdae997 59//#include <assert.h>
67e9aaa3 60
882fc8a9 61#include "wx/dbtable.h"
108106cf 62
1fc5dd6f 63#ifdef __UNIX__
108106cf
JS
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
1fc5dd6f 69# ifdef LINUX
108106cf
JS
70# include <sys/minmax.h>
71# endif
72#endif
73
a2115c88
GT
74ULONG lastTableID = 0;
75
76
e041ce57 77#ifdef __WXDEBUG__
89894079 78 wxList TablesInUse;
a2115c88
GT
79#endif
80
81
da99271d
GT
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;
a144affe
GT
96 KeyField = FALSE;
97 Updateable = FALSE;
98 InsertAllowed = FALSE;
99 DerivedCol = FALSE;
da99271d 100 CbValue = 0;
a144affe 101 Null = FALSE;
da99271d 102
a144affe 103 return TRUE;
da99271d
GT
104} // wxDbColDef::Initialize()
105
106
107/********** wxDbTable::wxDbTable() Constructor **********/
6b3f4fb8 108wxDbTable::wxDbTable(wxDb *pwxDb, const wxString &tblName, const UWORD numColumns,
4fdae997 109 const wxString &qryTblName, bool qryOnly, const wxString &tblPath)
108106cf 110{
6b3f4fb8 111 if (!initialize(pwxDb, tblName, numColumns, qryTblName, qryOnly, tblPath))
4fdae997
GT
112 cleanup();
113} // wxDbTable::wxDbTable()
114
115
116/***** DEPRECATED: use wxDbTable::wxDbTable() format above *****/
6b3f4fb8 117wxDbTable::wxDbTable(wxDb *pwxDb, const wxString &tblName, const UWORD numColumns,
4fdae997
GT
118 const wxChar *qryTblName, bool qryOnly, const wxString &tblPath)
119{
120 wxString tempQryTblName;
121 tempQryTblName = qryTblName;
6b3f4fb8 122 if (!initialize(pwxDb, tblName, numColumns, tempQryTblName, qryOnly, tblPath))
4fdae997
GT
123 cleanup();
124} // wxDbTable::wxDbTable()
125
126
127/********** wxDbTable::~wxDbTable() **********/
128wxDbTable::~wxDbTable()
129{
130 this->cleanup();
131} // wxDbTable::~wxDbTable()
132
133
6b3f4fb8 134bool wxDbTable::initialize(wxDb *pwxDb, const wxString &tblName, const UWORD numColumns,
4fdae997
GT
135 const wxString &qryTblName, bool qryOnly, const wxString &tblPath)
136{
137 // Initializing member variables
f6bcfd97 138 pDb = pwxDb; // Pointer to the wxDb object
89894079
VZ
139 henv = 0;
140 hdbc = 0;
141 hstmt = 0;
882fc8a9 142 m_hstmtGridQuery = 0;
89894079
VZ
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;
6b3f4fb8 151 noCols = numColumns; // Number of cols in the table
4fdae997
GT
152 where.Empty(); // Where clause
153 orderBy.Empty(); // Order By clause
154 from.Empty(); // From clause
a144affe 155 selectForUpdate = FALSE; // SELECT ... FOR UPDATE; Indicates whether to include the FOR UPDATE phrase
89894079 156 queryOnly = qryOnly;
a144affe 157 insertable = TRUE;
4fdae997
GT
158 tablePath.Empty();
159 tableName.Empty();
160 queryTableName.Empty();
89894079 161
4fdae997
GT
162 wxASSERT(tblName.Length());
163 wxASSERT(pDb);
89894079 164
4fdae997 165 if (!pDb)
a144affe 166 return FALSE;
4fdae997
GT
167
168 tableName = tblName; // Table Name
169 if (tblPath.Length())
170 tablePath = tblPath; // Table Path - used for dBase files
f02d4a64 171 else
4fdae997 172 tablePath.Empty();
da38429d 173
4fdae997
GT
174 if (qryTblName.Length()) // Name of the table/view to query
175 queryTableName = qryTblName;
89894079 176 else
4fdae997 177 queryTableName = tblName;
da38429d 178
f6bcfd97 179 pDb->incrementTableCount();
da38429d 180
1e92909e 181 wxString s;
89894079 182 tableID = ++lastTableID;
7d8c3dba 183 s.Printf(wxT("wxDbTable constructor (%-20s) tableID:[%6lu] pDb:[%p]"), tblName.c_str(), tableID, pDb);
9082f1a9 184
e041ce57 185#ifdef __WXDEBUG__
8128349e
GT
186 wxTablesInUse *tableInUse;
187 tableInUse = new wxTablesInUse();
89894079 188 tableInUse->tableName = tblName;
1e92909e
GT
189 tableInUse->tableID = tableID;
190 tableInUse->pDb = pDb;
89894079 191 TablesInUse.Append(tableInUse);
a2115c88 192#endif
da38429d 193
4fdae997 194 pDb->WriteSqlLog(s);
da38429d 195
f6bcfd97
BP
196 // Grab the HENV and HDBC from the wxDb object
197 henv = pDb->GetHENV();
198 hdbc = pDb->GetHDBC();
da38429d 199
89894079
VZ
200 // Allocate space for column definitions
201 if (noCols)
4fdae997 202 colDefs = new wxDbColDef[noCols]; // Points to the first column definition
da38429d 203
89894079
VZ
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);
da38429d 220
89894079
VZ
221 // Set the cursor type for the statement handles
222 cursorType = SQL_CURSOR_STATIC;
da38429d 223
89894079 224 if (SQLSetStmtOption(hstmtInternal, SQL_CURSOR_TYPE, cursorType) != SQL_SUCCESS)
da38429d 225 {
89894079
VZ
226 // Check to see if cursor type is supported
227 pDb->GetNextError(henv, hdbc, hstmtInternal);
4fdae997 228 if (! wxStrcmp(pDb->sqlState, wxT("01S02"))) // Option Value Changed
3ca6a5f0 229 {
89894079
VZ
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)
3ca6a5f0 234 pDb->DispAllErrors(henv, hdbc, hstmtInternal);
a2115c88 235#ifdef DBDEBUG_CONSOLE
4fdae997 236 cout << wxT("Static cursor changed to: ");
89894079 237 switch(cursorType)
3ca6a5f0
BP
238 {
239 case SQL_CURSOR_FORWARD_ONLY:
4fdae997 240 cout << wxT("Forward Only");
3ca6a5f0
BP
241 break;
242 case SQL_CURSOR_STATIC:
4fdae997 243 cout << wxT("Static");
3ca6a5f0
BP
244 break;
245 case SQL_CURSOR_KEYSET_DRIVEN:
4fdae997 246 cout << wxT("Keyset Driven");
3ca6a5f0
BP
247 break;
248 case SQL_CURSOR_DYNAMIC:
4fdae997 249 cout << wxT("Dynamic");
3ca6a5f0
BP
250 break;
251 }
89894079 252 cout << endl << endl;
108106cf 253#endif
3ca6a5f0
BP
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);
a144affe 263 return FALSE;
3ca6a5f0
BP
264 }
265 }
266 }
267 else
268 {
89894079
VZ
269 pDb->DispNextError();
270 pDb->DispAllErrors(henv, hdbc, hstmtInternal);
3ca6a5f0 271 }
89894079 272 }
a2115c88 273#ifdef DBDEBUG_CONSOLE
89894079 274 else
4fdae997 275 cout << wxT("Cursor Type set to STATIC") << endl << endl;
108106cf 276#endif
da38429d 277
89894079
VZ
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 }
da38429d 290
89894079 291 // Make the default cursor the active cursor
a144affe 292 hstmtDefault = GetNewCursor(FALSE,FALSE);
4fdae997 293 wxASSERT(hstmtDefault);
89894079 294 hstmt = *hstmtDefault;
108106cf 295
a144affe 296 return TRUE;
67e9aaa3 297
4fdae997
GT
298} // wxDbTable::initialize()
299
300
301void wxDbTable::cleanup()
108106cf 302{
1e92909e 303 wxString s;
89894079
VZ
304 if (pDb)
305 {
7d8c3dba 306 s.Printf(wxT("wxDbTable destructor (%-20s) tableID:[%6lu] pDb:[%p]"), tableName.c_str(), tableID, pDb);
4fdae997 307 pDb->WriteSqlLog(s);
89894079 308 }
a2115c88 309
e041ce57 310#ifdef __WXDEBUG__
89894079
VZ
311 if (tableID)
312 {
a144affe
GT
313 TablesInUse.DeleteContents(TRUE);
314 bool found = FALSE;
1e92909e 315
0b8410f3 316 wxNode *pNode;
89894079
VZ
317 pNode = TablesInUse.First();
318 while (pNode && !found)
319 {
8128349e 320 if (((wxTablesInUse *)pNode->Data())->tableID == tableID)
89894079 321 {
a144affe 322 found = TRUE;
89894079 323 if (!TablesInUse.DeleteNode(pNode))
4fdae997 324 wxLogDebug (s,wxT("Unable to delete node!"));
89894079
VZ
325 }
326 else
327 pNode = pNode->Next();
328 }
329 if (!found)
330 {
1e92909e 331 wxString msg;
597fadce 332 msg.Printf(wxT("Unable to find the tableID in the linked\nlist of tables in use.\n\n%s"),s.c_str());
4fdae997 333 wxLogDebug (msg,wxT("NOTICE..."));
89894079
VZ
334 }
335 }
a2115c88 336#endif
e041ce57 337
f6bcfd97 338 // Decrement the wxDb table count
89894079 339 if (pDb)
f6bcfd97 340 pDb->decrementTableCount();
89894079
VZ
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)
7d8c3dba
GT
350 {
351/*
352ODBC 3.0 says to use this form
353 if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS)
354*/
89894079
VZ
355 if (SQLFreeStmt(hstmtInsert, SQL_DROP) != SQL_SUCCESS)
356 pDb->DispAllErrors(henv, hdbc);
7d8c3dba 357 }
7c5c05ae 358
89894079 359 if (hstmtDelete)
7d8c3dba
GT
360 {
361/*
362ODBC 3.0 says to use this form
363 if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS)
364*/
89894079 365 if (SQLFreeStmt(hstmtDelete, SQL_DROP) != SQL_SUCCESS)
7d8c3dba
GT
366 pDb->DispAllErrors(henv, hdbc);
367 }
7c5c05ae 368
89894079 369 if (hstmtUpdate)
7d8c3dba
GT
370 {
371/*
372ODBC 3.0 says to use this form
373 if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS)
374*/
89894079
VZ
375 if (SQLFreeStmt(hstmtUpdate, SQL_DROP) != SQL_SUCCESS)
376 pDb->DispAllErrors(henv, hdbc);
7d8c3dba 377 }
89894079 378 }
3ca6a5f0 379
89894079 380 if (hstmtInternal)
7d8c3dba 381 {
89894079
VZ
382 if (SQLFreeStmt(hstmtInternal, SQL_DROP) != SQL_SUCCESS)
383 pDb->DispAllErrors(henv, hdbc);
7d8c3dba 384 }
89894079
VZ
385
386 // Delete dynamically allocated cursors
387 if (hstmtDefault)
388 DeleteCursor(hstmtDefault);
7c5c05ae 389
89894079
VZ
390 if (hstmtCount)
391 DeleteCursor(hstmtCount);
882fc8a9
GT
392
393 if (m_hstmtGridQuery)
394 DeleteCursor(m_hstmtGridQuery);
395
4fdae997 396} // wxDbTable::cleanup()
67e9aaa3
GT
397
398
6919c53f
GT
399/***************************** PRIVATE FUNCTIONS *****************************/
400
67e9aaa3 401
2beca662 402/********** wxDbTable::bindParams() **********/
4fdae997 403bool wxDbTable::bindParams(bool forUpdate)
6919c53f 404{
4fdae997 405 wxASSERT(!queryOnly);
89894079 406 if (queryOnly)
a144affe 407 return(FALSE);
da38429d 408
89894079
VZ
409 SWORD fSqlType = 0;
410 UDWORD precision = 0;
411 SWORD scale = 0;
da38429d 412
4fdae997
GT
413 // Bind each column of the table that should be bound
414 // to a parameter marker
6b3f4fb8 415 int i;
2beca662
GT
416 UWORD colNo;
417
418 for (i=0, colNo=1; i < noCols; i++)
89894079 419 {
4fdae997 420 if (forUpdate)
89894079 421 {
2beca662 422 if (!colDefs[i].Updateable)
4fdae997 423 continue;
89894079 424 }
4fdae997 425 else
3ca6a5f0 426 {
2beca662 427 if (!colDefs[i].InsertAllowed)
4fdae997 428 continue;
3ca6a5f0 429 }
6919c53f 430
89894079
VZ
431 switch(colDefs[i].DbDataType)
432 {
3ca6a5f0
BP
433 case DB_DATA_TYPE_VARCHAR:
434 fSqlType = pDb->GetTypeInfVarchar().FsqlType;
435 precision = colDefs[i].SzDataObj;
436 scale = 0;
f02d4a64
GT
437 if (colDefs[i].Null)
438 colDefs[i].CbValue = SQL_NULL_DATA;
439 else
440 colDefs[i].CbValue = SQL_NTS;
3ca6a5f0
BP
441 break;
442 case DB_DATA_TYPE_INTEGER:
443 fSqlType = pDb->GetTypeInfInteger().FsqlType;
444 precision = pDb->GetTypeInfInteger().Precision;
445 scale = 0;
f02d4a64
GT
446 if (colDefs[i].Null)
447 colDefs[i].CbValue = SQL_NULL_DATA;
448 else
449 colDefs[i].CbValue = 0;
3ca6a5f0
BP
450 break;
451 case DB_DATA_TYPE_FLOAT:
452 fSqlType = pDb->GetTypeInfFloat().FsqlType;
453 precision = pDb->GetTypeInfFloat().Precision;
da38429d 454 scale = pDb->GetTypeInfFloat().MaximumScale;
3ca6a5f0
BP
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;
f02d4a64
GT
460 if (colDefs[i].Null)
461 colDefs[i].CbValue = SQL_NULL_DATA;
462 else
463 colDefs[i].CbValue = 0;
3ca6a5f0
BP
464 break;
465 case DB_DATA_TYPE_DATE:
466 fSqlType = pDb->GetTypeInfDate().FsqlType;
467 precision = pDb->GetTypeInfDate().Precision;
468 scale = 0;
f02d4a64
GT
469 if (colDefs[i].Null)
470 colDefs[i].CbValue = SQL_NULL_DATA;
471 else
472 colDefs[i].CbValue = 0;
3ca6a5f0 473 break;
bf5423ea
GT
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;
3ca6a5f0 483 }
4fdae997
GT
484 if (forUpdate)
485 {
486 if (SQLBindParameter(hstmtUpdate, colNo++, SQL_PARAM_INPUT, colDefs[i].SqlCtype,
da38429d 487 fSqlType, precision, scale, (UCHAR*) colDefs[i].PtrDataObj,
4fdae997
GT
488 precision+1, &colDefs[i].CbValue) != SQL_SUCCESS)
489 {
490 return(pDb->DispAllErrors(henv, hdbc, hstmtUpdate));
491 }
492 }
493 else
3ca6a5f0 494 {
4fdae997 495 if (SQLBindParameter(hstmtInsert, colNo++, SQL_PARAM_INPUT, colDefs[i].SqlCtype,
da38429d 496 fSqlType, precision, scale, (UCHAR*) colDefs[i].PtrDataObj,
4fdae997
GT
497 precision+1,&colDefs[i].CbValue) != SQL_SUCCESS)
498 {
499 return(pDb->DispAllErrors(henv, hdbc, hstmtInsert));
500 }
89894079 501 }
89894079 502 }
da38429d 503
89894079 504 // Completed successfully
a144affe 505 return(TRUE);
6919c53f 506
4fdae997
GT
507} // wxDbTable::bindParams()
508
509
510/********** wxDbTable::bindInsertParams() **********/
511bool wxDbTable::bindInsertParams(void)
512{
a144affe 513 return bindParams(FALSE);
4fdae997
GT
514} // wxDbTable::bindInsertParams()
515
516
517/********** wxDbTable::bindUpdateParams() **********/
518bool wxDbTable::bindUpdateParams(void)
519{
a144affe 520 return bindParams(TRUE);
f6bcfd97 521} // wxDbTable::bindUpdateParams()
6919c53f 522
67e9aaa3 523
f6bcfd97
BP
524/********** wxDbTable::bindCols() **********/
525bool wxDbTable::bindCols(HSTMT cursor)
6919c53f 526{
89894079 527 // Bind each column of the table to a memory address for fetching data
6b3f4fb8 528 UWORD i;
89894079 529 for (i = 0; i < noCols; i++)
3ca6a5f0 530 {
6b3f4fb8 531 if (SQLBindCol(cursor, (UWORD)(i+1), colDefs[i].SqlCtype, (UCHAR*) colDefs[i].PtrDataObj,
f02d4a64 532 colDefs[i].SzDataObj, &colDefs[i].CbValue ) != SQL_SUCCESS)
3ca6a5f0
BP
533 {
534 return (pDb->DispAllErrors(henv, hdbc, cursor));
535 }
536 }
89894079
VZ
537
538 // Completed successfully
a144affe 539 return(TRUE);
6919c53f 540
f6bcfd97 541} // wxDbTable::bindCols()
6919c53f 542
67e9aaa3 543
f6bcfd97
BP
544/********** wxDbTable::getRec() **********/
545bool wxDbTable::getRec(UWORD fetchType)
6919c53f 546{
89894079
VZ
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);
5a226de0 556 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
3ca6a5f0 557 {
89894079 558 if (retcode == SQL_NO_DATA_FOUND)
a144affe 559 return(FALSE);
89894079
VZ
560 else
561 return(pDb->DispAllErrors(henv, hdbc, hstmt));
3ca6a5f0 562 }
f02d4a64
GT
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 }
89894079
VZ
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)
a144affe 579 return(FALSE);
89894079
VZ
580 else
581 return(pDb->DispAllErrors(henv, hdbc, hstmt));
582 }
f02d4a64
GT
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 }
89894079
VZ
591 }
592
593 // Completed successfully
a144affe 594 return(TRUE);
6919c53f 595
f6bcfd97 596} // wxDbTable::getRec()
6919c53f 597
67e9aaa3 598
f6bcfd97 599/********** wxDbTable::execDelete() **********/
4fdae997 600bool wxDbTable::execDelete(const wxString &pSqlStmt)
6919c53f 601{
2beca662
GT
602 RETCODE retcode;
603
89894079 604 // Execute the DELETE statement
2beca662 605 retcode = SQLExecDirect(hstmtDelete, (UCHAR FAR *) pSqlStmt.c_str(), SQL_NTS);
6919c53f 606
2beca662
GT
607 if (retcode == SQL_SUCCESS ||
608 retcode == SQL_NO_DATA_FOUND ||
609 retcode == SQL_SUCCESS_WITH_INFO)
610 {
611 // Record deleted successfully
a144affe 612 return(TRUE);
2beca662
GT
613 }
614
615 // Problem deleting record
616 return(pDb->DispAllErrors(henv, hdbc, hstmtDelete));
6919c53f 617
f6bcfd97 618} // wxDbTable::execDelete()
6919c53f 619
67e9aaa3 620
f6bcfd97 621/********** wxDbTable::execUpdate() **********/
4fdae997 622bool wxDbTable::execUpdate(const wxString &pSqlStmt)
6919c53f 623{
2beca662
GT
624 RETCODE retcode;
625
89894079 626 // Execute the UPDATE statement
2beca662 627 retcode = SQLExecDirect(hstmtUpdate, (UCHAR FAR *) pSqlStmt.c_str(), SQL_NTS);
6919c53f 628
2beca662
GT
629 if (retcode == SQL_SUCCESS ||
630 retcode == SQL_NO_DATA_FOUND ||
631 retcode == SQL_SUCCESS_WITH_INFO)
632 {
633 // Record updated successfully
a144affe 634 return(TRUE);
2beca662
GT
635 }
636
637 // Problem updating record
638 return(pDb->DispAllErrors(henv, hdbc, hstmtUpdate));
6919c53f 639
f6bcfd97 640} // wxDbTable::execUpdate()
6919c53f 641
67e9aaa3 642
f6bcfd97 643/********** wxDbTable::query() **********/
4fdae997 644bool wxDbTable::query(int queryType, bool forUpdate, bool distinct, const wxString &pSqlStmt)
6919c53f 645{
4fdae997 646 wxString sqlStmt;
6919c53f 647
89894079
VZ
648 if (forUpdate)
649 // The user may wish to select for update, but the DBMS may not be capable
650 selectForUpdate = CanSelectForUpdate();
651 else
a144affe 652 selectForUpdate = FALSE;
6919c53f 653
89894079
VZ
654 // Set the SQL SELECT string
655 if (queryType != DB_SELECT_STATEMENT) // A select statement was not passed in,
3ca6a5f0 656 { // so generate a select statement.
f6bcfd97 657 BuildSelectStmt(sqlStmt, queryType, distinct);
89894079 658 pDb->WriteSqlLog(sqlStmt);
3ca6a5f0 659 }
e93a3a18 660
89894079 661 // Make sure the cursor is closed first
e93a3a18 662 if (!CloseCursor(hstmt))
a144affe 663 return(FALSE);
6919c53f 664
89894079 665 // Execute the SQL SELECT statement
da38429d 666 int retcode;
4fdae997 667 retcode = SQLExecDirect(hstmt, (UCHAR FAR *) (queryType == DB_SELECT_STATEMENT ? pSqlStmt.c_str() : sqlStmt.c_str()), SQL_NTS);
89894079 668 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
3ca6a5f0 669 return(pDb->DispAllErrors(henv, hdbc, hstmt));
6919c53f 670
89894079 671 // Completed successfully
a144affe 672 return(TRUE);
6919c53f 673
f6bcfd97 674} // wxDbTable::query()
6919c53f
GT
675
676
67e9aaa3
GT
677/***************************** PUBLIC FUNCTIONS *****************************/
678
6919c53f 679
f6bcfd97 680/********** wxDbTable::Open() **********/
1454d4e6 681bool wxDbTable::Open(bool checkPrivileges, bool checkTableExists)
108106cf 682{
89894079 683 if (!pDb)
da38429d 684 return FALSE;
3ca6a5f0 685
89894079 686 int i;
1e92909e 687 wxString sqlStmt;
f02d4a64 688 wxString s;
882fc8a9
GT
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 }
89894079 702
4fdae997 703 s.Empty();
89894079 704 // Verify that the table exists in the database
6b3f4fb8 705 if (checkTableExists && !pDb->TableExists(tableName, pDb->GetUsername(), tablePath))
89894079 706 {
4fdae997
GT
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");
e16143f6 710 else
4fdae997 711 s += wxT(".\n");
f02d4a64
GT
712 }
713 else if (checkPrivileges)
714 {
715 // Verify the user has rights to access the table.
da38429d 716 // Shortcut boolean evaluation to optimize out call to
f02d4a64
GT
717 // TablePrivileges
718 //
719 // Unfortunately this optimization doesn't seem to be
720 // reliable!
da38429d 721 if (// *(pDb->dbInf.accessibleTables) == 'N' &&
6b3f4fb8 722 !pDb->TablePrivileges(tableName,wxT("SELECT"), pDb->GetUsername(), pDb->GetUsername(), tablePath))
4fdae997 723 s = wxT("Current logged in user does not have sufficient privileges to access this table.\n");
f02d4a64
GT
724 }
725
726 if (!s.IsEmpty())
727 {
728 wxString p;
729
4fdae997 730 if (!tablePath.IsEmpty())
7d8c3dba 731 p.Printf(wxT("Error opening '%s/%s'.\n"),tablePath.c_str(),tableName.c_str());
e16143f6 732 else
7d8c3dba 733 p.Printf(wxT("Error opening '%s'.\n"), tableName.c_str());
f02d4a64
GT
734
735 p += s;
736 pDb->LogError(p.GetData());
737
a144affe 738 return(FALSE);
89894079
VZ
739 }
740
741 // Bind the member variables for field exchange between
f6bcfd97 742 // the wxDbTable object and the ODBC record.
89894079
VZ
743 if (!queryOnly)
744 {
745 if (!bindInsertParams()) // Inserts
a144affe 746 return(FALSE);
da38429d 747
89894079 748 if (!bindUpdateParams()) // Updates
a144affe 749 return(FALSE);
89894079 750 }
3ca6a5f0 751
89894079 752 if (!bindCols(*hstmtDefault)) // Selects
a144affe 753 return(FALSE);
da38429d 754
89894079 755 if (!bindCols(hstmtInternal)) // Internal use only
a144affe 756 return(FALSE);
f02d4a64
GT
757
758 /*
89894079
VZ
759 * Do NOT bind the hstmtCount cursor!!!
760 */
761
762 // Build an insert statement using parameter markers
763 if (!queryOnly && noCols > 0)
764 {
a144affe 765 bool needComma = FALSE;
7d8c3dba 766 sqlStmt.Printf(wxT("INSERT INTO %s ("), tableName.c_str());
89894079
VZ
767 for (i = 0; i < noCols; i++)
768 {
769 if (! colDefs[i].InsertAllowed)
770 continue;
771 if (needComma)
4fdae997 772 sqlStmt += wxT(",");
1e92909e 773 sqlStmt += colDefs[i].ColName;
a144affe 774 needComma = TRUE;
89894079 775 }
a144affe 776 needComma = FALSE;
4fdae997 777 sqlStmt += wxT(") VALUES (");
f6bcfd97 778
3ca6a5f0 779 int insertableCount = 0;
f6bcfd97 780
89894079
VZ
781 for (i = 0; i < noCols; i++)
782 {
783 if (! colDefs[i].InsertAllowed)
784 continue;
785 if (needComma)
4fdae997
GT
786 sqlStmt += wxT(",");
787 sqlStmt += wxT("?");
a144affe 788 needComma = TRUE;
3ca6a5f0 789 insertableCount++;
89894079 790 }
4fdae997 791 sqlStmt += wxT(")");
da38429d 792
89894079 793 // Prepare the insert statement for execution
da38429d 794 if (insertableCount)
3ca6a5f0 795 {
f6bcfd97
BP
796 if (SQLPrepare(hstmtInsert, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
797 return(pDb->DispAllErrors(henv, hdbc, hstmtInsert));
798 }
da38429d 799 else
a144affe 800 insertable= FALSE;
89894079 801 }
da38429d 802
89894079 803 // Completed successfully
a144affe 804 return(TRUE);
108106cf 805
f6bcfd97 806} // wxDbTable::Open()
108106cf 807
67e9aaa3 808
f6bcfd97
BP
809/********** wxDbTable::Query() **********/
810bool wxDbTable::Query(bool forUpdate, bool distinct)
108106cf
JS
811{
812
89894079 813 return(query(DB_SELECT_WHERE, forUpdate, distinct));
108106cf 814
f6bcfd97 815} // wxDbTable::Query()
108106cf 816
67e9aaa3 817
f6bcfd97 818/********** wxDbTable::QueryBySqlStmt() **********/
4fdae997 819bool wxDbTable::QueryBySqlStmt(const wxString &pSqlStmt)
108106cf 820{
89894079 821 pDb->WriteSqlLog(pSqlStmt);
108106cf 822
a144affe 823 return(query(DB_SELECT_STATEMENT, FALSE, FALSE, pSqlStmt));
108106cf 824
f6bcfd97 825} // wxDbTable::QueryBySqlStmt()
108106cf 826
67e9aaa3 827
f6bcfd97
BP
828/********** wxDbTable::QueryMatching() **********/
829bool wxDbTable::QueryMatching(bool forUpdate, bool distinct)
108106cf
JS
830{
831
89894079 832 return(query(DB_SELECT_MATCHING, forUpdate, distinct));
108106cf 833
f6bcfd97 834} // wxDbTable::QueryMatching()
108106cf 835
67e9aaa3 836
f6bcfd97
BP
837/********** wxDbTable::QueryOnKeyFields() **********/
838bool wxDbTable::QueryOnKeyFields(bool forUpdate, bool distinct)
108106cf
JS
839{
840
89894079 841 return(query(DB_SELECT_KEYFIELDS, forUpdate, distinct));
108106cf 842
f6bcfd97 843} // wxDbTable::QueryOnKeyFields()
108106cf 844
67e9aaa3 845
f6bcfd97
BP
846/********** wxDbTable::GetPrev() **********/
847bool wxDbTable::GetPrev(void)
a3439c7d 848{
89894079
VZ
849 if (pDb->FwdOnlyCursors())
850 {
f6bcfd97 851 wxFAIL_MSG(wxT("GetPrev()::Backward scrolling cursors are not enabled for this instance of wxDbTable"));
a144affe 852 return FALSE;
89894079
VZ
853 }
854 else
855 return(getRec(SQL_FETCH_PRIOR));
3ca6a5f0 856
f6bcfd97 857} // wxDbTable::GetPrev()
a3439c7d 858
67e9aaa3 859
f6bcfd97
BP
860/********** wxDbTable::operator-- **********/
861bool wxDbTable::operator--(int)
a3439c7d 862{
89894079
VZ
863 if (pDb->FwdOnlyCursors())
864 {
f6bcfd97 865 wxFAIL_MSG(wxT("operator--:Backward scrolling cursors are not enabled for this instance of wxDbTable"));
a144affe 866 return FALSE;
89894079
VZ
867 }
868 else
869 return(getRec(SQL_FETCH_PRIOR));
3ca6a5f0 870
f6bcfd97 871} // wxDbTable::operator--
a3439c7d 872
67e9aaa3 873
f6bcfd97
BP
874/********** wxDbTable::GetFirst() **********/
875bool wxDbTable::GetFirst(void)
a3439c7d 876{
89894079
VZ
877 if (pDb->FwdOnlyCursors())
878 {
f6bcfd97 879 wxFAIL_MSG(wxT("GetFirst():Backward scrolling cursors are not enabled for this instance of wxDbTable"));
a144affe 880 return FALSE;
89894079
VZ
881 }
882 else
883 return(getRec(SQL_FETCH_FIRST));
3ca6a5f0 884
f6bcfd97 885} // wxDbTable::GetFirst()
a3439c7d 886
67e9aaa3 887
f6bcfd97
BP
888/********** wxDbTable::GetLast() **********/
889bool wxDbTable::GetLast(void)
a3439c7d 890{
89894079
VZ
891 if (pDb->FwdOnlyCursors())
892 {
f6bcfd97 893 wxFAIL_MSG(wxT("GetLast()::Backward scrolling cursors are not enabled for this instance of wxDbTable"));
a144affe 894 return FALSE;
89894079 895 }
da38429d 896 else
89894079 897 return(getRec(SQL_FETCH_LAST));
3ca6a5f0 898
f6bcfd97 899} // wxDbTable::GetLast()
a3439c7d 900
67e9aaa3 901
4fdae997
GT
902/********** wxDbTable::BuildDeleteStmt() **********/
903void wxDbTable::BuildDeleteStmt(wxString &pSqlStmt, int typeOfDel, const wxString &pWhereClause)
108106cf 904{
4fdae997
GT
905 wxASSERT(!queryOnly);
906 if (queryOnly)
907 return;
908
909 wxString whereClause;
89894079 910
4fdae997
GT
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 {
7d8c3dba 917 pSqlStmt.Printf(wxT("DELETE FROM %s"), tableName.c_str());
4fdae997
GT
918 return;
919 }
920
7d8c3dba 921 pSqlStmt.Printf(wxT("DELETE FROM %s WHERE "), tableName.c_str());
4fdae997
GT
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.
6b3f4fb8 938 if (SQLGetData(hstmt, (UWORD)(noCols+1), SQL_C_CHAR, (UCHAR*) rowid, wxDB_ROWID_LEN, &cb) == SQL_SUCCESS)
4fdae997
GT
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();
89894079
VZ
977
978 // Build a select statement to query the database
4fdae997 979 pSqlStmt = wxT("SELECT ");
89894079
VZ
980
981 // SELECT DISTINCT values only?
982 if (distinct)
4fdae997 983 pSqlStmt += wxT("DISTINCT ");
89894079
VZ
984
985 // Was a FROM clause specified to join tables to the base table?
986 // Available for ::Query() only!!!
a144affe 987 bool appendFromClause = FALSE;
f6bcfd97 988#if wxODBC_BACKWARD_COMPATABILITY
89894079 989 if (typeOfSelect == DB_SELECT_WHERE && from && wxStrlen(from))
a144affe 990 appendFromClause = TRUE;
f6bcfd97
BP
991#else
992 if (typeOfSelect == DB_SELECT_WHERE && from.Length())
a144affe 993 appendFromClause = TRUE;
f6bcfd97 994#endif
89894079
VZ
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
2beca662 1001 if (appendFromClause || pDb->Dbms() == dbmsACCESS)
89894079 1002 {
4fdae997
GT
1003 pSqlStmt += queryTableName;
1004 pSqlStmt += wxT(".");
89894079 1005 }
4fdae997 1006 pSqlStmt += colDefs[i].ColName;
89894079 1007 if (i + 1 < noCols)
4fdae997 1008 pSqlStmt += wxT(",");
89894079
VZ
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
2beca662 1016 if (appendFromClause || pDb->Dbms() == dbmsACCESS)
89894079 1017 {
4fdae997
GT
1018 pSqlStmt += wxT(",");
1019 pSqlStmt += queryTableName;
1020 pSqlStmt += wxT(".ROWID");
89894079
VZ
1021 }
1022 else
4fdae997 1023 pSqlStmt += wxT(",ROWID");
89894079
VZ
1024 }
1025
1026 // Append the FROM tablename portion
4fdae997
GT
1027 pSqlStmt += wxT(" FROM ");
1028 pSqlStmt += queryTableName;
89894079
VZ
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))
4fdae997 1036 pSqlStmt += wxT(" HOLDLOCK");
89894079
VZ
1037
1038 if (appendFromClause)
4fdae997 1039 pSqlStmt += from;
89894079
VZ
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 {
3ca6a5f0 1045 case DB_SELECT_WHERE:
f6bcfd97 1046#if wxODBC_BACKWARD_COMPATABILITY
3ca6a5f0 1047 if (where && wxStrlen(where)) // May not want a where clause!!!
f6bcfd97 1048#else
3ca6a5f0 1049 if (where.Length()) // May not want a where clause!!!
f6bcfd97 1050#endif
3ca6a5f0 1051 {
4fdae997
GT
1052 pSqlStmt += wxT(" WHERE ");
1053 pSqlStmt += where;
3ca6a5f0
BP
1054 }
1055 break;
1056 case DB_SELECT_KEYFIELDS:
1057 BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS);
4fdae997 1058 if (whereClause.Length())
3ca6a5f0 1059 {
4fdae997
GT
1060 pSqlStmt += wxT(" WHERE ");
1061 pSqlStmt += whereClause;
3ca6a5f0
BP
1062 }
1063 break;
1064 case DB_SELECT_MATCHING:
1065 BuildWhereClause(whereClause, DB_WHERE_MATCHING);
4fdae997 1066 if (whereClause.Length())
3ca6a5f0 1067 {
4fdae997
GT
1068 pSqlStmt += wxT(" WHERE ");
1069 pSqlStmt += whereClause;
3ca6a5f0
BP
1070 }
1071 break;
89894079
VZ
1072 }
1073
1074 // Append the ORDER BY clause
f6bcfd97 1075#if wxODBC_BACKWARD_COMPATABILITY
89894079 1076 if (orderBy && wxStrlen(orderBy))
f6bcfd97 1077#else
3ca6a5f0 1078 if (orderBy.Length())
f6bcfd97 1079#endif
89894079 1080 {
4fdae997
GT
1081 pSqlStmt += wxT(" ORDER BY ");
1082 pSqlStmt += orderBy;
89894079
VZ
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())
4fdae997
GT
1089 pSqlStmt += wxT(" FOR UPDATE");
1090
1091} // wxDbTable::BuildSelectStmt()
1092
108106cf 1093
4fdae997
GT
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);
f6bcfd97 1100} // wxDbTable::BuildSelectStmt()
108106cf 1101
67e9aaa3 1102
4fdae997
GT
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
a144affe 1113 bool firstColumn = TRUE;
4fdae997 1114
2beca662 1115 pSqlStmt.Printf(wxT("UPDATE %s SET "), tableName.Upper().c_str());
4fdae997
GT
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 {
9082f1a9 1124 if (!firstColumn)
4fdae997
GT
1125 pSqlStmt += wxT(",");
1126 else
a144affe 1127 firstColumn = FALSE;
4fdae997
GT
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.
6b3f4fb8 1149 if (SQLGetData(hstmt, (UWORD)(noCols+1), SQL_C_CHAR, (UCHAR*) rowid, wxDB_ROWID_LEN, &cb) == SQL_SUCCESS)
4fdae997
GT
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{
a144affe 1186 bool moreThanOneColumn = FALSE;
4fdae997
GT
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
a144affe 1204 moreThanOneColumn = TRUE;
4fdae997
GT
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
f6bcfd97
BP
1256/********** wxDbTable::GetRowNum() **********/
1257UWORD wxDbTable::GetRowNum(void)
108106cf 1258{
89894079 1259 UDWORD rowNum;
108106cf 1260
89894079
VZ
1261 if (SQLGetStmtOption(hstmt, SQL_ROW_NUMBER, (UCHAR*) &rowNum) != SQL_SUCCESS)
1262 {
1263 pDb->DispAllErrors(henv, hdbc, hstmt);
1264 return(0);
1265 }
108106cf 1266
89894079
VZ
1267 // Completed successfully
1268 return((UWORD) rowNum);
108106cf 1269
f6bcfd97 1270} // wxDbTable::GetRowNum()
108106cf 1271
67e9aaa3 1272
f6bcfd97
BP
1273/********** wxDbTable::CloseCursor() **********/
1274bool wxDbTable::CloseCursor(HSTMT cursor)
108106cf 1275{
89894079
VZ
1276 if (SQLFreeStmt(cursor, SQL_CLOSE) != SQL_SUCCESS)
1277 return(pDb->DispAllErrors(henv, hdbc, cursor));
108106cf 1278
89894079 1279 // Completed successfully
a144affe 1280 return(TRUE);
108106cf 1281
f6bcfd97 1282} // wxDbTable::CloseCursor()
108106cf 1283
67e9aaa3 1284
f6bcfd97
BP
1285/********** wxDbTable::CreateTable() **********/
1286bool wxDbTable::CreateTable(bool attemptDrop)
108106cf 1287{
89894079 1288 if (!pDb)
a144affe 1289 return FALSE;
1fc5dd6f 1290
89894079 1291 int i, j;
1e92909e 1292 wxString sqlStmt;
108106cf 1293
a2115c88 1294#ifdef DBDEBUG_CONSOLE
4fdae997 1295 cout << wxT("Creating Table ") << tableName << wxT("...") << endl;
108106cf
JS
1296#endif
1297
89894079
VZ
1298 // Drop table first
1299 if (attemptDrop && !DropTable())
a144affe 1300 return FALSE;
108106cf 1301
89894079 1302 // Create the table
a2115c88 1303#ifdef DBDEBUG_CONSOLE
89894079
VZ
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;
4fdae997 1309 cout << i + 1 << wxT(": ") << colDefs[i].ColName << wxT("; ");
89894079
VZ
1310 switch(colDefs[i].DbDataType)
1311 {
1312 case DB_DATA_TYPE_VARCHAR:
882fc8a9 1313 cout << pDb->GetTypeInfVarchar().TypeName << wxT("(") << colDefs[i].SzDataObj << wxT(")");
89894079
VZ
1314 break;
1315 case DB_DATA_TYPE_INTEGER:
882fc8a9 1316 cout << pDb->GetTypeInfInteger().TypeName;
89894079
VZ
1317 break;
1318 case DB_DATA_TYPE_FLOAT:
882fc8a9 1319 cout << pDb->GetTypeInfFloat().TypeName;
89894079
VZ
1320 break;
1321 case DB_DATA_TYPE_DATE:
882fc8a9 1322 cout << pDb->GetTypeInfDate().TypeName;
89894079 1323 break;
bf5423ea 1324 case DB_DATA_TYPE_BLOB:
882fc8a9 1325 cout << pDb->GetTypeInfBlob().TypeName;
bf5423ea 1326 break;
89894079
VZ
1327 }
1328 cout << endl;
1329 }
108106cf
JS
1330#endif
1331
89894079 1332 // Build a CREATE TABLE string from the colDefs structure.
a144affe 1333 bool needComma = FALSE;
7d8c3dba 1334 sqlStmt.Printf(wxT("CREATE TABLE %s ("), tableName.c_str());
1e92909e 1335
89894079
VZ
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)
4fdae997 1343 sqlStmt += wxT(",");
89894079 1344 // Column Name
1e92909e 1345 sqlStmt += colDefs[i].ColName;
4fdae997 1346 sqlStmt += wxT(" ");
89894079
VZ
1347 // Column Type
1348 switch(colDefs[i].DbDataType)
1349 {
1350 case DB_DATA_TYPE_VARCHAR:
3ca6a5f0
BP
1351 sqlStmt += pDb->GetTypeInfVarchar().TypeName;
1352 break;
89894079 1353 case DB_DATA_TYPE_INTEGER:
3ca6a5f0
BP
1354 sqlStmt += pDb->GetTypeInfInteger().TypeName;
1355 break;
89894079 1356 case DB_DATA_TYPE_FLOAT:
3ca6a5f0
BP
1357 sqlStmt += pDb->GetTypeInfFloat().TypeName;
1358 break;
89894079 1359 case DB_DATA_TYPE_DATE:
3ca6a5f0
BP
1360 sqlStmt += pDb->GetTypeInfDate().TypeName;
1361 break;
bf5423ea
GT
1362 case DB_DATA_TYPE_BLOB:
1363 sqlStmt += pDb->GetTypeInfBlob().TypeName;
1364 break;
89894079
VZ
1365 }
1366 // For varchars, append the size of the string
bf5423ea
GT
1367 if (colDefs[i].DbDataType == DB_DATA_TYPE_VARCHAR)// ||
1368// colDefs[i].DbDataType == DB_DATA_TYPE_BLOB)
89894079 1369 {
1e92909e 1370 wxString s;
4fdae997
GT
1371 s.Printf(wxT("(%d)"), colDefs[i].SzDataObj);
1372 sqlStmt += s;
89894079
VZ
1373 }
1374
e93a3a18
GT
1375 if (pDb->Dbms() == dbmsDB2 ||
1376 pDb->Dbms() == dbmsMY_SQL ||
1377 pDb->Dbms() == dbmsSYBASE_ASE ||
9082f1a9 1378 pDb->Dbms() == dbmsINTERBASE ||
e93a3a18 1379 pDb->Dbms() == dbmsMS_SQL_SERVER)
89894079
VZ
1380 {
1381 if (colDefs[i].KeyField)
1382 {
4fdae997 1383 sqlStmt += wxT(" NOT NULL");
89894079
VZ
1384 }
1385 }
da38429d 1386
a144affe 1387 needComma = TRUE;
89894079
VZ
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 {
87cc3456 1400 switch (pDb->Dbms())
89894079 1401 {
597fadce 1402 case dbmsINFORMIX:
87cc3456
GT
1403 case dbmsSYBASE_ASA:
1404 case dbmsSYBASE_ASE:
1405 case dbmsMY_SQL:
1406 {
2beca662 1407 // MySQL goes out on this one. We also declare the relevant key NON NULL above
87cc3456
GT
1408 sqlStmt += wxT(",PRIMARY KEY (");
1409 break;
1410 }
1411 default:
1412 {
1413 sqlStmt += wxT(",CONSTRAINT ");
2beca662
GT
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
87cc3456
GT
1423 sqlStmt += wxT("_PIDX PRIMARY KEY (");
1424 break;
1425 }
89894079
VZ
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
4fdae997 1434 sqlStmt += wxT(",");
1e92909e 1435 sqlStmt += colDefs[i].ColName;
89894079
VZ
1436 }
1437 }
2beca662
GT
1438 sqlStmt += wxT(")");
1439
597fadce
GT
1440 if (pDb->Dbms() == dbmsINFORMIX ||
1441 pDb->Dbms() == dbmsSYBASE_ASA ||
2beca662
GT
1442 pDb->Dbms() == dbmsSYBASE_ASE)
1443 {
1444 sqlStmt += wxT(" CONSTRAINT ");
1445 sqlStmt += tableName;
1446 sqlStmt += wxT("_PIDX");
1447 }
89894079
VZ
1448 }
1449 // Append the closing parentheses for the create table statement
4fdae997 1450 sqlStmt += wxT(")");
a2115c88 1451
4fdae997 1452 pDb->WriteSqlLog(sqlStmt);
1fc5dd6f 1453
a2115c88 1454#ifdef DBDEBUG_CONSOLE
f6bcfd97 1455 cout << endl << sqlStmt.c_str() << endl;
108106cf
JS
1456#endif
1457
89894079 1458 // Execute the CREATE TABLE statement
f6bcfd97 1459 RETCODE retcode = SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS);
89894079
VZ
1460 if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
1461 {
1462 pDb->DispAllErrors(henv, hdbc, hstmt);
1463 pDb->RollbackTrans();
1464 CloseCursor(hstmt);
a144affe 1465 return(FALSE);
89894079
VZ
1466 }
1467
1468 // Commit the transaction and close the cursor
3ca6a5f0 1469 if (!pDb->CommitTrans())
a144affe 1470 return(FALSE);
3ca6a5f0 1471 if (!CloseCursor(hstmt))
a144affe 1472 return(FALSE);
89894079
VZ
1473
1474 // Database table created successfully
a144affe 1475 return(TRUE);
108106cf 1476
f6bcfd97 1477} // wxDbTable::CreateTable()
108106cf 1478
67e9aaa3 1479
f6bcfd97
BP
1480/********** wxDbTable::DropTable() **********/
1481bool wxDbTable::DropTable()
a2115c88 1482{
a144affe 1483 // NOTE: This function returns TRUE if the Table does not exist, but
89894079 1484 // only for identified databases. Code will need to be added
0b8410f3 1485 // below for any other databases when those databases are defined
89894079 1486 // to handle this situation consistently
a2115c88 1487
1e92909e 1488 wxString sqlStmt;
a2115c88 1489
7d8c3dba 1490 sqlStmt.Printf(wxT("DROP TABLE %s"), tableName.c_str());
a2115c88 1491
4fdae997 1492 pDb->WriteSqlLog(sqlStmt);
a2115c88
GT
1493
1494#ifdef DBDEBUG_CONSOLE
f6bcfd97 1495 cout << endl << sqlStmt.c_str() << endl;
a2115c88
GT
1496#endif
1497
5a226de0
GT
1498
1499
1500
1501 RETCODE retcode = SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS);
2beca662 1502 if (retcode != SQL_SUCCESS)
89894079
VZ
1503 {
1504 // Check for "Base table not found" error and ignore
da38429d 1505 pDb->GetNextError(henv, hdbc, hstmt);
2beca662 1506 if (wxStrcmp(pDb->sqlState, wxT("S0002")) /*&&
da38429d 1507 wxStrcmp(pDb->sqlState, wxT("S1000"))*/) // "Base table not found"
2beca662 1508 {
89894079 1509 // Check for product specific error codes
2beca662 1510 if (!((pDb->Dbms() == dbmsSYBASE_ASA && !wxStrcmp(pDb->sqlState,wxT("42000"))) || // 5.x (and lower?)
da38429d 1511 (pDb->Dbms() == dbmsSYBASE_ASE && !wxStrcmp(pDb->sqlState,wxT("37000"))) ||
2beca662 1512 (pDb->Dbms() == dbmsPERVASIVE_SQL && !wxStrcmp(pDb->sqlState,wxT("S1000"))) || // Returns an S1000 then an S0002
da38429d 1513 (pDb->Dbms() == dbmsPOSTGRES && !wxStrcmp(pDb->sqlState,wxT("08S01")))))
89894079
VZ
1514 {
1515 pDb->DispNextError();
1516 pDb->DispAllErrors(henv, hdbc, hstmt);
1517 pDb->RollbackTrans();
5a226de0 1518// CloseCursor(hstmt);
a144affe 1519 return(FALSE);
89894079
VZ
1520 }
1521 }
1522 }
1523
1524 // Commit the transaction and close the cursor
1525 if (! pDb->CommitTrans())
a144affe 1526 return(FALSE);
89894079 1527 if (! CloseCursor(hstmt))
a144affe 1528 return(FALSE);
89894079 1529
a144affe 1530 return(TRUE);
f6bcfd97 1531} // wxDbTable::DropTable()
a2115c88 1532
67e9aaa3 1533
f6bcfd97 1534/********** wxDbTable::CreateIndex() **********/
87cc3456 1535bool wxDbTable::CreateIndex(const wxString &idxName, bool unique, UWORD noIdxCols,
2beca662 1536 wxDbIdxDef *pIdxDefs, bool attemptDrop)
108106cf 1537{
1e92909e 1538 wxString sqlStmt;
89894079
VZ
1539
1540 // Drop the index first
1541 if (attemptDrop && !DropIndex(idxName))
a144affe 1542 return (FALSE);
89894079 1543
3ca6a5f0
BP
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;
a144affe 1556 bool ok = TRUE;
3ca6a5f0
BP
1557 for (i = 0; i < noIdxCols && ok; i++)
1558 {
1559 int j = 0;
a144affe 1560 bool found = FALSE;
3ca6a5f0
BP
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)
a144affe 1568 found = TRUE;
3ca6a5f0
BP
1569 if (!found)
1570 j++;
1571 }
da38429d 1572
3ca6a5f0
BP
1573 if (found)
1574 {
4fdae997
GT
1575 ok = pDb->ModifyColumn(tableName, pIdxDefs[i].ColName,
1576 colDefs[j].DbDataType, colDefs[j].SzDataObj,
1577 wxT("NOT NULL"));
1578
3ca6a5f0
BP
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
a144affe 1589 ok = FALSE;
3ca6a5f0
BP
1590 }
1591 if (ok)
1592 pDb->CommitTrans();
1593 else
1594 {
1595 pDb->RollbackTrans();
a144affe 1596 return(FALSE);
3ca6a5f0
BP
1597 }
1598 }
da38429d 1599
89894079 1600 // Build a CREATE INDEX statement
4fdae997 1601 sqlStmt = wxT("CREATE ");
89894079 1602 if (unique)
4fdae997 1603 sqlStmt += wxT("UNIQUE ");
da38429d 1604
4fdae997 1605 sqlStmt += wxT("INDEX ");
1e92909e 1606 sqlStmt += idxName;
4fdae997 1607 sqlStmt += wxT(" ON ");
1e92909e 1608 sqlStmt += tableName;
4fdae997 1609 sqlStmt += wxT(" (");
da38429d 1610
89894079
VZ
1611 // Append list of columns making up index
1612 int i;
1613 for (i = 0; i < noIdxCols; i++)
1614 {
1e92909e 1615 sqlStmt += pIdxDefs[i].ColName;
9082f1a9
GT
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))
89894079
VZ
1620 {
1621 if (pIdxDefs[i].Ascending)
4fdae997 1622 sqlStmt += wxT(" ASC");
89894079 1623 else
4fdae997 1624 sqlStmt += wxT(" DESC");
89894079 1625 }
9082f1a9 1626 else
882fc8a9 1627 wxASSERT_MSG(pIdxDefs[i].Ascending, "Datasource does not support DESCending index columns");
89894079
VZ
1628
1629 if ((i + 1) < noIdxCols)
4fdae997 1630 sqlStmt += wxT(",");
89894079 1631 }
da38429d 1632
89894079 1633 // Append closing parentheses
4fdae997 1634 sqlStmt += wxT(")");
89894079 1635
4fdae997 1636 pDb->WriteSqlLog(sqlStmt);
1fc5dd6f 1637
a2115c88 1638#ifdef DBDEBUG_CONSOLE
f6bcfd97 1639 cout << endl << sqlStmt.c_str() << endl << endl;
108106cf
JS
1640#endif
1641
89894079 1642 // Execute the CREATE INDEX statement
f6bcfd97 1643 if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
89894079
VZ
1644 {
1645 pDb->DispAllErrors(henv, hdbc, hstmt);
1646 pDb->RollbackTrans();
1647 CloseCursor(hstmt);
a144affe 1648 return(FALSE);
89894079 1649 }
108106cf 1650
89894079
VZ
1651 // Commit the transaction and close the cursor
1652 if (! pDb->CommitTrans())
a144affe 1653 return(FALSE);
89894079 1654 if (! CloseCursor(hstmt))
a144affe 1655 return(FALSE);
108106cf 1656
89894079 1657 // Index Created Successfully
a144affe 1658 return(TRUE);
108106cf 1659
f6bcfd97 1660} // wxDbTable::CreateIndex()
108106cf 1661
67e9aaa3 1662
f6bcfd97 1663/********** wxDbTable::DropIndex() **********/
4fdae997 1664bool wxDbTable::DropIndex(const wxString &idxName)
a2115c88 1665{
a144affe 1666 // NOTE: This function returns TRUE if the Index does not exist, but
89894079 1667 // only for identified databases. Code will need to be added
5a226de0 1668 // below for any other databases when those databases are defined
89894079 1669 // to handle this situation consistently
a2115c88 1670
1e92909e 1671 wxString sqlStmt;
a2115c88 1672
5a226de0
GT
1673 if (pDb->Dbms() == dbmsACCESS || pDb->Dbms() == dbmsMY_SQL ||
1674 pDb->Dbms() == dbmsDBASE /*|| Paradox needs this syntax too when we add support*/)
7d8c3dba 1675 sqlStmt.Printf(wxT("DROP INDEX %s ON %s"),idxName.c_str(), tableName.c_str());
e93a3a18
GT
1676 else if ((pDb->Dbms() == dbmsMS_SQL_SERVER) ||
1677 (pDb->Dbms() == dbmsSYBASE_ASE))
7d8c3dba 1678 sqlStmt.Printf(wxT("DROP INDEX %s.%s"),tableName.c_str(), idxName.c_str());
89894079 1679 else
7d8c3dba 1680 sqlStmt.Printf(wxT("DROP INDEX %s"),idxName.c_str());
a2115c88 1681
4fdae997 1682 pDb->WriteSqlLog(sqlStmt);
a2115c88
GT
1683
1684#ifdef DBDEBUG_CONSOLE
f6bcfd97 1685 cout << endl << sqlStmt.c_str() << endl;
a2115c88
GT
1686#endif
1687
f6bcfd97 1688 if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
89894079
VZ
1689 {
1690 // Check for "Index not found" error and ignore
1691 pDb->GetNextError(henv, hdbc, hstmt);
4fdae997 1692 if (wxStrcmp(pDb->sqlState,wxT("S0012"))) // "Index not found"
89894079
VZ
1693 {
1694 // Check for product specific error codes
4fdae997
GT
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"))) ||
9082f1a9 1698 (pDb->Dbms() == dbmsINTERBASE && !wxStrcmp(pDb->sqlState,wxT("S1000"))) ||
4fdae997
GT
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")))
3ca6a5f0 1702 ))
89894079
VZ
1703 {
1704 pDb->DispNextError();
1705 pDb->DispAllErrors(henv, hdbc, hstmt);
1706 pDb->RollbackTrans();
1707 CloseCursor(hstmt);
a144affe 1708 return(FALSE);
89894079
VZ
1709 }
1710 }
1711 }
1712
1713 // Commit the transaction and close the cursor
1714 if (! pDb->CommitTrans())
a144affe 1715 return(FALSE);
89894079 1716 if (! CloseCursor(hstmt))
a144affe 1717 return(FALSE);
89894079 1718
a144affe 1719 return(TRUE);
f6bcfd97 1720} // wxDbTable::DropIndex()
a2115c88 1721
67e9aaa3 1722
38cfbffa 1723/********** wxDbTable::SetOrderByColNums() **********/
e938ff5e 1724bool wxDbTable::SetOrderByColNums(UWORD first, ... )
38cfbffa 1725{
2beca662 1726 int colNo = first; // using 'int' to be able to look for wxDB_NO_MORE_COLUN_NUMBERS
38cfbffa
GT
1727 va_list argptr;
1728
a144affe 1729 bool abort = FALSE;
38cfbffa
GT
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 {
a144affe 1741 abort = TRUE;
38cfbffa
GT
1742 continue;
1743 }
1744
1745 if (colNo != first)
4fdae997 1746 tempStr += wxT(",");
38cfbffa
GT
1747
1748 tempStr += colDefs[colNo].ColName;
1749 colNo = va_arg (argptr, int);
1750 }
1751 va_end (argptr); /* Reset variable arguments. */
1752
4fdae997 1753 SetOrderByClause(tempStr);
38cfbffa
GT
1754
1755 return (!abort);
1756} // wxDbTable::SetOrderByColNums()
1757
1758
f6bcfd97
BP
1759/********** wxDbTable::Insert() **********/
1760int wxDbTable::Insert(void)
108106cf 1761{
4fdae997 1762 wxASSERT(!queryOnly);
f6bcfd97 1763 if (queryOnly || !insertable)
89894079
VZ
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);
4fdae997 1775 if (! wxStrcmp(pDb->sqlState, wxT("23000"))) // Integrity constraint violated
89894079
VZ
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);
108106cf 1787
f6bcfd97 1788} // wxDbTable::Insert()
108106cf 1789
67e9aaa3 1790
f6bcfd97
BP
1791/********** wxDbTable::Update() **********/
1792bool wxDbTable::Update(void)
108106cf 1793{
4fdae997 1794 wxASSERT(!queryOnly);
89894079 1795 if (queryOnly)
a144affe 1796 return(FALSE);
a2115c88 1797
4fdae997 1798 wxString sqlStmt;
108106cf 1799
89894079 1800 // Build the SQL UPDATE statement
f6bcfd97 1801 BuildUpdateStmt(sqlStmt, DB_UPD_KEYFIELDS);
108106cf 1802
89894079 1803 pDb->WriteSqlLog(sqlStmt);
1fc5dd6f 1804
a2115c88 1805#ifdef DBDEBUG_CONSOLE
4fdae997 1806 cout << endl << sqlStmt.c_str() << endl << endl;
108106cf
JS
1807#endif
1808
89894079
VZ
1809 // Execute the SQL UPDATE statement
1810 return(execUpdate(sqlStmt));
108106cf 1811
f6bcfd97 1812} // wxDbTable::Update()
108106cf 1813
67e9aaa3 1814
f6bcfd97 1815/********** wxDbTable::Update(pSqlStmt) **********/
4fdae997 1816bool wxDbTable::Update(const wxString &pSqlStmt)
6919c53f 1817{
4fdae997 1818 wxASSERT(!queryOnly);
89894079 1819 if (queryOnly)
a144affe 1820 return(FALSE);
6919c53f 1821
89894079 1822 pDb->WriteSqlLog(pSqlStmt);
6919c53f 1823
89894079 1824 return(execUpdate(pSqlStmt));
6919c53f 1825
f6bcfd97 1826} // wxDbTable::Update(pSqlStmt)
6919c53f 1827
67e9aaa3 1828
f6bcfd97 1829/********** wxDbTable::UpdateWhere() **********/
4fdae997 1830bool wxDbTable::UpdateWhere(const wxString &pWhereClause)
108106cf 1831{
4fdae997 1832 wxASSERT(!queryOnly);
89894079 1833 if (queryOnly)
a144affe 1834 return(FALSE);
a2115c88 1835
4fdae997 1836 wxString sqlStmt;
108106cf 1837
89894079 1838 // Build the SQL UPDATE statement
f6bcfd97 1839 BuildUpdateStmt(sqlStmt, DB_UPD_WHERE, pWhereClause);
108106cf 1840
89894079 1841 pDb->WriteSqlLog(sqlStmt);
1fc5dd6f 1842
a2115c88 1843#ifdef DBDEBUG_CONSOLE
4fdae997 1844 cout << endl << sqlStmt.c_str() << endl << endl;
108106cf
JS
1845#endif
1846
89894079
VZ
1847 // Execute the SQL UPDATE statement
1848 return(execUpdate(sqlStmt));
108106cf 1849
f6bcfd97 1850} // wxDbTable::UpdateWhere()
108106cf 1851
67e9aaa3 1852
f6bcfd97
BP
1853/********** wxDbTable::Delete() **********/
1854bool wxDbTable::Delete(void)
108106cf 1855{
4fdae997 1856 wxASSERT(!queryOnly);
89894079 1857 if (queryOnly)
a144affe 1858 return(FALSE);
a2115c88 1859
4fdae997
GT
1860 wxString sqlStmt;
1861 sqlStmt.Empty();
108106cf 1862
89894079 1863 // Build the SQL DELETE statement
f6bcfd97 1864 BuildDeleteStmt(sqlStmt, DB_DEL_KEYFIELDS);
108106cf 1865
89894079 1866 pDb->WriteSqlLog(sqlStmt);
1fc5dd6f 1867
89894079
VZ
1868 // Execute the SQL DELETE statement
1869 return(execDelete(sqlStmt));
108106cf 1870
f6bcfd97 1871} // wxDbTable::Delete()
108106cf 1872
67e9aaa3 1873
f6bcfd97 1874/********** wxDbTable::DeleteWhere() **********/
4fdae997 1875bool wxDbTable::DeleteWhere(const wxString &pWhereClause)
108106cf 1876{
4fdae997 1877 wxASSERT(!queryOnly);
89894079 1878 if (queryOnly)
a144affe 1879 return(FALSE);
a2115c88 1880
4fdae997
GT
1881 wxString sqlStmt;
1882 sqlStmt.Empty();
108106cf 1883
89894079 1884 // Build the SQL DELETE statement
f6bcfd97 1885 BuildDeleteStmt(sqlStmt, DB_DEL_WHERE, pWhereClause);
108106cf 1886
89894079 1887 pDb->WriteSqlLog(sqlStmt);
1fc5dd6f 1888
89894079
VZ
1889 // Execute the SQL DELETE statement
1890 return(execDelete(sqlStmt));
108106cf 1891
f6bcfd97 1892} // wxDbTable::DeleteWhere()
108106cf 1893
67e9aaa3 1894
f6bcfd97
BP
1895/********** wxDbTable::DeleteMatching() **********/
1896bool wxDbTable::DeleteMatching(void)
108106cf 1897{
4fdae997 1898 wxASSERT(!queryOnly);
89894079 1899 if (queryOnly)
a144affe 1900 return(FALSE);
a2115c88 1901
4fdae997
GT
1902 wxString sqlStmt;
1903 sqlStmt.Empty();
108106cf 1904
89894079 1905 // Build the SQL DELETE statement
f6bcfd97 1906 BuildDeleteStmt(sqlStmt, DB_DEL_MATCHING);
108106cf 1907
89894079 1908 pDb->WriteSqlLog(sqlStmt);
1fc5dd6f 1909
89894079
VZ
1910 // Execute the SQL DELETE statement
1911 return(execDelete(sqlStmt));
108106cf 1912
f6bcfd97 1913} // wxDbTable::DeleteMatching()
108106cf 1914
67e9aaa3 1915
f6bcfd97 1916/********** wxDbTable::IsColNull() **********/
882fc8a9 1917bool wxDbTable::IsColNull(UWORD colNo) const
108106cf 1918{
f02d4a64 1919/*
a144affe 1920 This logic is just not right. It would indicate TRUE
f02d4a64
GT
1921 if a numeric field were set to a value of 0.
1922
89894079
VZ
1923 switch(colDefs[colNo].SqlCtype)
1924 {
3ca6a5f0
BP
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)
a144affe 1943 return(TRUE);
3ca6a5f0 1944 else
a144affe 1945 return(FALSE);
3ca6a5f0 1946 default:
a144affe 1947 return(TRUE);
89894079 1948 }
f02d4a64
GT
1949*/
1950 return (colDefs[colNo].Null);
f6bcfd97 1951} // wxDbTable::IsColNull()
108106cf 1952
67e9aaa3 1953
f6bcfd97
BP
1954/********** wxDbTable::CanSelectForUpdate() **********/
1955bool wxDbTable::CanSelectForUpdate(void)
108106cf 1956{
38cfbffa 1957 if (queryOnly)
a144affe 1958 return FALSE;
38cfbffa 1959
89894079 1960 if (pDb->Dbms() == dbmsMY_SQL)
a144affe 1961 return FALSE;
a2115c88 1962
e93a3a18
GT
1963 if ((pDb->Dbms() == dbmsORACLE) ||
1964 (pDb->dbInf.posStmts & SQL_PS_SELECT_FOR_UPDATE))
a144affe 1965 return(TRUE);
89894079 1966 else
a144affe 1967 return(FALSE);
108106cf 1968
f6bcfd97 1969} // wxDbTable::CanSelectForUpdate()
108106cf 1970
67e9aaa3 1971
f6bcfd97
BP
1972/********** wxDbTable::CanUpdByROWID() **********/
1973bool wxDbTable::CanUpdByROWID(void)
108106cf 1974{
67e9aaa3 1975/*
a144affe 1976 * NOTE: Returning FALSE for now until this can be debugged,
89894079 1977 * as the ROWID is not getting updated correctly
67e9aaa3 1978 */
a144affe 1979 return FALSE;
6b3f4fb8 1980/*
89894079 1981 if (pDb->Dbms() == dbmsORACLE)
a144affe 1982 return(TRUE);
89894079 1983 else
a144affe 1984 return(FALSE);
6b3f4fb8 1985*/
f6bcfd97 1986} // wxDbTable::CanUpdByROWID()
108106cf 1987
67e9aaa3 1988
f6bcfd97
BP
1989/********** wxDbTable::IsCursorClosedOnCommit() **********/
1990bool wxDbTable::IsCursorClosedOnCommit(void)
108106cf 1991{
89894079 1992 if (pDb->dbInf.cursorCommitBehavior == SQL_CB_PRESERVE)
a144affe 1993 return(FALSE);
89894079 1994 else
a144affe 1995 return(TRUE);
108106cf 1996
f6bcfd97 1997} // wxDbTable::IsCursorClosedOnCommit()
108106cf 1998
67e9aaa3 1999
f02d4a64
GT
2000
2001/********** wxDbTable::ClearMemberVar() **********/
e938ff5e 2002void wxDbTable::ClearMemberVar(UWORD colNo, bool setToNull)
108106cf 2003{
4fdae997 2004 wxASSERT(colNo < noCols);
f02d4a64
GT
2005
2006 switch(colDefs[colNo].SqlCtype)
89894079 2007 {
f02d4a64
GT
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;
89894079 2040 }
108106cf 2041
f02d4a64
GT
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
f6bcfd97 2056} // wxDbTable::ClearMemberVars()
108106cf 2057
67e9aaa3 2058
f6bcfd97
BP
2059/********** wxDbTable::SetQueryTimeout() **********/
2060bool wxDbTable::SetQueryTimeout(UDWORD nSeconds)
108106cf 2061{
89894079
VZ
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
a144affe 2072 return(TRUE);
108106cf 2073
f6bcfd97 2074} // wxDbTable::SetQueryTimeout()
108106cf 2075
67e9aaa3 2076
f6bcfd97 2077/********** wxDbTable::SetColDefs() **********/
87cc3456 2078void wxDbTable::SetColDefs(UWORD index, const wxString &fieldName, int dataType, void *pData,
6b3f4fb8 2079 SWORD cType, int size, bool keyField, bool upd,
e93a3a18 2080 bool insAllow, bool derivedCol)
108106cf 2081{
89894079
VZ
2082 if (!colDefs) // May happen if the database connection fails
2083 return;
2084
4fdae997 2085 if (fieldName.Length() > (unsigned int) DB_MAX_COLUMN_NAME_LEN)
89894079 2086 {
9082f1a9 2087 wxStrncpy(colDefs[index].ColName, fieldName, DB_MAX_COLUMN_NAME_LEN);
89894079 2088 colDefs[index].ColName[DB_MAX_COLUMN_NAME_LEN] = 0;
da38429d
VZ
2089
2090#ifdef __WXDEBUG__
9082f1a9 2091 wxString tmpMsg;
da38429d
VZ
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__
89894079
VZ
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 {
a144affe
GT
2109 colDefs[index].Updateable = FALSE;
2110 colDefs[index].InsertAllowed = FALSE;
89894079
VZ
2111 }
2112 else
2113 {
2114 colDefs[index].Updateable = upd;
2115 colDefs[index].InsertAllowed = insAllow;
2116 }
2117
a144affe 2118 colDefs[index].Null = FALSE;
da38429d 2119
f6bcfd97 2120} // wxDbTable::SetColDefs()
108106cf 2121
67e9aaa3 2122
e93a3a18 2123/********** wxDbTable::SetColDefs() **********/
87cc3456 2124wxDbColDataPtr* wxDbTable::SetColDefs(wxDbColInf *pColInfs, UWORD numCols)
67e9aaa3 2125{
4fdae997 2126 wxASSERT(pColInfs);
f6bcfd97 2127 wxDbColDataPtr *pColDataPtrs = NULL;
67e9aaa3 2128
89894079
VZ
2129 if (pColInfs)
2130 {
87cc3456 2131 UWORD index;
da38429d 2132
f6bcfd97 2133 pColDataPtrs = new wxDbColDataPtr[numCols+1];
67e9aaa3
GT
2134
2135 for (index = 0; index < numCols; index++)
89894079 2136 {
89894079
VZ
2137 // Process the fields
2138 switch (pColInfs[index].dbDataType)
2139 {
2140 case DB_DATA_TYPE_VARCHAR:
4fdae997 2141 pColDataPtrs[index].PtrDataObj = new wxChar[pColInfs[index].bufferLength+1];
0b8410f3
GT
2142 pColDataPtrs[index].SzDataObj = pColInfs[index].columnSize;
2143 pColDataPtrs[index].SqlCtype = SQL_C_CHAR;
2144 break;
89894079 2145 case DB_DATA_TYPE_INTEGER:
67e9aaa3 2146 // Can be long or short
89894079
VZ
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;
89894079 2160 case DB_DATA_TYPE_FLOAT:
89894079
VZ
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;
da38429d 2173 }
89894079 2174 break;
89894079 2175 case DB_DATA_TYPE_DATE:
89894079
VZ
2176 pColDataPtrs[index].PtrDataObj = new TIMESTAMP_STRUCT;
2177 pColDataPtrs[index].SzDataObj = sizeof(TIMESTAMP_STRUCT);
2178 pColDataPtrs[index].SqlCtype = SQL_C_TIMESTAMP;
2179 break;
bf5423ea 2180 case DB_DATA_TYPE_BLOB:
da38429d 2181 wxFAIL_MSG(wxT("This form of ::SetColDefs() cannot be used with BLOB columns"));
bf5423ea
GT
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 {
da38429d 2191 // Unable to build all the column definitions, as either one of
bf5423ea
GT
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;
89894079 2198 }
89894079
VZ
2199 }
2200 }
3ca6a5f0 2201
89894079 2202 return (pColDataPtrs);
3ca6a5f0 2203
e93a3a18 2204} // wxDbTable::SetColDefs()
67e9aaa3
GT
2205
2206
f6bcfd97
BP
2207/********** wxDbTable::SetCursor() **********/
2208void wxDbTable::SetCursor(HSTMT *hstmtActivate)
108106cf 2209{
f6bcfd97 2210 if (hstmtActivate == wxDB_DEFAULT_CURSOR)
89894079
VZ
2211 hstmt = *hstmtDefault;
2212 else
2213 hstmt = *hstmtActivate;
108106cf 2214
f6bcfd97 2215} // wxDbTable::SetCursor()
108106cf 2216
67e9aaa3 2217
4fdae997
GT
2218/********** wxDbTable::Count(const wxString &) **********/
2219ULONG wxDbTable::Count(const wxString &args)
108106cf 2220{
3ca6a5f0 2221 ULONG count;
1e92909e 2222 wxString sqlStmt;
89894079
VZ
2223 SDWORD cb;
2224
2225 // Build a "SELECT COUNT(*) FROM queryTableName [WHERE whereClause]" SQL Statement
4fdae997 2226 sqlStmt = wxT("SELECT COUNT(");
1e92909e 2227 sqlStmt += args;
4fdae997 2228 sqlStmt += wxT(") FROM ");
1e92909e 2229 sqlStmt += queryTableName;
f6bcfd97 2230#if wxODBC_BACKWARD_COMPATABILITY
89894079 2231 if (from && wxStrlen(from))
f6bcfd97
BP
2232#else
2233 if (from.Length())
2234#endif
1e92909e 2235 sqlStmt += from;
89894079
VZ
2236
2237 // Add the where clause if one is provided
f6bcfd97 2238#if wxODBC_BACKWARD_COMPATABILITY
89894079 2239 if (where && wxStrlen(where))
f6bcfd97
BP
2240#else
2241 if (where.Length())
2242#endif
89894079 2243 {
4fdae997 2244 sqlStmt += wxT(" WHERE ");
1e92909e 2245 sqlStmt += where;
89894079
VZ
2246 }
2247
4fdae997 2248 pDb->WriteSqlLog(sqlStmt);
89894079
VZ
2249
2250 // Initialize the Count cursor if it's not already initialized
2251 if (!hstmtCount)
2252 {
a144affe 2253 hstmtCount = GetNewCursor(FALSE,FALSE);
4fdae997 2254 wxASSERT(hstmtCount);
89894079
VZ
2255 if (!hstmtCount)
2256 return(0);
2257 }
2258
2259 // Execute the SQL statement
f6bcfd97 2260 if (SQLExecDirect(*hstmtCount, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS)
89894079
VZ
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
6b3f4fb8 2274 if (SQLGetData(*hstmtCount, (UWORD)1, SQL_C_ULONG, &count, sizeof(count), &cb) != SQL_SUCCESS)
89894079
VZ
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
3ca6a5f0 2285 return(count);
108106cf 2286
f6bcfd97 2287} // wxDbTable::Count()
108106cf 2288
67e9aaa3 2289
f6bcfd97
BP
2290/********** wxDbTable::Refresh() **********/
2291bool wxDbTable::Refresh(void)
108106cf 2292{
a144affe 2293 bool result = TRUE;
89894079
VZ
2294
2295 // Switch to the internal cursor so any active cursors are not corrupted
2296 HSTMT currCursor = GetCursor();
2297 hstmt = hstmtInternal;
f6bcfd97 2298#if wxODBC_BACKWARD_COMPATABILITY
89894079
VZ
2299 // Save the where and order by clauses
2300 char *saveWhere = where;
2301 char *saveOrderBy = orderBy;
f6bcfd97
BP
2302#else
2303 wxString saveWhere = where;
2304 wxString saveOrderBy = orderBy;
2305#endif
89894079
VZ
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.
4fdae997
GT
2308 wxString whereClause;
2309 whereClause.Empty();
2310
89894079
VZ
2311 if (CanUpdByROWID())
2312 {
2313 SDWORD cb;
4fdae997 2314 wxChar rowid[wxDB_ROWID_LEN+1];
89894079
VZ
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.
6b3f4fb8 2319 if (SQLGetData(hstmt, (UWORD)(noCols+1), SQL_C_CHAR, (UCHAR*) rowid, wxDB_ROWID_LEN, &cb) == SQL_SUCCESS)
89894079 2320 {
4fdae997
GT
2321 whereClause += queryTableName;
2322 whereClause += wxT(".ROWID = '");
2323 whereClause += rowid;
2324 whereClause += wxT("'");
89894079
VZ
2325 }
2326 }
2327
2328 // If unable to use the ROWID, build a where clause from the keyfields
2329 if (wxStrlen(whereClause) == 0)
f6bcfd97 2330 BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS, queryTableName);
89894079
VZ
2331
2332 // Requery the record
2333 where = whereClause;
4fdae997 2334 orderBy.Empty();
89894079 2335 if (!Query())
a144affe 2336 result = FALSE;
89894079
VZ
2337
2338 if (result && !GetNext())
a144affe 2339 result = FALSE;
89894079
VZ
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
1e92909e 2349 where = saveWhere;
89894079
VZ
2350 orderBy = saveOrderBy;
2351
2352 return(result);
108106cf 2353
f6bcfd97 2354} // wxDbTable::Refresh()
108106cf 2355
67e9aaa3 2356
e938ff5e
GT
2357/********** wxDbTable::SetColNull() **********/
2358bool wxDbTable::SetColNull(UWORD colNo, bool set)
a2115c88 2359{
89894079 2360 if (colNo < noCols)
f02d4a64
GT
2361 {
2362 colDefs[colNo].Null = set;
2363 if (set) // Blank out the values in the member variable
a144affe
GT
2364 ClearMemberVar(colNo,FALSE); // Must call with FALSE, or infinite recursion will happen
2365 return(TRUE);
f02d4a64 2366 }
89894079 2367 else
a144affe 2368 return(FALSE);
a2115c88 2369
4fdae997 2370} // wxDbTable::SetColNull()
67e9aaa3 2371
a2115c88 2372
e938ff5e 2373/********** wxDbTable::SetColNull() **********/
4fdae997 2374bool wxDbTable::SetColNull(const wxString &colName, bool set)
a2115c88 2375{
89894079
VZ
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)
f02d4a64
GT
2384 {
2385 colDefs[i].Null = set;
2386 if (set) // Blank out the values in the member variable
a144affe
GT
2387 ClearMemberVar(i,FALSE); // Must call with FALSE, or infinite recursion will happen
2388 return(TRUE);
f02d4a64 2389 }
89894079 2390 else
a144affe 2391 return(FALSE);
a2115c88 2392
4fdae997 2393} // wxDbTable::SetColNull()
a2115c88 2394
67e9aaa3 2395
f6bcfd97
BP
2396/********** wxDbTable::GetNewCursor() **********/
2397HSTMT *wxDbTable::GetNewCursor(bool setCursor, bool bindColumns)
a2115c88 2398{
89894079 2399 HSTMT *newHSTMT = new HSTMT;
4fdae997 2400 wxASSERT(newHSTMT);
89894079
VZ
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 {
882fc8a9 2420 if (!bindCols(*newHSTMT))
89894079
VZ
2421 {
2422 delete newHSTMT;
2423 return(0);
2424 }
2425 }
2426
2427 if (setCursor)
2428 SetCursor(newHSTMT);
2429
2430 return(newHSTMT);
2431
f6bcfd97 2432} // wxDbTable::GetNewCursor()
67e9aaa3 2433
a2115c88 2434
f6bcfd97
BP
2435/********** wxDbTable::DeleteCursor() **********/
2436bool wxDbTable::DeleteCursor(HSTMT *hstmtDel)
a2115c88 2437{
a144affe 2438 bool result = TRUE;
a2115c88 2439
89894079
VZ
2440 if (!hstmtDel) // Cursor already deleted
2441 return(result);
a2115c88 2442
7d8c3dba
GT
2443/*
2444ODBC 3.0 says to use this form
2445 if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS)
da38429d 2446
7d8c3dba 2447*/
89894079
VZ
2448 if (SQLFreeStmt(*hstmtDel, SQL_DROP) != SQL_SUCCESS)
2449 {
2450 pDb->DispAllErrors(henv, hdbc);
a144affe 2451 result = FALSE;
89894079 2452 }
a2115c88 2453
89894079 2454 delete hstmtDel;
a2115c88 2455
89894079 2456 return(result);
a2115c88 2457
f6bcfd97 2458} // wxDbTable::DeleteCursor()
a2115c88 2459
882fc8a9
GT
2460//////////////////////////////////////////////////////////////
2461// wxDbGrid support functions
2462//////////////////////////////////////////////////////////////
2463
2464void wxDbTable::SetRowMode(const rowmode_t rowmode)
2465{
2466 if (!m_hstmtGridQuery)
2467 {
a144affe 2468 m_hstmtGridQuery = GetNewCursor(FALSE,FALSE);
882fc8a9
GT
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 {
da38429d 2556 if ((colDefs[col].SqlCtype == SQL_C_DATE)
882fc8a9
GT
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()))
a144affe 2562 SetColNull(col,TRUE);
da38429d 2563 }
882fc8a9
GT
2564
2565 switch (colDefs[col].SqlCtype)
2566 {
2567 case SQL_CHAR:
2568 case SQL_VARCHAR:
2569 csstrncpyt((char *)(colDefs[col].PtrDataObj),
da38429d 2570 val.GetString().c_str(),
882fc8a9
GT
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;
da38429d 2598
882fc8a9
GT
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;
da38429d 2608
882fc8a9
GT
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();
da38429d 2621
882fc8a9
GT
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;
da38429d 2641
882fc8a9
GT
2642 blk = malloc(m_keysize);
2643 blkptr = (char *) blk;
da38429d 2644
882fc8a9
GT
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;
da38429d 2666
882fc8a9
GT
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 {
a144affe 2675 SetColNull(i, FALSE);
882fc8a9
GT
2676 memcpy(colDefs[i].PtrDataObj, blkptr, colDefs[i].SzDataObj);
2677 blkptr += colDefs[i].SzDataObj;
2678 }
2679 }
2680} // wxDbTable::SetKey()
2681
2682
a2115c88 2683#endif // wxUSE_ODBC
1fc5dd6f 2684