1 ///////////////////////////////////////////////////////////////////////////////
3 // Purpose: Implementation of the wxTable class.
6 // -Dynamic cursor support - Only one predefined cursor, as many others as
7 // you need may be created on demand
8 // -Reduced number of active cursors significantly
9 // -Query-Only wxTable objects
12 // Copyright: (c) 1996 Remstar International, Inc.
13 // Licence: wxWindows licence, plus:
14 // Notice: This class library and its intellectual design are free of charge for use,
15 // modification, enhancement, debugging under the following conditions:
16 // 1) These classes may only be used as part of the implementation of a
17 // wxWindows-based application
18 // 2) All enhancements and bug fixes are to be submitted back to the wxWindows
19 // user groups free of all charges for use with the wxWindows library.
20 // 3) These classes may not be distributed as part of any other class library,
21 // DLL, text (written or electronic), other than a complete distribution of
22 // the wxWindows GUI development toolkit.
23 ///////////////////////////////////////////////////////////////////////////////
30 // Use this line for wxWindows v1.x
32 // Use this line for wxWindows v2.x
33 #include "wx/version.h"
34 #include "wx/wxprec.h"
36 #if wxMAJOR_VERSION == 2
38 # pragma implementation "dbtable.h"
42 #ifdef DBDEBUG_CONSOLE
50 #if wxMAJOR_VERSION == 2
52 #include "wx/string.h"
53 #include "wx/object.h"
56 #include "wx/msgdlg.h"
58 #include "wx/filefn.h"
61 #if wxMAJOR_VERSION == 1
62 # if defined(wx_msw) || defined(wx_x)
78 #if wxMAJOR_VERSION == 1
80 #elif wxMAJOR_VERSION == 2
81 #include "wx/dbtable.h"
85 // The HPUX preprocessor lines below were commented out on 8/20/97
86 // because macros.h currently redefines DEBUG and is unneeded.
88 // # include <macros.h>
91 # include <sys/minmax.h>
95 ULONG lastTableID
= 0;
103 /********** wxTable::wxTable() **********/
104 wxTable::wxTable(wxDB
*pwxDB
, const char *tblName
, const int nCols
,
105 const char *qryTblName
, bool qryOnly
, const char *tblPath
)
107 pDb
= pwxDB
; // Pointer to the wxDB object
111 hstmtDefault
= 0; // Initialized below
112 hstmtCount
= 0; // Initialized first time it is needed
119 noCols
= nCols
; // No. of cols in the table
120 where
= 0; // Where clause
121 orderBy
= 0; // Order By clause
122 from
= 0; // From clause
123 selectForUpdate
= FALSE
; // SELECT ... FOR UPDATE; Indicates whether to include the FOR UPDATE phrase
128 wxStrcpy(tableName
, tblName
); // Table Name
130 wxStrcpy(tablePath
, tblPath
); // Table Path - used for dBase files
132 if (qryTblName
) // Name of the table/view to query
133 wxStrcpy(queryTableName
, qryTblName
);
135 wxStrcpy(queryTableName
, tblName
);
137 // assert(pDb); // Assert is placed after table name is assigned for error reporting reasons
144 tableID
= ++lastTableID
;
145 sprintf(s
, "wxTable constructor (%-20s) tableID:[%6lu] pDb:[%p]", tblName
,tableID
,pDb
);
148 CstructTablesInUse
*tableInUse
;
149 tableInUse
= new CstructTablesInUse();
150 tableInUse
->tableName
= tblName
;
151 tableInUse
->tableID
= tableID
;
152 tableInUse
->pDb
= pDb
;
153 TablesInUse
.Append(tableInUse
);
158 // Grab the HENV and HDBC from the wxDB object
162 // Allocate space for column definitions
164 colDefs
= new CcolDef
[noCols
]; // Points to the first column defintion
166 // Allocate statement handles for the table
169 // Allocate a separate statement handle for performing inserts
170 if (SQLAllocStmt(hdbc
, &hstmtInsert
) != SQL_SUCCESS
)
171 pDb
->DispAllErrors(henv
, hdbc
);
172 // Allocate a separate statement handle for performing deletes
173 if (SQLAllocStmt(hdbc
, &hstmtDelete
) != SQL_SUCCESS
)
174 pDb
->DispAllErrors(henv
, hdbc
);
175 // Allocate a separate statement handle for performing updates
176 if (SQLAllocStmt(hdbc
, &hstmtUpdate
) != SQL_SUCCESS
)
177 pDb
->DispAllErrors(henv
, hdbc
);
179 // Allocate a separate statement handle for internal use
180 if (SQLAllocStmt(hdbc
, &hstmtInternal
) != SQL_SUCCESS
)
181 pDb
->DispAllErrors(henv
, hdbc
);
183 // Set the cursor type for the statement handles
184 cursorType
= SQL_CURSOR_STATIC
;
185 if (SQLSetStmtOption(hstmtInternal
, SQL_CURSOR_TYPE
, cursorType
) != SQL_SUCCESS
)
187 // Check to see if cursor type is supported
188 pDb
->GetNextError(henv
, hdbc
, hstmtInternal
);
189 if (! wxStrcmp(pDb
->sqlState
, "01S02")) // Option Value Changed
191 // Datasource does not support static cursors. Driver
192 // will substitute a cursor type. Call SQLGetStmtOption()
193 // to determine which cursor type was selected.
194 if (SQLGetStmtOption(hstmtInternal
, SQL_CURSOR_TYPE
, &cursorType
) != SQL_SUCCESS
)
195 pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
);
196 #ifdef DBDEBUG_CONSOLE
197 cout
<< "Static cursor changed to: ";
200 case SQL_CURSOR_FORWARD_ONLY
:
201 cout
<< "Forward Only"; break;
202 case SQL_CURSOR_STATIC
:
203 cout
<< "Static"; break;
204 case SQL_CURSOR_KEYSET_DRIVEN
:
205 cout
<< "Keyset Driven"; break;
206 case SQL_CURSOR_DYNAMIC
:
207 cout
<< "Dynamic"; break;
209 cout
<< endl
<< endl
;
214 pDb
->DispNextError();
215 pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
);
218 #ifdef DBDEBUG_CONSOLE
220 cout
<< "Cursor Type set to STATIC" << endl
<< endl
;
225 // Set the cursor type for the INSERT statement handle
226 if (SQLSetStmtOption(hstmtInsert
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
)
227 pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
);
228 // Set the cursor type for the DELETE statement handle
229 if (SQLSetStmtOption(hstmtDelete
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
)
230 pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
);
231 // Set the cursor type for the UPDATE statement handle
232 if (SQLSetStmtOption(hstmtUpdate
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
)
233 pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
);
236 // Make the default cursor the active cursor
237 hstmtDefault
= NewCursor(FALSE
,FALSE
);
238 assert(hstmtDefault
);
239 hstmt
= *hstmtDefault
;
241 } // wxTable::wxTable()
243 /********** wxTable::~wxTable() **********/
249 sprintf(s
, "wxTable destructor (%-20s) tableID:[%6lu] pDb:[%p]", tableName
,tableID
,pDb
);
258 pNode
= TablesInUse
.First();
259 while (pNode
&& !found
)
261 if (((CstructTablesInUse
*)pNode
->Data())->tableID
== tableID
)
264 if (!TablesInUse
.DeleteNode(pNode
))
265 wxMessageBox (s
,"Unable to delete node!");
268 pNode
= pNode
->Next();
273 sprintf(msg
,"Unable to find the tableID in the linked\nlist of tables in use.\n\n%s",s
);
274 wxMessageBox (msg
,"NOTICE...");
279 // Decrement the wxDB table count
283 // Delete memory allocated for column definitions
287 // Free statement handles
291 if (SQLFreeStmt(hstmtInsert
, SQL_DROP
) != SQL_SUCCESS
)
292 pDb
->DispAllErrors(henv
, hdbc
);
294 if (SQLFreeStmt(hstmtDelete
, SQL_DROP
) != SQL_SUCCESS
)
295 pDb
->DispAllErrors(henv
, hdbc
);
297 if (SQLFreeStmt(hstmtUpdate
, SQL_DROP
) != SQL_SUCCESS
)
298 pDb
->DispAllErrors(henv
, hdbc
);
301 if (SQLFreeStmt(hstmtInternal
, SQL_DROP
) != SQL_SUCCESS
)
302 pDb
->DispAllErrors(henv
, hdbc
);
304 // Delete dynamically allocated cursors
306 DeleteCursor(hstmtDefault
);
308 DeleteCursor(hstmtCount
);
310 } // wxTable::~wxTable()
312 /***************************** PRIVATE FUNCTIONS *****************************/
314 /********** wxTable::bindInsertParams() **********/
315 bool wxTable::bindInsertParams(void)
322 UDWORD precision
= 0;
325 // Bind each column (that can be inserted) of the table to a parameter marker
327 for (i
= 0; i
< noCols
; i
++)
329 if (! colDefs
[i
].InsertAllowed
)
331 switch(colDefs
[i
].DbDataType
)
333 case DB_DATA_TYPE_VARCHAR
:
334 fSqlType
= pDb
->typeInfVarchar
.FsqlType
;
335 precision
= colDefs
[i
].SzDataObj
;
337 colDefs
[i
].CbValue
= SQL_NTS
;
339 case DB_DATA_TYPE_INTEGER
:
340 fSqlType
= pDb
->typeInfInteger
.FsqlType
;
341 precision
= pDb
->typeInfInteger
.Precision
;
343 colDefs
[i
].CbValue
= 0;
345 case DB_DATA_TYPE_FLOAT
:
346 fSqlType
= pDb
->typeInfFloat
.FsqlType
;
347 precision
= pDb
->typeInfFloat
.Precision
;
348 scale
= pDb
->typeInfFloat
.MaximumScale
;
349 // SQL Sybase Anywhere v5.5 returned a negative number for the
350 // MaxScale. This caused ODBC to kick out an error on ibscale.
351 // I check for this here and set the scale = precision.
353 // scale = (short) precision;
354 colDefs
[i
].CbValue
= 0;
356 case DB_DATA_TYPE_DATE
:
357 fSqlType
= pDb
->typeInfDate
.FsqlType
;
358 precision
= pDb
->typeInfDate
.Precision
;
360 colDefs
[i
].CbValue
= 0;
366 colDefs
[i
].CbValue
= SQL_NULL_DATA
;
367 colDefs
[i
].Null
= FALSE
;
369 if (SQLBindParameter(hstmtInsert
, i
+1, SQL_PARAM_INPUT
, colDefs
[i
].SqlCtype
,
370 fSqlType
, precision
, scale
, (UCHAR
*) colDefs
[i
].PtrDataObj
,
371 precision
+1,&colDefs
[i
].CbValue
) != SQL_SUCCESS
)
372 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
));
375 // Completed successfully
378 } // wxTable::bindInsertParams()
380 /********** wxTable::bindUpdateParams() **********/
381 bool wxTable::bindUpdateParams(void)
388 UDWORD precision
= 0;
391 // Bind each UPDATEABLE column of the table to a parameter marker
393 for (i
= 0, colNo
= 1; i
< noCols
; i
++)
395 if (! colDefs
[i
].Updateable
)
397 switch(colDefs
[i
].DbDataType
)
399 case DB_DATA_TYPE_VARCHAR
:
400 fSqlType
= pDb
->typeInfVarchar
.FsqlType
;
401 precision
= colDefs
[i
].SzDataObj
;
403 colDefs
[i
].CbValue
= SQL_NTS
;
405 case DB_DATA_TYPE_INTEGER
:
406 fSqlType
= pDb
->typeInfInteger
.FsqlType
;
407 precision
= pDb
->typeInfInteger
.Precision
;
409 colDefs
[i
].CbValue
= 0;
411 case DB_DATA_TYPE_FLOAT
:
412 fSqlType
= pDb
->typeInfFloat
.FsqlType
;
413 precision
= pDb
->typeInfFloat
.Precision
;
414 scale
= pDb
->typeInfFloat
.MaximumScale
;
415 // SQL Sybase Anywhere v5.5 returned a negative number for the
416 // MaxScale. This caused ODBC to kick out an error on ibscale.
417 // I check for this here and set the scale = precision.
419 // scale = (short) precision;
420 colDefs
[i
].CbValue
= 0;
422 case DB_DATA_TYPE_DATE
:
423 fSqlType
= pDb
->typeInfDate
.FsqlType
;
424 precision
= pDb
->typeInfDate
.Precision
;
426 colDefs
[i
].CbValue
= 0;
429 if (SQLBindParameter(hstmtUpdate
, colNo
++, SQL_PARAM_INPUT
, colDefs
[i
].SqlCtype
,
430 fSqlType
, precision
, scale
, (UCHAR
*) colDefs
[i
].PtrDataObj
,
431 precision
+1, &colDefs
[i
].CbValue
) != SQL_SUCCESS
)
432 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
));
435 // Completed successfully
438 } // wxTable::bindUpdateParams()
440 /********** wxTable::bindCols() **********/
441 bool wxTable::bindCols(HSTMT cursor
)
445 // Bind each column of the table to a memory address for fetching data
447 for (i
= 0; i
< noCols
; i
++)
449 if (SQLBindCol(cursor
, i
+1, colDefs
[i
].SqlCtype
, (UCHAR
*) colDefs
[i
].PtrDataObj
,
450 colDefs
[i
].SzDataObj
, &cb
) != SQL_SUCCESS
)
451 return(pDb
->DispAllErrors(henv
, hdbc
, cursor
));
454 // Completed successfully
457 } // wxTable::bindCols()
459 /********** wxTable::getRec() **********/
460 bool wxTable::getRec(UWORD fetchType
)
464 if (!pDb
->FwdOnlyCursors())
466 // Fetch the NEXT, PREV, FIRST or LAST record, depending on fetchType
470 retcode
= SQLExtendedFetch(hstmt
, fetchType
, 0, &cRowsFetched
, &rowStatus
);
471 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
472 if (retcode
== SQL_NO_DATA_FOUND
)
475 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
));
479 // Fetch the next record from the record set
480 retcode
= SQLFetch(hstmt
);
481 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
483 if (retcode
== SQL_NO_DATA_FOUND
)
486 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
));
490 // Completed successfully
493 } // wxTable::getRec()
495 /********** wxTable::execDelete() **********/
496 bool wxTable::execDelete(const char *pSqlStmt
)
498 // Execute the DELETE statement
499 if (SQLExecDirect(hstmtDelete
, (UCHAR FAR
*) pSqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
500 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
));
502 // Record deleted successfully
505 } // wxTable::execDelete()
507 /********** wxTable::execUpdate() **********/
508 bool wxTable::execUpdate(const char *pSqlStmt
)
510 // Execute the UPDATE statement
511 if (SQLExecDirect(hstmtUpdate
, (UCHAR FAR
*) pSqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
512 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
));
514 // Record deleted successfully
517 } // wxTable::execUpdate()
519 /********** wxTable::query() **********/
520 bool wxTable::query(int queryType
, bool forUpdate
, bool distinct
, char *pSqlStmt
)
522 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
524 // Set the selectForUpdate member variable
526 // The user may wish to select for update, but the DBMS may not be capable
527 selectForUpdate
= CanSelectForUpdate();
529 selectForUpdate
= FALSE
;
531 // Set the SQL SELECT string
532 if (queryType
!= DB_SELECT_STATEMENT
) // A select statement was not passed in,
533 { // so generate a select statement.
534 GetSelectStmt(sqlStmt
, queryType
, distinct
);
535 pDb
->WriteSqlLog(sqlStmt
);
538 // Make sure the cursor is closed first
539 if (! CloseCursor(hstmt
))
542 // Execute the SQL SELECT statement
545 retcode
= SQLExecDirect(hstmt
, (UCHAR FAR
*) (queryType
== DB_SELECT_STATEMENT
? pSqlStmt
: sqlStmt
), SQL_NTS
);
546 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
547 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
));
549 // Completed successfully
552 } // wxTable::query()
556 /********** wxTable::Open() **********/
557 bool wxTable::Open(void)
563 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
565 // Verify that the table exists in the database
566 if (!pDb
->TableExists(tableName
,NULL
,tablePath
))
569 sprintf(s
, "Error opening '%s', table/view does not exist in the database.", tableName
);
574 // Bind the member variables for field exchange between
575 // the wxTable object and the ODBC record.
578 if (!bindInsertParams()) // Inserts
580 if (!bindUpdateParams()) // Updates
583 if (!bindCols(*hstmtDefault
)) // Selects
585 if (!bindCols(hstmtInternal
)) // Internal use only
588 * Do NOT bind the hstmtCount cursor!!!
591 // Build an insert statement using parameter markers
592 if (!queryOnly
&& noCols
> 0)
594 bool needComma
= FALSE
;
595 sprintf(sqlStmt
, "INSERT INTO %s (", tableName
);
596 for (i
= 0; i
< noCols
; i
++)
598 if (! colDefs
[i
].InsertAllowed
)
601 wxStrcat(sqlStmt
, ",");
602 wxStrcat(sqlStmt
, colDefs
[i
].ColName
);
606 wxStrcat(sqlStmt
, ") VALUES (");
607 for (i
= 0; i
< noCols
; i
++)
609 if (! colDefs
[i
].InsertAllowed
)
612 wxStrcat(sqlStmt
, ",");
613 wxStrcat(sqlStmt
, "?");
616 wxStrcat(sqlStmt
, ")");
618 // pDb->WriteSqlLog(sqlStmt);
620 // Prepare the insert statement for execution
621 if (SQLPrepare(hstmtInsert
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
622 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
));
625 // Completed successfully
630 /********** wxTable::Query() **********/
631 bool wxTable::Query(bool forUpdate
, bool distinct
)
634 return(query(DB_SELECT_WHERE
, forUpdate
, distinct
));
636 } // wxTable::Query()
638 /********** wxTable::QueryBySqlStmt() **********/
639 bool wxTable::QueryBySqlStmt(char *pSqlStmt
)
641 pDb
->WriteSqlLog(pSqlStmt
);
643 return(query(DB_SELECT_STATEMENT
, FALSE
, FALSE
, pSqlStmt
));
645 } // wxTable::QueryBySqlStmt()
647 /********** wxTable::QueryMatching() **********/
648 bool wxTable::QueryMatching(bool forUpdate
, bool distinct
)
651 return(query(DB_SELECT_MATCHING
, forUpdate
, distinct
));
653 } // wxTable::QueryMatching()
655 /********** wxTable::QueryOnKeyFields() **********/
656 bool wxTable::QueryOnKeyFields(bool forUpdate
, bool distinct
)
659 return(query(DB_SELECT_KEYFIELDS
, forUpdate
, distinct
));
661 } // wxTable::QueryOnKeyFields()
663 /********** wxTable::GetPrev() **********/
664 bool wxTable::GetPrev(void)
666 if (pDb
->FwdOnlyCursors())
668 wxFAIL_MSG(wxT("GetPrev()::Backward scrolling cursors are not enabled for this instance of wxTable"));
672 return(getRec(SQL_FETCH_PRIOR
));
673 } // wxTable::GetPrev()
675 /********** wxTable::operator-- **********/
676 bool wxTable::operator--(int)
678 if (pDb
->FwdOnlyCursors())
680 wxFAIL_MSG(wxT("operator--:Backward scrolling cursors are not enabled for this instance of wxTable"));
684 return(getRec(SQL_FETCH_PRIOR
));
685 } // wxTable::operator--
687 /********** wxTable::GetFirst() **********/
688 bool wxTable::GetFirst(void)
690 if (pDb
->FwdOnlyCursors())
692 wxFAIL_MSG(wxT("GetFirst():Backward scrolling cursors are not enabled for this instance of wxTable"));
696 return(getRec(SQL_FETCH_FIRST
));
697 } // wxTable::GetFirst()
699 /********** wxTable::GetLast() **********/
700 bool wxTable::GetLast(void)
702 if (pDb
->FwdOnlyCursors())
704 wxFAIL_MSG(wxT("GetLast()::Backward scrolling cursors are not enabled for this instance of wxTable"));
708 return(getRec(SQL_FETCH_LAST
));
709 } // wxTable::GetLast()
711 /********** wxTable::GetSelectStmt() **********/
712 void wxTable::GetSelectStmt(char *pSqlStmt
, int typeOfSelect
, bool distinct
)
714 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
];
718 // Build a select statement to query the database
719 wxStrcpy(pSqlStmt
, "SELECT ");
721 // SELECT DISTINCT values only?
723 wxStrcat(pSqlStmt
, "DISTINCT ");
725 // Was a FROM clause specified to join tables to the base table?
726 // Available for ::Query() only!!!
727 bool appendFromClause
= FALSE
;
728 if (typeOfSelect
== DB_SELECT_WHERE
&& from
&& wxStrlen(from
))
729 appendFromClause
= TRUE
;
731 // Add the column list
733 for (i
= 0; i
< noCols
; i
++)
735 // If joining tables, the base table column names must be qualified to avoid ambiguity
736 if (appendFromClause
)
738 wxStrcat(pSqlStmt
, queryTableName
);
739 wxStrcat(pSqlStmt
, ".");
741 wxStrcat(pSqlStmt
, colDefs
[i
].ColName
);
743 wxStrcat(pSqlStmt
, ",");
746 // If the datasource supports ROWID, get this column as well. Exception: Don't retrieve
747 // the ROWID if querying distinct records. The rowid will always be unique.
748 if (!distinct
&& CanUpdByROWID())
750 // If joining tables, the base table column names must be qualified to avoid ambiguity
751 if (appendFromClause
)
753 wxStrcat(pSqlStmt
, ",");
754 wxStrcat(pSqlStmt
, queryTableName
);
755 wxStrcat(pSqlStmt
, ".ROWID");
758 wxStrcat(pSqlStmt
, ",ROWID");
761 // Append the FROM tablename portion
762 wxStrcat(pSqlStmt
, " FROM ");
763 wxStrcat(pSqlStmt
, queryTableName
);
765 // Sybase uses the HOLDLOCK keyword to lock a record during query.
766 // The HOLDLOCK keyword follows the table name in the from clause.
767 // Each table in the from clause must specify HOLDLOCK or
768 // NOHOLDLOCK (the default). Note: The "FOR UPDATE" clause
769 // is parsed but ignored in SYBASE Transact-SQL.
770 if (selectForUpdate
&& (pDb
->Dbms() == dbmsSYBASE_ASA
|| pDb
->Dbms() == dbmsSYBASE_ASE
))
771 wxStrcat(pSqlStmt
, " HOLDLOCK");
773 if (appendFromClause
)
774 wxStrcat(pSqlStmt
, from
);
776 // Append the WHERE clause. Either append the where clause for the class
777 // or build a where clause. The typeOfSelect determines this.
780 case DB_SELECT_WHERE
:
781 if (where
&& wxStrlen(where
)) // May not want a where clause!!!
783 wxStrcat(pSqlStmt
, " WHERE ");
784 wxStrcat(pSqlStmt
, where
);
787 case DB_SELECT_KEYFIELDS
:
788 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
);
789 if (wxStrlen(whereClause
))
791 wxStrcat(pSqlStmt
, " WHERE ");
792 wxStrcat(pSqlStmt
, whereClause
);
795 case DB_SELECT_MATCHING
:
796 GetWhereClause(whereClause
, DB_WHERE_MATCHING
);
797 if (wxStrlen(whereClause
))
799 wxStrcat(pSqlStmt
, " WHERE ");
800 wxStrcat(pSqlStmt
, whereClause
);
805 // Append the ORDER BY clause
806 if (orderBy
&& wxStrlen(orderBy
))
808 wxStrcat(pSqlStmt
, " ORDER BY ");
809 wxStrcat(pSqlStmt
, orderBy
);
812 // SELECT FOR UPDATE if told to do so and the datasource is capable. Sybase
813 // parses the FOR UPDATE clause but ignores it. See the comment above on the
814 // HOLDLOCK for Sybase.
815 if (selectForUpdate
&& CanSelectForUpdate())
816 wxStrcat(pSqlStmt
, " FOR UPDATE");
818 } // wxTable::GetSelectStmt()
820 /********** wxTable::GetRowNum() **********/
821 UWORD
wxTable::GetRowNum(void)
825 if (SQLGetStmtOption(hstmt
, SQL_ROW_NUMBER
, (UCHAR
*) &rowNum
) != SQL_SUCCESS
)
827 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
831 // Completed successfully
832 return((UWORD
) rowNum
);
834 } // wxTable::GetRowNum()
836 /********** wxTable::CloseCursor() **********/
837 bool wxTable::CloseCursor(HSTMT cursor
)
839 if (SQLFreeStmt(cursor
, SQL_CLOSE
) != SQL_SUCCESS
)
840 return(pDb
->DispAllErrors(henv
, hdbc
, cursor
));
842 // Completed successfully
845 } // wxTable::CloseCursor()
847 /********** wxTable::CreateTable() **********/
848 bool wxTable::CreateTable(bool attemptDrop
)
854 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
856 #ifdef DBDEBUG_CONSOLE
857 cout
<< "Creating Table " << tableName
<< "..." << endl
;
861 if (attemptDrop
&& !DropTable())
865 #ifdef DBDEBUG_CONSOLE
866 for (i
= 0; i
< noCols
; i
++)
868 // Exclude derived columns since they are NOT part of the base table
869 if (colDefs
[i
].DerivedCol
)
871 cout
<< i
+ 1 << ": " << colDefs
[i
].ColName
<< "; ";
872 switch(colDefs
[i
].DbDataType
)
874 case DB_DATA_TYPE_VARCHAR
:
875 cout
<< pDb
->typeInfVarchar
.TypeName
<< "(" << colDefs
[i
].SzDataObj
<< ")";
877 case DB_DATA_TYPE_INTEGER
:
878 cout
<< pDb
->typeInfInteger
.TypeName
;
880 case DB_DATA_TYPE_FLOAT
:
881 cout
<< pDb
->typeInfFloat
.TypeName
;
883 case DB_DATA_TYPE_DATE
:
884 cout
<< pDb
->typeInfDate
.TypeName
;
891 // Build a CREATE TABLE string from the colDefs structure.
892 bool needComma
= FALSE
;
893 sprintf(sqlStmt
, "CREATE TABLE %s (", tableName
);
894 for (i
= 0; i
< noCols
; i
++)
896 // Exclude derived columns since they are NOT part of the base table
897 if (colDefs
[i
].DerivedCol
)
901 wxStrcat(sqlStmt
, ",");
903 wxStrcat(sqlStmt
, colDefs
[i
].ColName
);
904 wxStrcat(sqlStmt
, " ");
906 switch(colDefs
[i
].DbDataType
)
908 case DB_DATA_TYPE_VARCHAR
:
909 wxStrcat(sqlStmt
, pDb
->typeInfVarchar
.TypeName
); break;
910 case DB_DATA_TYPE_INTEGER
:
911 wxStrcat(sqlStmt
, pDb
->typeInfInteger
.TypeName
); break;
912 case DB_DATA_TYPE_FLOAT
:
913 wxStrcat(sqlStmt
, pDb
->typeInfFloat
.TypeName
); break;
914 case DB_DATA_TYPE_DATE
:
915 wxStrcat(sqlStmt
, pDb
->typeInfDate
.TypeName
); break;
917 // For varchars, append the size of the string
918 if (colDefs
[i
].DbDataType
== DB_DATA_TYPE_VARCHAR
)
921 // wxStrcat(sqlStmt, "(");
922 // wxStrcat(sqlStmt, itoa(colDefs[i].SzDataObj, s, 10));
923 // wxStrcat(sqlStmt, ")");
924 sprintf(s
, "(%d)", colDefs
[i
].SzDataObj
);
925 wxStrcat(sqlStmt
, s
);
928 if (pDb
->Dbms() == dbmsSYBASE_ASE
|| pDb
->Dbms() == dbmsMY_SQL
)
930 if (colDefs
[i
].KeyField
)
932 wxStrcat(sqlStmt
, " NOT NULL");
938 // If there is a primary key defined, include it in the create statement
939 for (i
= j
= 0; i
< noCols
; i
++)
941 if (colDefs
[i
].KeyField
)
947 if (j
&& pDb
->Dbms() != dbmsDBASE
) // Found a keyfield
949 if (pDb
->Dbms() != dbmsMY_SQL
)
951 wxStrcat(sqlStmt
, ",CONSTRAINT ");
952 wxStrcat(sqlStmt
, tableName
);
953 wxStrcat(sqlStmt
, "_PIDX PRIMARY KEY (");
957 /* MySQL goes out on this one. We also declare the relevant key NON NULL above */
958 wxStrcat(sqlStmt
, ", PRIMARY KEY (");
961 // List column name(s) of column(s) comprising the primary key
962 for (i
= j
= 0; i
< noCols
; i
++)
964 if (colDefs
[i
].KeyField
)
966 if (j
++) // Multi part key, comma separate names
967 wxStrcat(sqlStmt
, ",");
968 wxStrcat(sqlStmt
, colDefs
[i
].ColName
);
971 wxStrcat(sqlStmt
, ")");
973 // Append the closing parentheses for the create table statement
974 wxStrcat(sqlStmt
, ")");
976 pDb
->WriteSqlLog(sqlStmt
);
978 #ifdef DBDEBUG_CONSOLE
979 cout
<< endl
<< sqlStmt
<< endl
;
982 // Execute the CREATE TABLE statement
983 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
985 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
986 pDb
->RollbackTrans();
991 // Commit the transaction and close the cursor
992 if (! pDb
->CommitTrans())
994 if (! CloseCursor(hstmt
))
997 // Database table created successfully
1000 } // wxTable::CreateTable()
1002 /********** wxTable::DropTable() **********/
1003 bool wxTable::DropTable()
1005 // NOTE: This function returns TRUE if the Table does not exist, but
1006 // only for identified databases. Code will need to be added
1007 // below for any other databases when those databases are defined
1008 // to handle this situation consistently
1010 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1012 sprintf(sqlStmt
, "DROP TABLE %s", tableName
);
1014 pDb
->WriteSqlLog(sqlStmt
);
1016 #ifdef DBDEBUG_CONSOLE
1017 cout
<< endl
<< sqlStmt
<< endl
;
1020 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
1022 // Check for "Base table not found" error and ignore
1023 pDb
->GetNextError(henv
, hdbc
, hstmt
);
1024 if (wxStrcmp(pDb
->sqlState
,"S0002")) // "Base table not found"
1026 // Check for product specific error codes
1027 if (!((pDb
->Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(pDb
->sqlState
,"42000")) || // 5.x (and lower?)
1028 (pDb
->Dbms() == dbmsMY_SQL
&& !wxStrcmp(pDb
->sqlState
,"S1000")) || // untested
1029 (pDb
->Dbms() == dbmsPOSTGRES
&& !wxStrcmp(pDb
->sqlState
,"08S01")))) // untested
1031 pDb
->DispNextError();
1032 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
1033 pDb
->RollbackTrans();
1040 // Commit the transaction and close the cursor
1041 if (! pDb
->CommitTrans())
1043 if (! CloseCursor(hstmt
))
1047 } // wxTable::DropTable()
1049 /********** wxTable::CreateIndex() **********/
1050 bool wxTable::CreateIndex(const char * idxName
, bool unique
, int noIdxCols
, CidxDef
*pIdxDefs
, bool attemptDrop
)
1052 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1054 // Drop the index first
1055 if (attemptDrop
&& !DropIndex(idxName
))
1058 // Build a CREATE INDEX statement
1059 wxStrcpy(sqlStmt
, "CREATE ");
1061 wxStrcat(sqlStmt
, "UNIQUE ");
1063 wxStrcat(sqlStmt
, "INDEX ");
1064 wxStrcat(sqlStmt
, idxName
);
1065 wxStrcat(sqlStmt
, " ON ");
1066 wxStrcat(sqlStmt
, tableName
);
1067 wxStrcat(sqlStmt
, " (");
1069 // Append list of columns making up index
1071 for (i
= 0; i
< noIdxCols
; i
++)
1073 wxStrcat(sqlStmt
, pIdxDefs
[i
].ColName
);
1074 /* Postgres doesn't cope with ASC */
1075 if (pDb
->Dbms() != dbmsPOSTGRES
)
1077 if (pIdxDefs
[i
].Ascending
)
1078 wxStrcat(sqlStmt
, " ASC");
1080 wxStrcat(sqlStmt
, " DESC");
1083 if ((i
+ 1) < noIdxCols
)
1084 wxStrcat(sqlStmt
, ",");
1087 // Append closing parentheses
1088 wxStrcat(sqlStmt
, ")");
1090 pDb
->WriteSqlLog(sqlStmt
);
1092 #ifdef DBDEBUG_CONSOLE
1093 cout
<< endl
<< sqlStmt
<< endl
<< endl
;
1096 // Execute the CREATE INDEX statement
1097 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
1099 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
1100 pDb
->RollbackTrans();
1105 // Commit the transaction and close the cursor
1106 if (! pDb
->CommitTrans())
1108 if (! CloseCursor(hstmt
))
1111 // Index Created Successfully
1114 } // wxTable::CreateIndex()
1116 /********** wxTable::DropIndex() **********/
1117 bool wxTable::DropIndex(const char * idxName
)
1119 // NOTE: This function returns TRUE if the Index does not exist, but
1120 // only for identified databases. Code will need to be added
1121 // below for any other databases when those databases are defined
1122 // to handle this situation consistently
1124 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1126 if (pDb
->Dbms() == dbmsACCESS
)
1127 sprintf(sqlStmt
, "DROP INDEX %s ON %s",idxName
,tableName
);
1128 else if (pDb
->Dbms() == dbmsSYBASE_ASE
)
1129 sprintf(sqlStmt
, "DROP INDEX %s.%s",tableName
,idxName
);
1131 sprintf(sqlStmt
, "DROP INDEX %s",idxName
);
1133 pDb
->WriteSqlLog(sqlStmt
);
1135 #ifdef DBDEBUG_CONSOLE
1136 cout
<< endl
<< sqlStmt
<< endl
;
1139 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
1141 // Check for "Index not found" error and ignore
1142 pDb
->GetNextError(henv
, hdbc
, hstmt
);
1143 if (wxStrcmp(pDb
->sqlState
,"S0012")) // "Index not found"
1145 // Check for product specific error codes
1146 if (!((pDb
->Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(pDb
->sqlState
,"42000")) || // v5.x (and lower?)
1147 (pDb
->Dbms() == dbmsSYBASE_ASE
&& !wxStrcmp(pDb
->sqlState
,"S0002")) || // Base table not found
1148 (pDb
->Dbms() == dbmsMY_SQL
&& !wxStrcmp(pDb
->sqlState
,"42S02")) // untested
1151 pDb
->DispNextError();
1152 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
1153 pDb
->RollbackTrans();
1160 // Commit the transaction and close the cursor
1161 if (! pDb
->CommitTrans())
1163 if (! CloseCursor(hstmt
))
1167 } // wxTable::DropIndex()
1169 /********** wxTable::Insert() **********/
1170 int wxTable::Insert(void)
1178 // Insert the record by executing the already prepared insert statement
1180 retcode
=SQLExecute(hstmtInsert
);
1181 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1183 // Check to see if integrity constraint was violated
1184 pDb
->GetNextError(henv
, hdbc
, hstmtInsert
);
1185 if (! wxStrcmp(pDb
->sqlState
, "23000")) // Integrity constraint violated
1186 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1189 pDb
->DispNextError();
1190 pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
);
1195 // Record inserted into the datasource successfully
1198 } // wxTable::Insert()
1200 /********** wxTable::Update() **********/
1201 bool wxTable::Update(void)
1207 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1209 // Build the SQL UPDATE statement
1210 GetUpdateStmt(sqlStmt
, DB_UPD_KEYFIELDS
);
1212 pDb
->WriteSqlLog(sqlStmt
);
1214 #ifdef DBDEBUG_CONSOLE
1215 cout
<< endl
<< sqlStmt
<< endl
<< endl
;
1218 // Execute the SQL UPDATE statement
1219 return(execUpdate(sqlStmt
));
1221 } // wxTable::Update()
1223 /********** wxTable::Update(pSqlStmt) **********/
1224 bool wxTable::Update(const char *pSqlStmt
)
1230 pDb
->WriteSqlLog(pSqlStmt
);
1232 return(execUpdate(pSqlStmt
));
1234 } // wxTable::Update(pSqlStmt)
1236 /********** wxTable::UpdateWhere() **********/
1237 bool wxTable::UpdateWhere(const char *pWhereClause
)
1243 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1245 // Build the SQL UPDATE statement
1246 GetUpdateStmt(sqlStmt
, DB_UPD_WHERE
, pWhereClause
);
1248 pDb
->WriteSqlLog(sqlStmt
);
1250 #ifdef DBDEBUG_CONSOLE
1251 cout
<< endl
<< sqlStmt
<< endl
<< endl
;
1254 // Execute the SQL UPDATE statement
1255 return(execUpdate(sqlStmt
));
1257 } // wxTable::UpdateWhere()
1259 /********** wxTable::Delete() **********/
1260 bool wxTable::Delete(void)
1266 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1268 // Build the SQL DELETE statement
1269 GetDeleteStmt(sqlStmt
, DB_DEL_KEYFIELDS
);
1271 pDb
->WriteSqlLog(sqlStmt
);
1273 // Execute the SQL DELETE statement
1274 return(execDelete(sqlStmt
));
1276 } // wxTable::Delete()
1278 /********** wxTable::DeleteWhere() **********/
1279 bool wxTable::DeleteWhere(const char *pWhereClause
)
1285 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1287 // Build the SQL DELETE statement
1288 GetDeleteStmt(sqlStmt
, DB_DEL_WHERE
, pWhereClause
);
1290 pDb
->WriteSqlLog(sqlStmt
);
1292 // Execute the SQL DELETE statement
1293 return(execDelete(sqlStmt
));
1295 } // wxTable::DeleteWhere()
1297 /********** wxTable::DeleteMatching() **********/
1298 bool wxTable::DeleteMatching(void)
1304 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1306 // Build the SQL DELETE statement
1307 GetDeleteStmt(sqlStmt
, DB_DEL_MATCHING
);
1309 pDb
->WriteSqlLog(sqlStmt
);
1311 // Execute the SQL DELETE statement
1312 return(execDelete(sqlStmt
));
1314 } // wxTable::DeleteMatching()
1316 /********** wxTable::GetUpdateStmt() **********/
1317 void wxTable::GetUpdateStmt(char *pSqlStmt
, int typeOfUpd
, const char *pWhereClause
)
1323 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
];
1324 bool firstColumn
= TRUE
;
1327 sprintf(pSqlStmt
, "UPDATE %s SET ", tableName
);
1329 // Append a list of columns to be updated
1331 for (i
= 0; i
< noCols
; i
++)
1333 // Only append Updateable columns
1334 if (colDefs
[i
].Updateable
)
1337 wxStrcat(pSqlStmt
, ",");
1339 firstColumn
= FALSE
;
1340 wxStrcat(pSqlStmt
, colDefs
[i
].ColName
);
1341 wxStrcat(pSqlStmt
, " = ?");
1345 // Append the WHERE clause to the SQL UPDATE statement
1346 wxStrcat(pSqlStmt
, " WHERE ");
1349 case DB_UPD_KEYFIELDS
:
1350 // If the datasource supports the ROWID column, build
1351 // the where on ROWID for efficiency purposes.
1352 // e.g. UPDATE PARTS SET Col1 = ?, Col2 = ? WHERE ROWID = '111.222.333'
1353 if (CanUpdByROWID())
1356 char rowid
[ROWID_LEN
];
1358 // Get the ROWID value. If not successful retreiving the ROWID,
1359 // simply fall down through the code and build the WHERE clause
1360 // based on the key fields.
1361 if (SQLGetData(hstmt
, noCols
+1, SQL_C_CHAR
, (UCHAR
*) rowid
, ROWID_LEN
, &cb
) == SQL_SUCCESS
)
1363 wxStrcat(pSqlStmt
, "ROWID = '");
1364 wxStrcat(pSqlStmt
, rowid
);
1365 wxStrcat(pSqlStmt
, "'");
1369 // Unable to delete by ROWID, so build a WHERE
1370 // clause based on the keyfields.
1371 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
);
1372 wxStrcat(pSqlStmt
, whereClause
);
1375 wxStrcat(pSqlStmt
, pWhereClause
);
1378 } // GetUpdateStmt()
1380 /********** wxTable::GetDeleteStmt() **********/
1381 void wxTable::GetDeleteStmt(char *pSqlStmt
, int typeOfDel
, const char *pWhereClause
)
1387 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
];
1391 // Handle the case of DeleteWhere() and the where clause is blank. It should
1392 // delete all records from the database in this case.
1393 if (typeOfDel
== DB_DEL_WHERE
&& (pWhereClause
== 0 || wxStrlen(pWhereClause
) == 0))
1395 sprintf(pSqlStmt
, "DELETE FROM %s", tableName
);
1399 sprintf(pSqlStmt
, "DELETE FROM %s WHERE ", tableName
);
1401 // Append the WHERE clause to the SQL DELETE statement
1404 case DB_DEL_KEYFIELDS
:
1405 // If the datasource supports the ROWID column, build
1406 // the where on ROWID for efficiency purposes.
1407 // e.g. DELETE FROM PARTS WHERE ROWID = '111.222.333'
1408 if (CanUpdByROWID())
1411 char rowid
[ROWID_LEN
];
1413 // Get the ROWID value. If not successful retreiving the ROWID,
1414 // simply fall down through the code and build the WHERE clause
1415 // based on the key fields.
1416 if (SQLGetData(hstmt
, noCols
+1, SQL_C_CHAR
, (UCHAR
*) rowid
, ROWID_LEN
, &cb
) == SQL_SUCCESS
)
1418 wxStrcat(pSqlStmt
, "ROWID = '");
1419 wxStrcat(pSqlStmt
, rowid
);
1420 wxStrcat(pSqlStmt
, "'");
1424 // Unable to delete by ROWID, so build a WHERE
1425 // clause based on the keyfields.
1426 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
);
1427 wxStrcat(pSqlStmt
, whereClause
);
1430 wxStrcat(pSqlStmt
, pWhereClause
);
1432 case DB_DEL_MATCHING
:
1433 GetWhereClause(whereClause
, DB_WHERE_MATCHING
);
1434 wxStrcat(pSqlStmt
, whereClause
);
1438 } // GetDeleteStmt()
1440 /********** wxTable::GetWhereClause() **********/
1442 * Note: GetWhereClause() currently ignores timestamp columns.
1443 * They are not included as part of the where clause.
1446 void wxTable::GetWhereClause(char *pWhereClause
, int typeOfWhere
, const char *qualTableName
)
1448 bool moreThanOneColumn
= FALSE
;
1451 // Loop through the columns building a where clause as you go
1453 for (i
= 0; i
< noCols
; i
++)
1455 // Determine if this column should be included in the WHERE clause
1456 if ((typeOfWhere
== DB_WHERE_KEYFIELDS
&& colDefs
[i
].KeyField
) ||
1457 (typeOfWhere
== DB_WHERE_MATCHING
&& (! IsColNull(i
))))
1459 // Skip over timestamp columns
1460 if (colDefs
[i
].SqlCtype
== SQL_C_TIMESTAMP
)
1462 // If there is more than 1 column, join them with the keyword "AND"
1463 if (moreThanOneColumn
)
1464 wxStrcat(pWhereClause
, " AND ");
1466 moreThanOneColumn
= TRUE
;
1467 // Concatenate where phrase for the column
1468 if (qualTableName
&& wxStrlen(qualTableName
))
1470 wxStrcat(pWhereClause
, qualTableName
);
1471 wxStrcat(pWhereClause
, ".");
1473 wxStrcat(pWhereClause
, colDefs
[i
].ColName
);
1474 wxStrcat(pWhereClause
, " = ");
1475 switch(colDefs
[i
].SqlCtype
)
1478 sprintf(colValue
, "'%s'", (UCHAR FAR
*) colDefs
[i
].PtrDataObj
);
1481 sprintf(colValue
, "%hi", *((SWORD
*) colDefs
[i
].PtrDataObj
));
1484 sprintf(colValue
, "%hu", *((UWORD
*) colDefs
[i
].PtrDataObj
));
1487 sprintf(colValue
, "%li", *((SDWORD
*) colDefs
[i
].PtrDataObj
));
1490 sprintf(colValue
, "%lu", *((UDWORD
*) colDefs
[i
].PtrDataObj
));
1493 sprintf(colValue
, "%.6f", *((SFLOAT
*) colDefs
[i
].PtrDataObj
));
1496 sprintf(colValue
, "%.6f", *((SDOUBLE
*) colDefs
[i
].PtrDataObj
));
1499 wxStrcat(pWhereClause
, colValue
);
1503 } // wxTable::GetWhereClause()
1505 /********** wxTable::IsColNull() **********/
1506 bool wxTable::IsColNull(int colNo
)
1508 switch(colDefs
[colNo
].SqlCtype
)
1511 return(((UCHAR FAR
*) colDefs
[colNo
].PtrDataObj
)[0] == 0);
1513 return(( *((SWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1515 return(( *((UWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1517 return(( *((SDWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1519 return(( *((UDWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1521 return(( *((SFLOAT
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1523 return((*((SDOUBLE
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1524 case SQL_C_TIMESTAMP
:
1525 TIMESTAMP_STRUCT
*pDt
;
1526 pDt
= (TIMESTAMP_STRUCT
*) colDefs
[colNo
].PtrDataObj
;
1527 if (pDt
->year
== 0 && pDt
->month
== 0 && pDt
->day
== 0)
1535 } // wxTable::IsColNull()
1537 /********** wxTable::CanSelectForUpdate() **********/
1538 bool wxTable::CanSelectForUpdate(void)
1540 if (pDb
->Dbms() == dbmsMY_SQL
)
1543 if (pDb
->dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1548 } // wxTable::CanSelectForUpdate()
1550 /********** wxTable::CanUpdByROWID() **********/
1551 bool wxTable::CanUpdByROWID(void)
1554 //NOTE: Returning FALSE for now until this can be debugged,
1555 // as the ROWID is not getting updated correctly
1558 if (pDb
->Dbms() == dbmsORACLE
)
1563 } // wxTable::CanUpdByROWID()
1565 /********** wxTable::IsCursorClosedOnCommit() **********/
1566 bool wxTable::IsCursorClosedOnCommit(void)
1568 if (pDb
->dbInf
.cursorCommitBehavior
== SQL_CB_PRESERVE
)
1573 } // wxTable::IsCursorClosedOnCommit()
1575 /********** wxTable::ClearMemberVars() **********/
1576 void wxTable::ClearMemberVars(void)
1578 // Loop through the columns setting each member variable to zero
1580 for (i
= 0; i
< noCols
; i
++)
1582 switch(colDefs
[i
].SqlCtype
)
1585 ((UCHAR FAR
*) colDefs
[i
].PtrDataObj
)[0] = 0;
1588 *((SWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1591 *((UWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1594 *((SDWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1597 *((UDWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1600 *((SFLOAT
*) colDefs
[i
].PtrDataObj
) = 0.0f
;
1603 *((SDOUBLE
*) colDefs
[i
].PtrDataObj
) = 0.0f
;
1605 case SQL_C_TIMESTAMP
:
1606 TIMESTAMP_STRUCT
*pDt
;
1607 pDt
= (TIMESTAMP_STRUCT
*) colDefs
[i
].PtrDataObj
;
1619 } // wxTable::ClearMemberVars()
1621 /********** wxTable::SetQueryTimeout() **********/
1622 bool wxTable::SetQueryTimeout(UDWORD nSeconds
)
1624 if (SQLSetStmtOption(hstmtInsert
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1625 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
));
1626 if (SQLSetStmtOption(hstmtUpdate
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1627 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
));
1628 if (SQLSetStmtOption(hstmtDelete
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1629 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
));
1630 if (SQLSetStmtOption(hstmtInternal
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1631 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
));
1633 // Completed Successfully
1636 } // wxTable::SetQueryTimeout()
1638 /********** wxTable::SetColDefs() **********/
1639 void wxTable::SetColDefs (int index
, const char *fieldName
, int dataType
, void *pData
,
1640 int cType
, int size
, bool keyField
, bool upd
,
1641 bool insAllow
, bool derivedCol
)
1643 if (!colDefs
) // May happen if the database connection fails
1646 if (wxStrlen(fieldName
) > (unsigned int) DB_MAX_COLUMN_NAME_LEN
)
1648 wxStrncpy (colDefs
[index
].ColName
, fieldName
, DB_MAX_COLUMN_NAME_LEN
);
1649 colDefs
[index
].ColName
[DB_MAX_COLUMN_NAME_LEN
] = 0;
1652 wxStrcpy(colDefs
[index
].ColName
, fieldName
);
1654 colDefs
[index
].DbDataType
= dataType
;
1655 colDefs
[index
].PtrDataObj
= pData
;
1656 colDefs
[index
].SqlCtype
= cType
;
1657 colDefs
[index
].SzDataObj
= size
;
1658 colDefs
[index
].KeyField
= keyField
;
1659 colDefs
[index
].DerivedCol
= derivedCol
;
1660 // Derived columns by definition would NOT be "Insertable" or "Updateable"
1663 colDefs
[index
].Updateable
= FALSE
;
1664 colDefs
[index
].InsertAllowed
= FALSE
;
1668 colDefs
[index
].Updateable
= upd
;
1669 colDefs
[index
].InsertAllowed
= insAllow
;
1672 colDefs
[index
].Null
= FALSE
;
1674 } // wxTable::SetColDefs()
1676 /********** wxTable::SetCursor() **********/
1677 void wxTable::SetCursor(HSTMT
*hstmtActivate
)
1679 if (hstmtActivate
== DEFAULT_CURSOR
)
1680 hstmt
= *hstmtDefault
;
1682 hstmt
= *hstmtActivate
;
1684 } // wxTable::SetCursor()
1686 /********** wxTable::Count(const char *) **********/
1687 ULONG
wxTable::Count(const char *args
)
1690 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1693 // Build a "SELECT COUNT(*) FROM queryTableName [WHERE whereClause]" SQL Statement
1694 wxStrcpy(sqlStmt
, "SELECT COUNT(");
1695 wxStrcat(sqlStmt
, args
);
1696 wxStrcat(sqlStmt
, ") FROM ");
1697 wxStrcat(sqlStmt
, queryTableName
);
1699 if (from
&& wxStrlen(from
))
1700 wxStrcat(sqlStmt
, from
);
1702 // Add the where clause if one is provided
1703 if (where
&& wxStrlen(where
))
1705 wxStrcat(sqlStmt
, " WHERE ");
1706 wxStrcat(sqlStmt
, where
);
1709 pDb
->WriteSqlLog(sqlStmt
);
1711 // Initialize the Count cursor if it's not already initialized
1714 hstmtCount
= NewCursor(FALSE
,FALSE
);
1720 // Execute the SQL statement
1721 if (SQLExecDirect(*hstmtCount
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
1723 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1728 if (SQLFetch(*hstmtCount
) != SQL_SUCCESS
)
1730 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1734 // Obtain the result
1735 if (SQLGetData(*hstmtCount
, 1, SQL_C_ULONG
, &l
, sizeof(l
), &cb
) != SQL_SUCCESS
)
1737 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1742 if (SQLFreeStmt(*hstmtCount
, SQL_CLOSE
) != SQL_SUCCESS
)
1743 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1745 // Return the record count
1748 } // wxTable::Count()
1750 /********** wxTable::Refresh() **********/
1751 bool wxTable::Refresh(void)
1755 // Switch to the internal cursor so any active cursors are not corrupted
1756 HSTMT currCursor
= GetCursor();
1757 hstmt
= hstmtInternal
;
1759 // Save the where and order by clauses
1760 char *saveWhere
= where
;
1761 char *saveOrderBy
= orderBy
;
1763 // Build a where clause to refetch the record with. Try and use the
1764 // ROWID if it's available, ow use the key fields.
1765 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
+1];
1766 wxStrcpy(whereClause
, "");
1767 if (CanUpdByROWID())
1770 char rowid
[ROWID_LEN
+1];
1772 // Get the ROWID value. If not successful retreiving the ROWID,
1773 // simply fall down through the code and build the WHERE clause
1774 // based on the key fields.
1775 if (SQLGetData(hstmt
, noCols
+1, SQL_C_CHAR
, (UCHAR
*) rowid
, ROWID_LEN
, &cb
) == SQL_SUCCESS
)
1777 wxStrcat(whereClause
, queryTableName
);
1778 wxStrcat(whereClause
, ".ROWID = '");
1779 wxStrcat(whereClause
, rowid
);
1780 wxStrcat(whereClause
, "'");
1784 // If unable to use the ROWID, build a where clause from the keyfields
1785 if (wxStrlen(whereClause
) == 0)
1786 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
, queryTableName
);
1788 // Requery the record
1789 where
= whereClause
;
1794 if (result
&& !GetNext())
1797 // Switch back to original cursor
1798 SetCursor(&currCursor
);
1800 // Free the internal cursor
1801 if (SQLFreeStmt(hstmtInternal
, SQL_CLOSE
) != SQL_SUCCESS
)
1802 pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
);
1804 // Restore the original where and order by clauses
1806 orderBy
= saveOrderBy
;
1810 } // wxTable::Refresh()
1812 /********** wxTable::SetNull(UINT colNo) **********/
1813 bool wxTable::SetNull(int colNo
)
1816 return(colDefs
[colNo
].Null
= TRUE
);
1820 } // wxTable::SetNull(UINT colNo)
1822 /********** wxTable::SetNull(char *colName) **********/
1823 bool wxTable::SetNull(const char *colName
)
1826 for (i
= 0; i
< noCols
; i
++)
1828 if (!wxStricmp(colName
, colDefs
[i
].ColName
))
1833 return(colDefs
[i
].Null
= TRUE
);
1837 } // wxTable::SetNull(char *colName)
1839 /********** wxTable::NewCursor() **********/
1840 HSTMT
*wxTable::NewCursor(bool setCursor
, bool bindColumns
)
1842 HSTMT
*newHSTMT
= new HSTMT
;
1847 if (SQLAllocStmt(hdbc
, newHSTMT
) != SQL_SUCCESS
)
1849 pDb
->DispAllErrors(henv
, hdbc
);
1854 if (SQLSetStmtOption(*newHSTMT
, SQL_CURSOR_TYPE
, cursorType
) != SQL_SUCCESS
)
1856 pDb
->DispAllErrors(henv
, hdbc
, *newHSTMT
);
1863 if(!bindCols(*newHSTMT
))
1871 SetCursor(newHSTMT
);
1875 } // wxTable::NewCursor()
1877 /********** wxTable::DeleteCursor() **********/
1878 bool wxTable::DeleteCursor(HSTMT
*hstmtDel
)
1882 if (!hstmtDel
) // Cursor already deleted
1885 if (SQLFreeStmt(*hstmtDel
, SQL_DROP
) != SQL_SUCCESS
)
1887 pDb
->DispAllErrors(henv
, hdbc
);
1895 } // wxTable::DeleteCursor()
1897 #endif // wxUSE_ODBC