1 ///////////////////////////////////////////////////////////////////////////////
3 // Purpose: Implementation of the wxTable class.
5 // Modified by: George Tasker
7 // -Dynamic cursor support - Only one predefined cursor, as many others as
8 // you need may be created on demand
9 // -Reduced number of active cursors significantly
10 // -Query-Only wxTable objects
13 // Copyright: (c) 1996 Remstar International, Inc.
14 // Licence: wxWindows licence, plus:
15 // Notice: This class library and its intellectual design are free of charge for use,
16 // modification, enhancement, debugging under the following conditions:
17 // 1) These classes may only be used as part of the implementation of a
18 // wxWindows-based application
19 // 2) All enhancements and bug fixes are to be submitted back to the wxWindows
20 // user groups free of all charges for use with the wxWindows library.
21 // 3) These classes may not be distributed as part of any other class library,
22 // DLL, text (written or electronic), other than a complete distribution of
23 // the wxWindows GUI development toolkit.
24 ///////////////////////////////////////////////////////////////////////////////
31 // Use this line for wxWindows v1.x
33 // Use this line for wxWindows v2.x
34 #include "wx/version.h"
35 #include "wx/wxprec.h"
37 #if wxMAJOR_VERSION == 2
39 # pragma implementation "dbtable.h"
43 #ifdef DBDEBUG_CONSOLE
44 #include "wx/ioswrap.h"
51 #if wxMAJOR_VERSION == 2
53 #include "wx/string.h"
54 #include "wx/object.h"
57 #include "wx/msgdlg.h"
60 #include "wx/filefn.h"
63 #if wxMAJOR_VERSION == 1
64 # if defined(wx_msw) || defined(wx_x)
81 #if wxMAJOR_VERSION == 1
83 #elif wxMAJOR_VERSION == 2
84 #include "wx/dbtable.h"
88 // The HPUX preprocessor lines below were commented out on 8/20/97
89 // because macros.h currently redefines DEBUG and is unneeded.
91 // # include <macros.h>
94 # include <sys/minmax.h>
98 ULONG lastTableID
= 0;
106 /********** wxTable::wxTable() **********/
107 wxTable::wxTable(wxDB
*pwxDB
, const char *tblName
, const int nCols
,
108 const char *qryTblName
, bool qryOnly
, const char *tblPath
)
110 pDb
= pwxDB
; // Pointer to the wxDB object
114 hstmtDefault
= 0; // Initialized below
115 hstmtCount
= 0; // Initialized first time it is needed
122 noCols
= nCols
; // No. of cols in the table
123 where
= 0; // Where clause
124 orderBy
= 0; // Order By clause
125 from
= 0; // From clause
126 selectForUpdate
= FALSE
; // SELECT ... FOR UPDATE; Indicates whether to include the FOR UPDATE phrase
131 wxStrcpy(tableName
, tblName
); // Table Name
133 wxStrcpy(tablePath
, tblPath
); // Table Path - used for dBase files
135 if (qryTblName
) // Name of the table/view to query
136 wxStrcpy(queryTableName
, qryTblName
);
138 wxStrcpy(queryTableName
, tblName
);
146 tableID
= ++lastTableID
;
147 s
.sprintf("wxTable constructor (%-20s) tableID:[%6lu] pDb:[%p]", tblName
,tableID
,pDb
);
150 wxTablesInUse
*tableInUse
;
151 tableInUse
= new wxTablesInUse();
152 tableInUse
->tableName
= tblName
;
153 tableInUse
->tableID
= tableID
;
154 tableInUse
->pDb
= pDb
;
155 TablesInUse
.Append(tableInUse
);
158 pDb
->WriteSqlLog(s
.GetData());
160 // Grab the HENV and HDBC from the wxDB object
164 // Allocate space for column definitions
166 colDefs
= new wxColDef
[noCols
]; // Points to the first column defintion
168 // Allocate statement handles for the table
171 // Allocate a separate statement handle for performing inserts
172 if (SQLAllocStmt(hdbc
, &hstmtInsert
) != SQL_SUCCESS
)
173 pDb
->DispAllErrors(henv
, hdbc
);
174 // Allocate a separate statement handle for performing deletes
175 if (SQLAllocStmt(hdbc
, &hstmtDelete
) != SQL_SUCCESS
)
176 pDb
->DispAllErrors(henv
, hdbc
);
177 // Allocate a separate statement handle for performing updates
178 if (SQLAllocStmt(hdbc
, &hstmtUpdate
) != SQL_SUCCESS
)
179 pDb
->DispAllErrors(henv
, hdbc
);
181 // Allocate a separate statement handle for internal use
182 if (SQLAllocStmt(hdbc
, &hstmtInternal
) != SQL_SUCCESS
)
183 pDb
->DispAllErrors(henv
, hdbc
);
185 // Set the cursor type for the statement handles
186 cursorType
= SQL_CURSOR_STATIC
;
187 if (SQLSetStmtOption(hstmtInternal
, SQL_CURSOR_TYPE
, cursorType
) != SQL_SUCCESS
)
189 // Check to see if cursor type is supported
190 pDb
->GetNextError(henv
, hdbc
, hstmtInternal
);
191 if (! wxStrcmp(pDb
->sqlState
, "01S02")) // Option Value Changed
193 // Datasource does not support static cursors. Driver
194 // will substitute a cursor type. Call SQLGetStmtOption()
195 // to determine which cursor type was selected.
196 if (SQLGetStmtOption(hstmtInternal
, SQL_CURSOR_TYPE
, &cursorType
) != SQL_SUCCESS
)
197 pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
);
198 #ifdef DBDEBUG_CONSOLE
199 cout
<< "Static cursor changed to: ";
202 case SQL_CURSOR_FORWARD_ONLY
:
203 cout
<< "Forward Only"; break;
204 case SQL_CURSOR_STATIC
:
205 cout
<< "Static"; break;
206 case SQL_CURSOR_KEYSET_DRIVEN
:
207 cout
<< "Keyset Driven"; break;
208 case SQL_CURSOR_DYNAMIC
:
209 cout
<< "Dynamic"; break;
211 cout
<< endl
<< endl
;
216 pDb
->DispNextError();
217 pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
);
220 #ifdef DBDEBUG_CONSOLE
222 cout
<< "Cursor Type set to STATIC" << endl
<< endl
;
227 // Set the cursor type for the INSERT statement handle
228 if (SQLSetStmtOption(hstmtInsert
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
)
229 pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
);
230 // Set the cursor type for the DELETE statement handle
231 if (SQLSetStmtOption(hstmtDelete
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
)
232 pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
);
233 // Set the cursor type for the UPDATE statement handle
234 if (SQLSetStmtOption(hstmtUpdate
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
)
235 pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
);
238 // Make the default cursor the active cursor
239 hstmtDefault
= NewCursor(FALSE
,FALSE
);
240 assert(hstmtDefault
);
241 hstmt
= *hstmtDefault
;
243 } // wxTable::wxTable()
246 /********** wxTable::~wxTable() **********/
252 s
.sprintf("wxTable destructor (%-20s) tableID:[%6lu] pDb:[%p]", tableName
,tableID
,pDb
);
253 pDb
->WriteSqlLog(s
.GetData());
259 TablesInUse
.DeleteContents(TRUE
);
263 pNode
= TablesInUse
.First();
264 while (pNode
&& !found
)
266 if (((wxTablesInUse
*)pNode
->Data())->tableID
== tableID
)
269 if (!TablesInUse
.DeleteNode(pNode
))
270 wxLogDebug (s
.GetData(),"Unable to delete node!");
273 pNode
= pNode
->Next();
278 msg
.sprintf("Unable to find the tableID in the linked\nlist of tables in use.\n\n%s",s
.GetData());
279 wxLogDebug (msg
.GetData(),"NOTICE...");
286 // Decrement the wxDB table count
290 // Delete memory allocated for column definitions
294 // Free statement handles
298 if (SQLFreeStmt(hstmtInsert
, SQL_DROP
) != SQL_SUCCESS
)
299 pDb
->DispAllErrors(henv
, hdbc
);
302 if (SQLFreeStmt(hstmtDelete
, SQL_DROP
) != SQL_SUCCESS
)
305 if (SQLFreeStmt(hstmtUpdate
, SQL_DROP
) != SQL_SUCCESS
)
306 pDb
->DispAllErrors(henv
, hdbc
);
310 if (SQLFreeStmt(hstmtInternal
, SQL_DROP
) != SQL_SUCCESS
)
311 pDb
->DispAllErrors(henv
, hdbc
);
313 // Delete dynamically allocated cursors
315 DeleteCursor(hstmtDefault
);
318 DeleteCursor(hstmtCount
);
321 } // wxTable::~wxTable()
325 /***************************** PRIVATE FUNCTIONS *****************************/
329 /********** wxTable::bindInsertParams() **********/
330 bool wxTable::bindInsertParams(void)
337 UDWORD precision
= 0;
340 // Bind each column (that can be inserted) of the table to a parameter marker
342 for (i
= 0, colNo
= 1; i
< noCols
; i
++)
344 if (! colDefs
[i
].InsertAllowed
)
346 switch(colDefs
[i
].DbDataType
)
348 case DB_DATA_TYPE_VARCHAR
:
349 fSqlType
= pDb
->typeInfVarchar
.FsqlType
;
350 precision
= colDefs
[i
].SzDataObj
;
352 colDefs
[i
].CbValue
= SQL_NTS
;
354 case DB_DATA_TYPE_INTEGER
:
355 fSqlType
= pDb
->typeInfInteger
.FsqlType
;
356 precision
= pDb
->typeInfInteger
.Precision
;
358 colDefs
[i
].CbValue
= 0;
360 case DB_DATA_TYPE_FLOAT
:
361 fSqlType
= pDb
->typeInfFloat
.FsqlType
;
362 precision
= pDb
->typeInfFloat
.Precision
;
363 scale
= pDb
->typeInfFloat
.MaximumScale
;
364 // SQL Sybase Anywhere v5.5 returned a negative number for the
365 // MaxScale. This caused ODBC to kick out an error on ibscale.
366 // I check for this here and set the scale = precision.
368 // scale = (short) precision;
369 colDefs
[i
].CbValue
= 0;
371 case DB_DATA_TYPE_DATE
:
372 fSqlType
= pDb
->typeInfDate
.FsqlType
;
373 precision
= pDb
->typeInfDate
.Precision
;
375 colDefs
[i
].CbValue
= 0;
381 colDefs
[i
].CbValue
= SQL_NULL_DATA
;
382 colDefs
[i
].Null
= FALSE
;
384 if (SQLBindParameter(hstmtInsert
, colNo
++, SQL_PARAM_INPUT
, colDefs
[i
].SqlCtype
,
385 fSqlType
, precision
, scale
, (UCHAR
*) colDefs
[i
].PtrDataObj
,
386 precision
+1,&colDefs
[i
].CbValue
) != SQL_SUCCESS
)
387 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
));
390 // Completed successfully
393 } // wxTable::bindInsertParams()
396 /********** wxTable::bindUpdateParams() **********/
397 bool wxTable::bindUpdateParams(void)
404 UDWORD precision
= 0;
407 // Bind each UPDATEABLE column of the table to a parameter marker
409 for (i
= 0, colNo
= 1; i
< noCols
; i
++)
411 if (! colDefs
[i
].Updateable
)
413 switch(colDefs
[i
].DbDataType
)
415 case DB_DATA_TYPE_VARCHAR
:
416 fSqlType
= pDb
->typeInfVarchar
.FsqlType
;
417 precision
= colDefs
[i
].SzDataObj
;
419 colDefs
[i
].CbValue
= SQL_NTS
;
421 case DB_DATA_TYPE_INTEGER
:
422 fSqlType
= pDb
->typeInfInteger
.FsqlType
;
423 precision
= pDb
->typeInfInteger
.Precision
;
425 colDefs
[i
].CbValue
= 0;
427 case DB_DATA_TYPE_FLOAT
:
428 fSqlType
= pDb
->typeInfFloat
.FsqlType
;
429 precision
= pDb
->typeInfFloat
.Precision
;
430 scale
= pDb
->typeInfFloat
.MaximumScale
;
431 // SQL Sybase Anywhere v5.5 returned a negative number for the
432 // MaxScale. This caused ODBC to kick out an error on ibscale.
433 // I check for this here and set the scale = precision.
435 // scale = (short) precision;
436 colDefs
[i
].CbValue
= 0;
438 case DB_DATA_TYPE_DATE
:
439 fSqlType
= pDb
->typeInfDate
.FsqlType
;
440 precision
= pDb
->typeInfDate
.Precision
;
442 colDefs
[i
].CbValue
= 0;
445 if (SQLBindParameter(hstmtUpdate
, colNo
++, SQL_PARAM_INPUT
, colDefs
[i
].SqlCtype
,
446 fSqlType
, precision
, scale
, (UCHAR
*) colDefs
[i
].PtrDataObj
,
447 precision
+1, &colDefs
[i
].CbValue
) != SQL_SUCCESS
)
448 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
));
451 // Completed successfully
454 } // wxTable::bindUpdateParams()
457 /********** wxTable::bindCols() **********/
458 bool wxTable::bindCols(HSTMT cursor
)
462 // Bind each column of the table to a memory address for fetching data
464 for (i
= 0; i
< noCols
; i
++)
466 if (SQLBindCol(cursor
, i
+1, colDefs
[i
].SqlCtype
, (UCHAR
*) colDefs
[i
].PtrDataObj
,
467 colDefs
[i
].SzDataObj
, &cb
) != SQL_SUCCESS
)
468 return(pDb
->DispAllErrors(henv
, hdbc
, cursor
));
471 // Completed successfully
474 } // wxTable::bindCols()
477 /********** wxTable::getRec() **********/
478 bool wxTable::getRec(UWORD fetchType
)
482 if (!pDb
->FwdOnlyCursors())
484 // Fetch the NEXT, PREV, FIRST or LAST record, depending on fetchType
488 retcode
= SQLExtendedFetch(hstmt
, fetchType
, 0, &cRowsFetched
, &rowStatus
);
489 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
490 if (retcode
== SQL_NO_DATA_FOUND
)
493 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
));
497 // Fetch the next record from the record set
498 retcode
= SQLFetch(hstmt
);
499 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
501 if (retcode
== SQL_NO_DATA_FOUND
)
504 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
));
508 // Completed successfully
511 } // wxTable::getRec()
514 /********** wxTable::execDelete() **********/
515 bool wxTable::execDelete(const char *pSqlStmt
)
517 // Execute the DELETE statement
518 if (SQLExecDirect(hstmtDelete
, (UCHAR FAR
*) pSqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
519 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
));
521 // Record deleted successfully
524 } // wxTable::execDelete()
527 /********** wxTable::execUpdate() **********/
528 bool wxTable::execUpdate(const char *pSqlStmt
)
530 // Execute the UPDATE statement
531 if (SQLExecDirect(hstmtUpdate
, (UCHAR FAR
*) pSqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
532 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
));
534 // Record deleted successfully
537 } // wxTable::execUpdate()
540 /********** wxTable::query() **********/
541 bool wxTable::query(int queryType
, bool forUpdate
, bool distinct
, char *pSqlStmt
)
543 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
545 // Set the selectForUpdate member variable
547 // The user may wish to select for update, but the DBMS may not be capable
548 selectForUpdate
= CanSelectForUpdate();
550 selectForUpdate
= FALSE
;
552 // Set the SQL SELECT string
553 if (queryType
!= DB_SELECT_STATEMENT
) // A select statement was not passed in,
554 { // so generate a select statement.
555 GetSelectStmt(sqlStmt
, queryType
, distinct
);
556 pDb
->WriteSqlLog(sqlStmt
);
559 // Make sure the cursor is closed first
560 if (! CloseCursor(hstmt
))
563 // Execute the SQL SELECT statement
566 retcode
= SQLExecDirect(hstmt
, (UCHAR FAR
*) (queryType
== DB_SELECT_STATEMENT
? pSqlStmt
: sqlStmt
), SQL_NTS
);
567 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
568 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
));
570 // Completed successfully
573 } // wxTable::query()
576 /***************************** PUBLIC FUNCTIONS *****************************/
579 /********** wxTable::Open() **********/
580 bool wxTable::Open(void)
588 // Verify that the table exists in the database
589 if (!pDb
->TableExists(tableName
,pDb
->GetUsername(),tablePath
))
592 if (wxStrcmp(tablePath
,""))
593 s
.sprintf("Error opening '%s/%s'.\n",tablePath
,tableName
);
595 s
.sprintf("Error opening '%s'.\n", tableName
);
596 if (!pDb
->TableExists(tableName
,NULL
,tablePath
))
597 s
+= "Table/view does not exist in the database.\n";
599 s
+= "Current logged in user does not have sufficient privileges to access this table.\n";
600 pDb
->LogError(s
.GetData());
604 // Bind the member variables for field exchange between
605 // the wxTable object and the ODBC record.
608 if (!bindInsertParams()) // Inserts
610 if (!bindUpdateParams()) // Updates
613 if (!bindCols(*hstmtDefault
)) // Selects
615 if (!bindCols(hstmtInternal
)) // Internal use only
618 * Do NOT bind the hstmtCount cursor!!!
621 // Build an insert statement using parameter markers
622 if (!queryOnly
&& noCols
> 0)
624 bool needComma
= FALSE
;
625 sqlStmt
.sprintf("INSERT INTO %s (", tableName
);
626 for (i
= 0; i
< noCols
; i
++)
628 if (! colDefs
[i
].InsertAllowed
)
632 sqlStmt
+= colDefs
[i
].ColName
;
636 sqlStmt
+= ") VALUES (";
637 for (i
= 0; i
< noCols
; i
++)
639 if (! colDefs
[i
].InsertAllowed
)
648 // pDb->WriteSqlLog(sqlStmt);
650 // Prepare the insert statement for execution
651 if (SQLPrepare(hstmtInsert
, (UCHAR FAR
*) sqlStmt
.GetData(), SQL_NTS
) != SQL_SUCCESS
)
652 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
));
655 // Completed successfully
661 /********** wxTable::Query() **********/
662 bool wxTable::Query(bool forUpdate
, bool distinct
)
665 return(query(DB_SELECT_WHERE
, forUpdate
, distinct
));
667 } // wxTable::Query()
670 /********** wxTable::QueryBySqlStmt() **********/
671 bool wxTable::QueryBySqlStmt(char *pSqlStmt
)
673 pDb
->WriteSqlLog(pSqlStmt
);
675 return(query(DB_SELECT_STATEMENT
, FALSE
, FALSE
, pSqlStmt
));
677 } // wxTable::QueryBySqlStmt()
680 /********** wxTable::QueryMatching() **********/
681 bool wxTable::QueryMatching(bool forUpdate
, bool distinct
)
684 return(query(DB_SELECT_MATCHING
, forUpdate
, distinct
));
686 } // wxTable::QueryMatching()
689 /********** wxTable::QueryOnKeyFields() **********/
690 bool wxTable::QueryOnKeyFields(bool forUpdate
, bool distinct
)
693 return(query(DB_SELECT_KEYFIELDS
, forUpdate
, distinct
));
695 } // wxTable::QueryOnKeyFields()
698 /********** wxTable::GetPrev() **********/
699 bool wxTable::GetPrev(void)
701 if (pDb
->FwdOnlyCursors())
703 wxFAIL_MSG(wxT("GetPrev()::Backward scrolling cursors are not enabled for this instance of wxTable"));
707 return(getRec(SQL_FETCH_PRIOR
));
708 } // wxTable::GetPrev()
711 /********** wxTable::operator-- **********/
712 bool wxTable::operator--(int)
714 if (pDb
->FwdOnlyCursors())
716 wxFAIL_MSG(wxT("operator--:Backward scrolling cursors are not enabled for this instance of wxTable"));
720 return(getRec(SQL_FETCH_PRIOR
));
721 } // wxTable::operator--
724 /********** wxTable::GetFirst() **********/
725 bool wxTable::GetFirst(void)
727 if (pDb
->FwdOnlyCursors())
729 wxFAIL_MSG(wxT("GetFirst():Backward scrolling cursors are not enabled for this instance of wxTable"));
733 return(getRec(SQL_FETCH_FIRST
));
734 } // wxTable::GetFirst()
737 /********** wxTable::GetLast() **********/
738 bool wxTable::GetLast(void)
740 if (pDb
->FwdOnlyCursors())
742 wxFAIL_MSG(wxT("GetLast()::Backward scrolling cursors are not enabled for this instance of wxTable"));
746 return(getRec(SQL_FETCH_LAST
));
747 } // wxTable::GetLast()
750 /********** wxTable::GetSelectStmt() **********/
751 void wxTable::GetSelectStmt(char *pSqlStmt
, int typeOfSelect
, bool distinct
)
753 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
];
757 // Build a select statement to query the database
758 wxStrcpy(pSqlStmt
, "SELECT ");
760 // SELECT DISTINCT values only?
762 wxStrcat(pSqlStmt
, "DISTINCT ");
764 // Was a FROM clause specified to join tables to the base table?
765 // Available for ::Query() only!!!
766 bool appendFromClause
= FALSE
;
767 if (typeOfSelect
== DB_SELECT_WHERE
&& from
&& wxStrlen(from
))
768 appendFromClause
= TRUE
;
770 // Add the column list
772 for (i
= 0; i
< noCols
; i
++)
774 // If joining tables, the base table column names must be qualified to avoid ambiguity
775 if (appendFromClause
)
777 wxStrcat(pSqlStmt
, queryTableName
);
778 wxStrcat(pSqlStmt
, ".");
780 wxStrcat(pSqlStmt
, colDefs
[i
].ColName
);
782 wxStrcat(pSqlStmt
, ",");
785 // If the datasource supports ROWID, get this column as well. Exception: Don't retrieve
786 // the ROWID if querying distinct records. The rowid will always be unique.
787 if (!distinct
&& CanUpdByROWID())
789 // If joining tables, the base table column names must be qualified to avoid ambiguity
790 if (appendFromClause
)
792 wxStrcat(pSqlStmt
, ",");
793 wxStrcat(pSqlStmt
, queryTableName
);
794 wxStrcat(pSqlStmt
, ".ROWID");
797 wxStrcat(pSqlStmt
, ",ROWID");
800 // Append the FROM tablename portion
801 wxStrcat(pSqlStmt
, " FROM ");
802 wxStrcat(pSqlStmt
, queryTableName
);
804 // Sybase uses the HOLDLOCK keyword to lock a record during query.
805 // The HOLDLOCK keyword follows the table name in the from clause.
806 // Each table in the from clause must specify HOLDLOCK or
807 // NOHOLDLOCK (the default). Note: The "FOR UPDATE" clause
808 // is parsed but ignored in SYBASE Transact-SQL.
809 if (selectForUpdate
&& (pDb
->Dbms() == dbmsSYBASE_ASA
|| pDb
->Dbms() == dbmsSYBASE_ASE
))
810 wxStrcat(pSqlStmt
, " HOLDLOCK");
812 if (appendFromClause
)
813 wxStrcat(pSqlStmt
, from
);
815 // Append the WHERE clause. Either append the where clause for the class
816 // or build a where clause. The typeOfSelect determines this.
819 case DB_SELECT_WHERE
:
820 if (where
&& wxStrlen(where
)) // May not want a where clause!!!
822 wxStrcat(pSqlStmt
, " WHERE ");
823 wxStrcat(pSqlStmt
, where
);
826 case DB_SELECT_KEYFIELDS
:
827 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
);
828 if (wxStrlen(whereClause
))
830 wxStrcat(pSqlStmt
, " WHERE ");
831 wxStrcat(pSqlStmt
, whereClause
);
834 case DB_SELECT_MATCHING
:
835 GetWhereClause(whereClause
, DB_WHERE_MATCHING
);
836 if (wxStrlen(whereClause
))
838 wxStrcat(pSqlStmt
, " WHERE ");
839 wxStrcat(pSqlStmt
, whereClause
);
844 // Append the ORDER BY clause
845 if (orderBy
&& wxStrlen(orderBy
))
847 wxStrcat(pSqlStmt
, " ORDER BY ");
848 wxStrcat(pSqlStmt
, orderBy
);
851 // SELECT FOR UPDATE if told to do so and the datasource is capable. Sybase
852 // parses the FOR UPDATE clause but ignores it. See the comment above on the
853 // HOLDLOCK for Sybase.
854 if (selectForUpdate
&& CanSelectForUpdate())
855 wxStrcat(pSqlStmt
, " FOR UPDATE");
857 } // wxTable::GetSelectStmt()
860 /********** wxTable::GetRowNum() **********/
861 UWORD
wxTable::GetRowNum(void)
865 if (SQLGetStmtOption(hstmt
, SQL_ROW_NUMBER
, (UCHAR
*) &rowNum
) != SQL_SUCCESS
)
867 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
871 // Completed successfully
872 return((UWORD
) rowNum
);
874 } // wxTable::GetRowNum()
877 /********** wxTable::CloseCursor() **********/
878 bool wxTable::CloseCursor(HSTMT cursor
)
880 if (SQLFreeStmt(cursor
, SQL_CLOSE
) != SQL_SUCCESS
)
881 return(pDb
->DispAllErrors(henv
, hdbc
, cursor
));
883 // Completed successfully
886 } // wxTable::CloseCursor()
889 /********** wxTable::CreateTable() **********/
890 bool wxTable::CreateTable(bool attemptDrop
)
896 // char sqlStmt[DB_MAX_STATEMENT_LEN];
899 #ifdef DBDEBUG_CONSOLE
900 cout
<< "Creating Table " << tableName
<< "..." << endl
;
904 if (attemptDrop
&& !DropTable())
908 #ifdef DBDEBUG_CONSOLE
909 for (i
= 0; i
< noCols
; i
++)
911 // Exclude derived columns since they are NOT part of the base table
912 if (colDefs
[i
].DerivedCol
)
914 cout
<< i
+ 1 << ": " << colDefs
[i
].ColName
<< "; ";
915 switch(colDefs
[i
].DbDataType
)
917 case DB_DATA_TYPE_VARCHAR
:
918 cout
<< pDb
->typeInfVarchar
.TypeName
<< "(" << colDefs
[i
].SzDataObj
<< ")";
920 case DB_DATA_TYPE_INTEGER
:
921 cout
<< pDb
->typeInfInteger
.TypeName
;
923 case DB_DATA_TYPE_FLOAT
:
924 cout
<< pDb
->typeInfFloat
.TypeName
;
926 case DB_DATA_TYPE_DATE
:
927 cout
<< pDb
->typeInfDate
.TypeName
;
934 // Build a CREATE TABLE string from the colDefs structure.
935 bool needComma
= FALSE
;
936 sqlStmt
.sprintf("CREATE TABLE %s (", tableName
);
938 for (i
= 0; i
< noCols
; i
++)
940 // Exclude derived columns since they are NOT part of the base table
941 if (colDefs
[i
].DerivedCol
)
947 sqlStmt
+= colDefs
[i
].ColName
;
950 switch(colDefs
[i
].DbDataType
)
952 case DB_DATA_TYPE_VARCHAR
:
953 sqlStmt
+= pDb
->typeInfVarchar
.TypeName
; break;
954 case DB_DATA_TYPE_INTEGER
:
955 sqlStmt
+= pDb
->typeInfInteger
.TypeName
; break;
956 case DB_DATA_TYPE_FLOAT
:
957 sqlStmt
+= pDb
->typeInfFloat
.TypeName
; break;
958 case DB_DATA_TYPE_DATE
:
959 sqlStmt
+= pDb
->typeInfDate
.TypeName
; break;
961 // For varchars, append the size of the string
962 if (colDefs
[i
].DbDataType
== DB_DATA_TYPE_VARCHAR
)
965 // wxStrcat(sqlStmt, "(");
966 // wxStrcat(sqlStmt, itoa(colDefs[i].SzDataObj, s, 10));
967 // wxStrcat(sqlStmt, ")");
968 s
.sprintf("(%d)", colDefs
[i
].SzDataObj
);
969 sqlStmt
+= s
.GetData();
972 if (pDb
->Dbms() == dbmsSYBASE_ASE
|| pDb
->Dbms() == dbmsMY_SQL
)
974 if (colDefs
[i
].KeyField
)
976 sqlStmt
+= " NOT NULL";
982 // If there is a primary key defined, include it in the create statement
983 for (i
= j
= 0; i
< noCols
; i
++)
985 if (colDefs
[i
].KeyField
)
991 if (j
&& pDb
->Dbms() != dbmsDBASE
) // Found a keyfield
993 if (pDb
->Dbms() != dbmsMY_SQL
)
995 sqlStmt
+= ",CONSTRAINT ";
996 sqlStmt
+= tableName
;
997 sqlStmt
+= "_PIDX PRIMARY KEY (";
1001 /* MySQL goes out on this one. We also declare the relevant key NON NULL above */
1002 sqlStmt
+= ", PRIMARY KEY (";
1005 // List column name(s) of column(s) comprising the primary key
1006 for (i
= j
= 0; i
< noCols
; i
++)
1008 if (colDefs
[i
].KeyField
)
1010 if (j
++) // Multi part key, comma separate names
1012 sqlStmt
+= colDefs
[i
].ColName
;
1017 // Append the closing parentheses for the create table statement
1020 pDb
->WriteSqlLog(sqlStmt
.GetData());
1022 #ifdef DBDEBUG_CONSOLE
1023 cout
<< endl
<< sqlStmt
.GetData() << endl
;
1026 // Execute the CREATE TABLE statement
1027 RETCODE retcode
= SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
.GetData(), SQL_NTS
);
1028 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1030 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
1031 pDb
->RollbackTrans();
1036 // Commit the transaction and close the cursor
1037 if (! pDb
->CommitTrans())
1039 if (! CloseCursor(hstmt
))
1042 // Database table created successfully
1045 } // wxTable::CreateTable()
1048 /********** wxTable::DropTable() **********/
1049 bool wxTable::DropTable()
1051 // NOTE: This function returns TRUE if the Table does not exist, but
1052 // only for identified databases. Code will need to be added
1053 // below for any other databases when those databases are defined
1054 // to handle this situation consistently
1058 sqlStmt
.sprintf("DROP TABLE %s", tableName
);
1060 pDb
->WriteSqlLog(sqlStmt
.GetData());
1062 #ifdef DBDEBUG_CONSOLE
1063 cout
<< endl
<< sqlStmt
.GetData() << endl
;
1066 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
.GetData(), SQL_NTS
) != SQL_SUCCESS
)
1068 // Check for "Base table not found" error and ignore
1069 pDb
->GetNextError(henv
, hdbc
, hstmt
);
1070 if (wxStrcmp(pDb
->sqlState
,"S0002")) // "Base table not found"
1072 // Check for product specific error codes
1073 if (!((pDb
->Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(pDb
->sqlState
,"42000")) || // 5.x (and lower?)
1074 (pDb
->Dbms() == dbmsMY_SQL
&& !wxStrcmp(pDb
->sqlState
,"S1000")) || // untested
1075 (pDb
->Dbms() == dbmsPOSTGRES
&& !wxStrcmp(pDb
->sqlState
,"08S01")))) // untested
1077 pDb
->DispNextError();
1078 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
1079 pDb
->RollbackTrans();
1086 // Commit the transaction and close the cursor
1087 if (! pDb
->CommitTrans())
1089 if (! CloseCursor(hstmt
))
1093 } // wxTable::DropTable()
1096 /********** wxTable::CreateIndex() **********/
1097 bool wxTable::CreateIndex(const char * idxName
, bool unique
, int noIdxCols
, wxIdxDef
*pIdxDefs
, bool attemptDrop
)
1099 // char sqlStmt[DB_MAX_STATEMENT_LEN];
1102 // Drop the index first
1103 if (attemptDrop
&& !DropIndex(idxName
))
1106 // Build a CREATE INDEX statement
1107 sqlStmt
= "CREATE ";
1109 sqlStmt
+= "UNIQUE ";
1111 sqlStmt
+= "INDEX ";
1114 sqlStmt
+= tableName
;
1117 // Append list of columns making up index
1119 for (i
= 0; i
< noIdxCols
; i
++)
1121 sqlStmt
+= pIdxDefs
[i
].ColName
;
1122 /* Postgres doesn't cope with ASC */
1123 if (pDb
->Dbms() != dbmsPOSTGRES
)
1125 if (pIdxDefs
[i
].Ascending
)
1131 if ((i
+ 1) < noIdxCols
)
1135 // Append closing parentheses
1138 pDb
->WriteSqlLog(sqlStmt
.GetData());
1140 #ifdef DBDEBUG_CONSOLE
1141 cout
<< endl
<< sqlStmt
.GetData() << endl
<< endl
;
1144 // Execute the CREATE INDEX statement
1145 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
.GetData(), SQL_NTS
) != SQL_SUCCESS
)
1147 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
1148 pDb
->RollbackTrans();
1153 // Commit the transaction and close the cursor
1154 if (! pDb
->CommitTrans())
1156 if (! CloseCursor(hstmt
))
1159 // Index Created Successfully
1162 } // wxTable::CreateIndex()
1165 /********** wxTable::DropIndex() **********/
1166 bool wxTable::DropIndex(const char * idxName
)
1168 // NOTE: This function returns TRUE if the Index does not exist, but
1169 // only for identified databases. Code will need to be added
1170 // below for any other databases when those databases are defined
1171 // to handle this situation consistently
1175 if (pDb
->Dbms() == dbmsACCESS
)
1176 sqlStmt
.sprintf("DROP INDEX %s ON %s",idxName
,tableName
);
1177 else if (pDb
->Dbms() == dbmsSYBASE_ASE
)
1178 sqlStmt
.sprintf("DROP INDEX %s.%s",tableName
,idxName
);
1180 sqlStmt
.sprintf("DROP INDEX %s",idxName
);
1182 pDb
->WriteSqlLog(sqlStmt
.GetData());
1184 #ifdef DBDEBUG_CONSOLE
1185 cout
<< endl
<< sqlStmt
.GetData() << endl
;
1188 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
.GetData(), SQL_NTS
) != SQL_SUCCESS
)
1190 // Check for "Index not found" error and ignore
1191 pDb
->GetNextError(henv
, hdbc
, hstmt
);
1192 if (wxStrcmp(pDb
->sqlState
,"S0012")) // "Index not found"
1194 // Check for product specific error codes
1195 if (!((pDb
->Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(pDb
->sqlState
,"42000")) || // v5.x (and lower?)
1196 (pDb
->Dbms() == dbmsSYBASE_ASE
&& !wxStrcmp(pDb
->sqlState
,"S0002")) || // Base table not found
1197 (pDb
->Dbms() == dbmsMY_SQL
&& !wxStrcmp(pDb
->sqlState
,"42S02")) // untested
1200 pDb
->DispNextError();
1201 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
1202 pDb
->RollbackTrans();
1209 // Commit the transaction and close the cursor
1210 if (! pDb
->CommitTrans())
1212 if (! CloseCursor(hstmt
))
1216 } // wxTable::DropIndex()
1219 /********** wxTable::Insert() **********/
1220 int wxTable::Insert(void)
1228 // Insert the record by executing the already prepared insert statement
1230 retcode
=SQLExecute(hstmtInsert
);
1231 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1233 // Check to see if integrity constraint was violated
1234 pDb
->GetNextError(henv
, hdbc
, hstmtInsert
);
1235 if (! wxStrcmp(pDb
->sqlState
, "23000")) // Integrity constraint violated
1236 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1239 pDb
->DispNextError();
1240 pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
);
1245 // Record inserted into the datasource successfully
1248 } // wxTable::Insert()
1251 /********** wxTable::Update() **********/
1252 bool wxTable::Update(void)
1258 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1260 // Build the SQL UPDATE statement
1261 GetUpdateStmt(sqlStmt
, DB_UPD_KEYFIELDS
);
1263 pDb
->WriteSqlLog(sqlStmt
);
1265 #ifdef DBDEBUG_CONSOLE
1266 cout
<< endl
<< sqlStmt
<< endl
<< endl
;
1269 // Execute the SQL UPDATE statement
1270 return(execUpdate(sqlStmt
));
1272 } // wxTable::Update()
1275 /********** wxTable::Update(pSqlStmt) **********/
1276 bool wxTable::Update(const char *pSqlStmt
)
1282 pDb
->WriteSqlLog(pSqlStmt
);
1284 return(execUpdate(pSqlStmt
));
1286 } // wxTable::Update(pSqlStmt)
1289 /********** wxTable::UpdateWhere() **********/
1290 bool wxTable::UpdateWhere(const char *pWhereClause
)
1296 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1298 // Build the SQL UPDATE statement
1299 GetUpdateStmt(sqlStmt
, DB_UPD_WHERE
, pWhereClause
);
1301 pDb
->WriteSqlLog(sqlStmt
);
1303 #ifdef DBDEBUG_CONSOLE
1304 cout
<< endl
<< sqlStmt
<< endl
<< endl
;
1307 // Execute the SQL UPDATE statement
1308 return(execUpdate(sqlStmt
));
1310 } // wxTable::UpdateWhere()
1313 /********** wxTable::Delete() **********/
1314 bool wxTable::Delete(void)
1320 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1322 // Build the SQL DELETE statement
1323 GetDeleteStmt(sqlStmt
, DB_DEL_KEYFIELDS
);
1325 pDb
->WriteSqlLog(sqlStmt
);
1327 // Execute the SQL DELETE statement
1328 return(execDelete(sqlStmt
));
1330 } // wxTable::Delete()
1333 /********** wxTable::DeleteWhere() **********/
1334 bool wxTable::DeleteWhere(const char *pWhereClause
)
1340 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1342 // Build the SQL DELETE statement
1343 GetDeleteStmt(sqlStmt
, DB_DEL_WHERE
, pWhereClause
);
1345 pDb
->WriteSqlLog(sqlStmt
);
1347 // Execute the SQL DELETE statement
1348 return(execDelete(sqlStmt
));
1350 } // wxTable::DeleteWhere()
1353 /********** wxTable::DeleteMatching() **********/
1354 bool wxTable::DeleteMatching(void)
1360 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1362 // Build the SQL DELETE statement
1363 GetDeleteStmt(sqlStmt
, DB_DEL_MATCHING
);
1365 pDb
->WriteSqlLog(sqlStmt
);
1367 // Execute the SQL DELETE statement
1368 return(execDelete(sqlStmt
));
1370 } // wxTable::DeleteMatching()
1373 /********** wxTable::GetUpdateStmt() **********/
1374 void wxTable::GetUpdateStmt(char *pSqlStmt
, int typeOfUpd
, const char *pWhereClause
)
1380 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
];
1381 bool firstColumn
= TRUE
;
1384 sprintf(pSqlStmt
, "UPDATE %s SET ", tableName
);
1386 // Append a list of columns to be updated
1388 for (i
= 0; i
< noCols
; i
++)
1390 // Only append Updateable columns
1391 if (colDefs
[i
].Updateable
)
1394 wxStrcat(pSqlStmt
, ",");
1396 firstColumn
= FALSE
;
1397 wxStrcat(pSqlStmt
, colDefs
[i
].ColName
);
1398 wxStrcat(pSqlStmt
, " = ?");
1402 // Append the WHERE clause to the SQL UPDATE statement
1403 wxStrcat(pSqlStmt
, " WHERE ");
1406 case DB_UPD_KEYFIELDS
:
1407 // If the datasource supports the ROWID column, build
1408 // the where on ROWID for efficiency purposes.
1409 // e.g. UPDATE PARTS SET Col1 = ?, Col2 = ? WHERE ROWID = '111.222.333'
1410 if (CanUpdByROWID())
1413 char rowid
[ROWID_LEN
];
1415 // Get the ROWID value. If not successful retreiving the ROWID,
1416 // simply fall down through the code and build the WHERE clause
1417 // based on the key fields.
1418 if (SQLGetData(hstmt
, noCols
+1, SQL_C_CHAR
, (UCHAR
*) rowid
, ROWID_LEN
, &cb
) == SQL_SUCCESS
)
1420 wxStrcat(pSqlStmt
, "ROWID = '");
1421 wxStrcat(pSqlStmt
, rowid
);
1422 wxStrcat(pSqlStmt
, "'");
1426 // Unable to delete by ROWID, so build a WHERE
1427 // clause based on the keyfields.
1428 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
);
1429 wxStrcat(pSqlStmt
, whereClause
);
1432 wxStrcat(pSqlStmt
, pWhereClause
);
1435 } // GetUpdateStmt()
1438 /********** wxTable::GetDeleteStmt() **********/
1439 void wxTable::GetDeleteStmt(char *pSqlStmt
, int typeOfDel
, const char *pWhereClause
)
1445 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
];
1449 // Handle the case of DeleteWhere() and the where clause is blank. It should
1450 // delete all records from the database in this case.
1451 if (typeOfDel
== DB_DEL_WHERE
&& (pWhereClause
== 0 || wxStrlen(pWhereClause
) == 0))
1453 sprintf(pSqlStmt
, "DELETE FROM %s", tableName
);
1457 sprintf(pSqlStmt
, "DELETE FROM %s WHERE ", tableName
);
1459 // Append the WHERE clause to the SQL DELETE statement
1462 case DB_DEL_KEYFIELDS
:
1463 // If the datasource supports the ROWID column, build
1464 // the where on ROWID for efficiency purposes.
1465 // e.g. DELETE FROM PARTS WHERE ROWID = '111.222.333'
1466 if (CanUpdByROWID())
1469 char rowid
[ROWID_LEN
];
1471 // Get the ROWID value. If not successful retreiving the ROWID,
1472 // simply fall down through the code and build the WHERE clause
1473 // based on the key fields.
1474 if (SQLGetData(hstmt
, noCols
+1, SQL_C_CHAR
, (UCHAR
*) rowid
, ROWID_LEN
, &cb
) == SQL_SUCCESS
)
1476 wxStrcat(pSqlStmt
, "ROWID = '");
1477 wxStrcat(pSqlStmt
, rowid
);
1478 wxStrcat(pSqlStmt
, "'");
1482 // Unable to delete by ROWID, so build a WHERE
1483 // clause based on the keyfields.
1484 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
);
1485 wxStrcat(pSqlStmt
, whereClause
);
1488 wxStrcat(pSqlStmt
, pWhereClause
);
1490 case DB_DEL_MATCHING
:
1491 GetWhereClause(whereClause
, DB_WHERE_MATCHING
);
1492 wxStrcat(pSqlStmt
, whereClause
);
1496 } // GetDeleteStmt()
1499 /********** wxTable::GetWhereClause() **********/
1500 void wxTable::GetWhereClause(char *pWhereClause
, int typeOfWhere
,
1501 const char *qualTableName
, bool useLikeComparison
)
1503 * Note: GetWhereClause() currently ignores timestamp columns.
1504 * They are not included as part of the where clause.
1507 bool moreThanOneColumn
= FALSE
;
1510 // Loop through the columns building a where clause as you go
1512 for (i
= 0; i
< noCols
; i
++)
1514 // Determine if this column should be included in the WHERE clause
1515 if ((typeOfWhere
== DB_WHERE_KEYFIELDS
&& colDefs
[i
].KeyField
) ||
1516 (typeOfWhere
== DB_WHERE_MATCHING
&& (! IsColNull(i
))))
1518 // Skip over timestamp columns
1519 if (colDefs
[i
].SqlCtype
== SQL_C_TIMESTAMP
)
1521 // If there is more than 1 column, join them with the keyword "AND"
1522 if (moreThanOneColumn
)
1523 wxStrcat(pWhereClause
, " AND ");
1525 moreThanOneColumn
= TRUE
;
1526 // Concatenate where phrase for the column
1527 if (qualTableName
&& wxStrlen(qualTableName
))
1529 wxStrcat(pWhereClause
, qualTableName
);
1530 wxStrcat(pWhereClause
, ".");
1532 wxStrcat(pWhereClause
, colDefs
[i
].ColName
);
1533 if (useLikeComparison
&& (colDefs
[i
].SqlCtype
== SQL_C_CHAR
))
1534 wxStrcat(pWhereClause
, " LIKE ");
1536 wxStrcat(pWhereClause
, " = ");
1537 switch(colDefs
[i
].SqlCtype
)
1540 sprintf(colValue
, "'%s'", (UCHAR FAR
*) colDefs
[i
].PtrDataObj
);
1543 sprintf(colValue
, "%hi", *((SWORD
*) colDefs
[i
].PtrDataObj
));
1546 sprintf(colValue
, "%hu", *((UWORD
*) colDefs
[i
].PtrDataObj
));
1549 sprintf(colValue
, "%li", *((SDWORD
*) colDefs
[i
].PtrDataObj
));
1552 sprintf(colValue
, "%lu", *((UDWORD
*) colDefs
[i
].PtrDataObj
));
1555 sprintf(colValue
, "%.6f", *((SFLOAT
*) colDefs
[i
].PtrDataObj
));
1558 sprintf(colValue
, "%.6f", *((SDOUBLE
*) colDefs
[i
].PtrDataObj
));
1561 wxStrcat(pWhereClause
, colValue
);
1564 } // wxTable::GetWhereClause()
1567 /********** wxTable::IsColNull() **********/
1568 bool wxTable::IsColNull(int colNo
)
1570 switch(colDefs
[colNo
].SqlCtype
)
1573 return(((UCHAR FAR
*) colDefs
[colNo
].PtrDataObj
)[0] == 0);
1575 return(( *((SWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1577 return(( *((UWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1579 return(( *((SDWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1581 return(( *((UDWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1583 return(( *((SFLOAT
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1585 return((*((SDOUBLE
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1586 case SQL_C_TIMESTAMP
:
1587 TIMESTAMP_STRUCT
*pDt
;
1588 pDt
= (TIMESTAMP_STRUCT
*) colDefs
[colNo
].PtrDataObj
;
1589 if (pDt
->year
== 0 && pDt
->month
== 0 && pDt
->day
== 0)
1596 } // wxTable::IsColNull()
1599 /********** wxTable::CanSelectForUpdate() **********/
1600 bool wxTable::CanSelectForUpdate(void)
1602 if (pDb
->Dbms() == dbmsMY_SQL
)
1605 if (pDb
->dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1610 } // wxTable::CanSelectForUpdate()
1613 /********** wxTable::CanUpdByROWID() **********/
1614 bool wxTable::CanUpdByROWID(void)
1617 * NOTE: Returning FALSE for now until this can be debugged,
1618 * as the ROWID is not getting updated correctly
1622 if (pDb
->Dbms() == dbmsORACLE
)
1627 } // wxTable::CanUpdByROWID()
1630 /********** wxTable::IsCursorClosedOnCommit() **********/
1631 bool wxTable::IsCursorClosedOnCommit(void)
1633 if (pDb
->dbInf
.cursorCommitBehavior
== SQL_CB_PRESERVE
)
1638 } // wxTable::IsCursorClosedOnCommit()
1641 /********** wxTable::ClearMemberVars() **********/
1642 void wxTable::ClearMemberVars(void)
1644 // Loop through the columns setting each member variable to zero
1646 for (i
= 0; i
< noCols
; i
++)
1648 switch(colDefs
[i
].SqlCtype
)
1651 ((UCHAR FAR
*) colDefs
[i
].PtrDataObj
)[0] = 0;
1654 *((SWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1657 *((UWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1660 *((SDWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1663 *((UDWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1666 *((SFLOAT
*) colDefs
[i
].PtrDataObj
) = 0.0f
;
1669 *((SDOUBLE
*) colDefs
[i
].PtrDataObj
) = 0.0f
;
1671 case SQL_C_TIMESTAMP
:
1672 TIMESTAMP_STRUCT
*pDt
;
1673 pDt
= (TIMESTAMP_STRUCT
*) colDefs
[i
].PtrDataObj
;
1686 } // wxTable::ClearMemberVars()
1689 /********** wxTable::SetQueryTimeout() **********/
1690 bool wxTable::SetQueryTimeout(UDWORD nSeconds
)
1692 if (SQLSetStmtOption(hstmtInsert
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1693 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
));
1694 if (SQLSetStmtOption(hstmtUpdate
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1695 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
));
1696 if (SQLSetStmtOption(hstmtDelete
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1697 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
));
1698 if (SQLSetStmtOption(hstmtInternal
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1699 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
));
1701 // Completed Successfully
1704 } // wxTable::SetQueryTimeout()
1707 /********** wxTable::SetColDefs() **********/
1708 void wxTable::SetColDefs (int index
, const char *fieldName
, int dataType
, void *pData
,
1709 int cType
, int size
, bool keyField
, bool upd
,
1710 bool insAllow
, bool derivedCol
)
1712 if (!colDefs
) // May happen if the database connection fails
1715 if (wxStrlen(fieldName
) > (unsigned int) DB_MAX_COLUMN_NAME_LEN
)
1717 wxStrncpy (colDefs
[index
].ColName
, fieldName
, DB_MAX_COLUMN_NAME_LEN
);
1718 colDefs
[index
].ColName
[DB_MAX_COLUMN_NAME_LEN
] = 0;
1721 wxStrcpy(colDefs
[index
].ColName
, fieldName
);
1723 colDefs
[index
].DbDataType
= dataType
;
1724 colDefs
[index
].PtrDataObj
= pData
;
1725 colDefs
[index
].SqlCtype
= cType
;
1726 colDefs
[index
].SzDataObj
= size
;
1727 colDefs
[index
].KeyField
= keyField
;
1728 colDefs
[index
].DerivedCol
= derivedCol
;
1729 // Derived columns by definition would NOT be "Insertable" or "Updateable"
1732 colDefs
[index
].Updateable
= FALSE
;
1733 colDefs
[index
].InsertAllowed
= FALSE
;
1737 colDefs
[index
].Updateable
= upd
;
1738 colDefs
[index
].InsertAllowed
= insAllow
;
1741 colDefs
[index
].Null
= FALSE
;
1743 } // wxTable::SetColDefs()
1746 /********** wxTable::SetColDef() **********/
1747 wxColDataPtr
* wxTable::SetColDefs (wxColInf
*pColInfs
, ULONG numCols
)
1750 wxColDataPtr
*pColDataPtrs
= NULL
;
1757 pColDataPtrs
= new wxColDataPtr
[numCols
+1];
1759 for (index
= 0; index
< numCols
; index
++)
1761 // Process the fields
1762 switch (pColInfs
[index
].dbDataType
)
1764 case DB_DATA_TYPE_VARCHAR
:
1766 pColDataPtrs
[index
].PtrDataObj
= new char[pColInfs
[index
].bufferLength
+1];
1767 pColDataPtrs
[index
].SzDataObj
= pColInfs
[index
].columnSize
;
1768 pColDataPtrs
[index
].SqlCtype
= SQL_C_CHAR
;
1771 case DB_DATA_TYPE_INTEGER
:
1773 // Can be long or short
1774 if (pColInfs
[index
].bufferLength
== sizeof(long))
1776 pColDataPtrs
[index
].PtrDataObj
= new long;
1777 pColDataPtrs
[index
].SzDataObj
= sizeof(long);
1778 pColDataPtrs
[index
].SqlCtype
= SQL_C_SLONG
;
1782 pColDataPtrs
[index
].PtrDataObj
= new short;
1783 pColDataPtrs
[index
].SzDataObj
= sizeof(short);
1784 pColDataPtrs
[index
].SqlCtype
= SQL_C_SSHORT
;
1788 case DB_DATA_TYPE_FLOAT
:
1790 // Can be float or double
1791 if (pColInfs
[index
].bufferLength
== sizeof(float))
1793 pColDataPtrs
[index
].PtrDataObj
= new float;
1794 pColDataPtrs
[index
].SzDataObj
= sizeof(float);
1795 pColDataPtrs
[index
].SqlCtype
= SQL_C_FLOAT
;
1799 pColDataPtrs
[index
].PtrDataObj
= new double;
1800 pColDataPtrs
[index
].SzDataObj
= sizeof(double);
1801 pColDataPtrs
[index
].SqlCtype
= SQL_C_DOUBLE
;
1805 case DB_DATA_TYPE_DATE
:
1807 pColDataPtrs
[index
].PtrDataObj
= new TIMESTAMP_STRUCT
;
1808 pColDataPtrs
[index
].SzDataObj
= sizeof(TIMESTAMP_STRUCT
);
1809 pColDataPtrs
[index
].SqlCtype
= SQL_C_TIMESTAMP
;
1814 SetColDefs (index
,pColInfs
[index
].colName
,pColInfs
[index
].dbDataType
, pColDataPtrs
[index
].PtrDataObj
, pColDataPtrs
[index
].SqlCtype
, pColDataPtrs
[index
].SzDataObj
);
1817 return (pColDataPtrs
);
1818 } // wxTable::SetColDef()
1821 /********** wxTable::SetCursor() **********/
1822 void wxTable::SetCursor(HSTMT
*hstmtActivate
)
1824 if (hstmtActivate
== DEFAULT_CURSOR
)
1825 hstmt
= *hstmtDefault
;
1827 hstmt
= *hstmtActivate
;
1829 } // wxTable::SetCursor()
1832 /********** wxTable::Count(const char *) **********/
1833 ULONG
wxTable::Count(const char *args
)
1839 // Build a "SELECT COUNT(*) FROM queryTableName [WHERE whereClause]" SQL Statement
1840 sqlStmt
= "SELECT COUNT(";
1842 sqlStmt
+= ") FROM ";
1843 sqlStmt
+= queryTableName
;
1845 if (from
&& wxStrlen(from
))
1848 // Add the where clause if one is provided
1849 if (where
&& wxStrlen(where
))
1851 sqlStmt
+= " WHERE ";
1855 pDb
->WriteSqlLog(sqlStmt
.GetData());
1857 // Initialize the Count cursor if it's not already initialized
1860 hstmtCount
= NewCursor(FALSE
,FALSE
);
1866 // Execute the SQL statement
1867 if (SQLExecDirect(*hstmtCount
, (UCHAR FAR
*) sqlStmt
.GetData(), SQL_NTS
) != SQL_SUCCESS
)
1869 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1874 if (SQLFetch(*hstmtCount
) != SQL_SUCCESS
)
1876 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1880 // Obtain the result
1881 if (SQLGetData(*hstmtCount
, 1, SQL_C_ULONG
, &l
, sizeof(l
), &cb
) != SQL_SUCCESS
)
1883 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1888 if (SQLFreeStmt(*hstmtCount
, SQL_CLOSE
) != SQL_SUCCESS
)
1889 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1891 // Return the record count
1894 } // wxTable::Count()
1897 /********** wxTable::Refresh() **********/
1898 bool wxTable::Refresh(void)
1902 // Switch to the internal cursor so any active cursors are not corrupted
1903 HSTMT currCursor
= GetCursor();
1904 hstmt
= hstmtInternal
;
1906 // Save the where and order by clauses
1907 char *saveWhere
= where
;
1908 char *saveOrderBy
= orderBy
;
1910 // Build a where clause to refetch the record with. Try and use the
1911 // ROWID if it's available, ow use the key fields.
1912 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
+1];
1913 wxStrcpy(whereClause
, "");
1914 if (CanUpdByROWID())
1917 char rowid
[ROWID_LEN
+1];
1919 // Get the ROWID value. If not successful retreiving the ROWID,
1920 // simply fall down through the code and build the WHERE clause
1921 // based on the key fields.
1922 if (SQLGetData(hstmt
, noCols
+1, SQL_C_CHAR
, (UCHAR
*) rowid
, ROWID_LEN
, &cb
) == SQL_SUCCESS
)
1924 wxStrcat(whereClause
, queryTableName
);
1925 wxStrcat(whereClause
, ".ROWID = '");
1926 wxStrcat(whereClause
, rowid
);
1927 wxStrcat(whereClause
, "'");
1931 // If unable to use the ROWID, build a where clause from the keyfields
1932 if (wxStrlen(whereClause
) == 0)
1933 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
, queryTableName
);
1935 // Requery the record
1936 where
= whereClause
;
1941 if (result
&& !GetNext())
1944 // Switch back to original cursor
1945 SetCursor(&currCursor
);
1947 // Free the internal cursor
1948 if (SQLFreeStmt(hstmtInternal
, SQL_CLOSE
) != SQL_SUCCESS
)
1949 pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
);
1951 // Restore the original where and order by clauses
1953 orderBy
= saveOrderBy
;
1957 } // wxTable::Refresh()
1960 /********** wxTable::SetNull(int colNo) **********/
1961 bool wxTable::SetNull(int colNo
)
1964 return(colDefs
[colNo
].Null
= TRUE
);
1968 } // wxTable::SetNull(int colNo)
1971 /********** wxTable::SetNull(char *colName) **********/
1972 bool wxTable::SetNull(const char *colName
)
1975 for (i
= 0; i
< noCols
; i
++)
1977 if (!wxStricmp(colName
, colDefs
[i
].ColName
))
1982 return(colDefs
[i
].Null
= TRUE
);
1986 } // wxTable::SetNull(char *colName)
1989 /********** wxTable::NewCursor() **********/
1990 HSTMT
*wxTable::NewCursor(bool setCursor
, bool bindColumns
)
1992 HSTMT
*newHSTMT
= new HSTMT
;
1997 if (SQLAllocStmt(hdbc
, newHSTMT
) != SQL_SUCCESS
)
1999 pDb
->DispAllErrors(henv
, hdbc
);
2004 if (SQLSetStmtOption(*newHSTMT
, SQL_CURSOR_TYPE
, cursorType
) != SQL_SUCCESS
)
2006 pDb
->DispAllErrors(henv
, hdbc
, *newHSTMT
);
2013 if(!bindCols(*newHSTMT
))
2021 SetCursor(newHSTMT
);
2025 } // wxTable::NewCursor()
2028 /********** wxTable::DeleteCursor() **********/
2029 bool wxTable::DeleteCursor(HSTMT
*hstmtDel
)
2033 if (!hstmtDel
) // Cursor already deleted
2036 if (SQLFreeStmt(*hstmtDel
, SQL_DROP
) != SQL_SUCCESS
)
2038 pDb
->DispAllErrors(henv
, hdbc
);
2046 } // wxTable::DeleteCursor()
2048 #endif // wxUSE_ODBC