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