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
43 # include <iostream.h>
50 #if wxMAJOR_VERSION == 2
56 #if wxMAJOR_VERSION == 1
57 # if defined(wx_msw) || defined(wx_x)
73 #if wxMAJOR_VERSION == 1
75 #elif wxMAJOR_VERSION == 2
76 #include "wx/dbtable.h"
80 // The HPUX preprocessor lines below were commented out on 8/20/97
81 // because macros.h currently redefines DEBUG and is unneeded.
83 // # include <macros.h>
86 # include <sys/minmax.h>
90 ULONG lastTableID
= 0;
98 /********** wxTable::wxTable() **********/
99 wxTable::wxTable(wxDB
*pwxDB
, const char *tblName
, const int nCols
,
100 const char *qryTblName
, bool qryOnly
, char *tblPath
)
102 pDb
= pwxDB
; // Pointer to the wxDB object
106 hstmtDefault
= 0; // Initialized below
107 hstmtCount
= 0; // Initialized first time it is needed
114 noCols
= nCols
; // No. of cols in the table
115 where
= 0; // Where clause
116 orderBy
= 0; // Order By clause
117 from
= 0; // From clause
118 selectForUpdate
= FALSE
; // SELECT ... FOR UPDATE; Indicates whether to include the FOR UPDATE phrase
123 strcpy(tableName
, tblName
); // Table Name
125 strcpy(tablePath
, tblPath
); // Table Path - used for dBase files
127 if (qryTblName
) // Name of the table/view to query
128 strcpy(queryTableName
, qryTblName
);
130 strcpy(queryTableName
, tblName
);
132 // assert(pDb); // Assert is placed after table name is assigned for error reporting reasons
139 tableID
= ++lastTableID
;
140 sprintf(s
, "wxTable constructor (%-20s) tableID:[%6lu] pDb:[%lu]", tblName
,tableID
,pDb
);
143 CstructTablesInUse
*tableInUse
;
144 tableInUse
= new CstructTablesInUse();
145 tableInUse
->tableName
= tblName
;
146 tableInUse
->tableID
= tableID
;
147 tableInUse
->pDb
= pDb
;
148 TablesInUse
.Append(tableInUse
);
153 // Grab the HENV and HDBC from the wxDB object
157 // Allocate space for column definitions
159 colDefs
= new CcolDef
[noCols
]; // Points to the first column defintion
161 // Allocate statement handles for the table
164 // Allocate a separate statement handle for performing inserts
165 if (SQLAllocStmt(hdbc
, &hstmtInsert
) != SQL_SUCCESS
)
166 pDb
->DispAllErrors(henv
, hdbc
);
167 // Allocate a separate statement handle for performing deletes
168 if (SQLAllocStmt(hdbc
, &hstmtDelete
) != SQL_SUCCESS
)
169 pDb
->DispAllErrors(henv
, hdbc
);
170 // Allocate a separate statement handle for performing updates
171 if (SQLAllocStmt(hdbc
, &hstmtUpdate
) != SQL_SUCCESS
)
172 pDb
->DispAllErrors(henv
, hdbc
);
174 // Allocate a separate statement handle for internal use
175 if (SQLAllocStmt(hdbc
, &hstmtInternal
) != SQL_SUCCESS
)
176 pDb
->DispAllErrors(henv
, hdbc
);
178 // Set the cursor type for the statement handles
179 cursorType
= SQL_CURSOR_STATIC
;
180 if (SQLSetStmtOption(hstmtInternal
, SQL_CURSOR_TYPE
, cursorType
) != SQL_SUCCESS
)
182 // Check to see if cursor type is supported
183 pDb
->GetNextError(henv
, hdbc
, hstmtInternal
);
184 if (! strcmp(pDb
->sqlState
, "01S02")) // Option Value Changed
186 // Datasource does not support static cursors. Driver
187 // will substitute a cursor type. Call SQLGetStmtOption()
188 // to determine which cursor type was selected.
189 if (SQLGetStmtOption(hstmtInternal
, SQL_CURSOR_TYPE
, &cursorType
) != SQL_SUCCESS
)
190 pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
);
191 #ifdef DBDEBUG_CONSOLE
192 cout
<< "Static cursor changed to: ";
195 case SQL_CURSOR_FORWARD_ONLY
:
196 cout
<< "Forward Only"; break;
197 case SQL_CURSOR_STATIC
:
198 cout
<< "Static"; break;
199 case SQL_CURSOR_KEYSET_DRIVEN
:
200 cout
<< "Keyset Driven"; break;
201 case SQL_CURSOR_DYNAMIC
:
202 cout
<< "Dynamic"; break;
204 cout
<< endl
<< endl
;
209 pDb
->DispNextError();
210 pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
);
213 #ifdef DBDEBUG_CONSOLE
215 cout
<< "Cursor Type set to STATIC" << endl
<< endl
;
220 // Set the cursor type for the INSERT statement handle
221 if (SQLSetStmtOption(hstmtInsert
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
)
222 pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
);
223 // Set the cursor type for the DELETE statement handle
224 if (SQLSetStmtOption(hstmtDelete
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
)
225 pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
);
226 // Set the cursor type for the UPDATE statement handle
227 if (SQLSetStmtOption(hstmtUpdate
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
)
228 pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
);
231 // Make the default cursor the active cursor
232 hstmtDefault
= NewCursor(FALSE
,FALSE
);
233 assert(hstmtDefault
);
234 hstmt
= *hstmtDefault
;
236 } // wxTable::wxTable()
238 /********** wxTable::~wxTable() **********/
244 sprintf(s
, "wxTable destructor (%-20s) tableID:[%6lu] pDb:[%lu]", tableName
,tableID
,pDb
);
248 #ifndef PROGRAM_FP4UPG
254 pNode
= TablesInUse
.First();
255 while (pNode
&& !found
)
257 if (((CstructTablesInUse
*)pNode
->Data())->tableID
== tableID
)
260 if (!TablesInUse
.DeleteNode(pNode
))
261 wxMessageBox (s
,"Unable to delete node!");
264 pNode
= pNode
->Next();
269 sprintf(msg
,"Unable to find the tableID in the linked\nlist of tables in use.\n\n%s",s
);
270 wxMessageBox (msg
,"NOTICE...");
275 // Decrement the wxDB table count
279 // Delete memory allocated for column definitions
283 // Free statement handles
287 if (SQLFreeStmt(hstmtInsert
, SQL_DROP
) != SQL_SUCCESS
)
288 pDb
->DispAllErrors(henv
, hdbc
);
290 if (SQLFreeStmt(hstmtDelete
, SQL_DROP
) != SQL_SUCCESS
)
291 pDb
->DispAllErrors(henv
, hdbc
);
293 if (SQLFreeStmt(hstmtUpdate
, SQL_DROP
) != SQL_SUCCESS
)
294 pDb
->DispAllErrors(henv
, hdbc
);
297 if (SQLFreeStmt(hstmtInternal
, SQL_DROP
) != SQL_SUCCESS
)
298 pDb
->DispAllErrors(henv
, hdbc
);
300 // Delete dynamically allocated cursors
302 DeleteCursor(hstmtDefault
);
304 DeleteCursor(hstmtCount
);
306 } // wxTable::~wxTable()
308 /********** wxTable::Open() **********/
309 bool wxTable::Open(void)
315 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
317 // Verify that the table exists in the database
318 if (!pDb
->TableExists(tableName
,NULL
,tablePath
))
321 sprintf(s
, "Error opening '%s', table/view does not exist in the database.", tableName
);
326 // Bind the member variables for field exchange between
327 // the wxTable object and the ODBC record.
330 if (!bindInsertParams()) // Inserts
332 if (!bindUpdateParams()) // Updates
335 if (!bindCols(*hstmtDefault
)) // Selects
337 if (!bindCols(hstmtInternal
)) // Internal use only
340 * Do NOT bind the hstmtCount cursor!!!
343 // Build an insert statement using parameter markers
344 if (!queryOnly
&& noCols
> 0)
346 bool needComma
= FALSE
;
347 sprintf(sqlStmt
, "INSERT INTO %s (", tableName
);
348 for (i
= 0; i
< noCols
; i
++)
350 if (! colDefs
[i
].InsertAllowed
)
353 strcat(sqlStmt
, ",");
354 strcat(sqlStmt
, colDefs
[i
].ColName
);
358 strcat(sqlStmt
, ") VALUES (");
359 for (i
= 0; i
< noCols
; i
++)
361 if (! colDefs
[i
].InsertAllowed
)
364 strcat(sqlStmt
, ",");
365 strcat(sqlStmt
, "?");
368 strcat(sqlStmt
, ")");
370 // pDb->WriteSqlLog(sqlStmt);
372 // Prepare the insert statement for execution
373 if (SQLPrepare(hstmtInsert
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
374 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
));
377 // Completed successfully
382 /********** wxTable::Query() **********/
383 bool wxTable::Query(bool forUpdate
, bool distinct
)
386 return(query(DB_SELECT_WHERE
, forUpdate
, distinct
));
388 } // wxTable::Query()
390 /********** wxTable::QueryBySqlStmt() **********/
391 bool wxTable::QueryBySqlStmt(char *pSqlStmt
)
393 pDb
->WriteSqlLog(pSqlStmt
);
395 return(query(DB_SELECT_STATEMENT
, FALSE
, FALSE
, pSqlStmt
));
397 } // wxTable::QueryBySqlStmt()
399 /********** wxTable::QueryMatching() **********/
400 bool wxTable::QueryMatching(bool forUpdate
, bool distinct
)
403 return(query(DB_SELECT_MATCHING
, forUpdate
, distinct
));
405 } // wxTable::QueryMatching()
407 /********** wxTable::QueryOnKeyFields() **********/
408 bool wxTable::QueryOnKeyFields(bool forUpdate
, bool distinct
)
411 return(query(DB_SELECT_KEYFIELDS
, forUpdate
, distinct
));
413 } // wxTable::QueryOnKeyFields()
415 /********** wxTable::query() **********/
416 bool wxTable::query(int queryType
, bool forUpdate
, bool distinct
, char *pSqlStmt
)
418 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
420 // Set the selectForUpdate member variable
422 // The user may wish to select for update, but the DBMS may not be capable
423 selectForUpdate
= CanSelectForUpdate();
425 selectForUpdate
= FALSE
;
427 // Set the SQL SELECT string
428 if (queryType
!= DB_SELECT_STATEMENT
) // A select statement was not passed in,
429 { // so generate a select statement.
430 GetSelectStmt(sqlStmt
, queryType
, distinct
);
431 pDb
->WriteSqlLog(sqlStmt
);
434 // Make sure the cursor is closed first
435 if (! CloseCursor(hstmt
))
438 // Execute the SQL SELECT statement
441 retcode
= SQLExecDirect(hstmt
, (UCHAR FAR
*) (queryType
== DB_SELECT_STATEMENT
? pSqlStmt
: sqlStmt
), SQL_NTS
);
442 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
443 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
));
445 // Completed successfully
448 } // wxTable::query()
450 /********** wxTable::GetSelectStmt() **********/
451 void wxTable::GetSelectStmt(char *pSqlStmt
, int typeOfSelect
, bool distinct
)
453 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
];
457 // Build a select statement to query the database
458 strcpy(pSqlStmt
, "SELECT ");
460 // SELECT DISTINCT values only?
462 strcat(pSqlStmt
, "DISTINCT ");
464 // Was a FROM clause specified to join tables to the base table?
465 // Available for ::Query() only!!!
466 bool appendFromClause
= FALSE
;
467 if (typeOfSelect
== DB_SELECT_WHERE
&& from
&& strlen(from
))
468 appendFromClause
= TRUE
;
470 // Add the column list
472 for (i
= 0; i
< noCols
; i
++)
474 // If joining tables, the base table column names must be qualified to avoid ambiguity
475 if (appendFromClause
)
477 strcat(pSqlStmt
, queryTableName
);
478 strcat(pSqlStmt
, ".");
480 strcat(pSqlStmt
, colDefs
[i
].ColName
);
482 strcat(pSqlStmt
, ",");
485 // If the datasource supports ROWID, get this column as well. Exception: Don't retrieve
486 // the ROWID if querying distinct records. The rowid will always be unique.
487 if (!distinct
&& CanUpdByROWID())
489 // If joining tables, the base table column names must be qualified to avoid ambiguity
490 if (appendFromClause
)
492 strcat(pSqlStmt
, ",");
493 strcat(pSqlStmt
, queryTableName
);
494 strcat(pSqlStmt
, ".ROWID");
497 strcat(pSqlStmt
, ",ROWID");
500 // Append the FROM tablename portion
501 strcat(pSqlStmt
, " FROM ");
502 strcat(pSqlStmt
, queryTableName
);
504 // Sybase uses the HOLDLOCK keyword to lock a record during query.
505 // The HOLDLOCK keyword follows the table name in the from clause.
506 // Each table in the from clause must specify HOLDLOCK or
507 // NOHOLDLOCK (the default). Note: The "FOR UPDATE" clause
508 // is parsed but ignored in SYBASE Transact-SQL.
509 if (selectForUpdate
&& (pDb
->Dbms() == dbmsSYBASE_ASA
|| pDb
->Dbms() == dbmsSYBASE_ASE
))
510 strcat(pSqlStmt
, " HOLDLOCK");
512 if (appendFromClause
)
513 strcat(pSqlStmt
, from
);
515 // Append the WHERE clause. Either append the where clause for the class
516 // or build a where clause. The typeOfSelect determines this.
519 case DB_SELECT_WHERE
:
520 if (where
&& strlen(where
)) // May not want a where clause!!!
522 strcat(pSqlStmt
, " WHERE ");
523 strcat(pSqlStmt
, where
);
526 case DB_SELECT_KEYFIELDS
:
527 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
);
528 if (strlen(whereClause
))
530 strcat(pSqlStmt
, " WHERE ");
531 strcat(pSqlStmt
, whereClause
);
534 case DB_SELECT_MATCHING
:
535 GetWhereClause(whereClause
, DB_WHERE_MATCHING
);
536 if (strlen(whereClause
))
538 strcat(pSqlStmt
, " WHERE ");
539 strcat(pSqlStmt
, whereClause
);
544 // Append the ORDER BY clause
545 if (orderBy
&& strlen(orderBy
))
547 strcat(pSqlStmt
, " ORDER BY ");
548 strcat(pSqlStmt
, orderBy
);
551 // SELECT FOR UPDATE if told to do so and the datasource is capable. Sybase
552 // parses the FOR UPDATE clause but ignores it. See the comment above on the
553 // HOLDLOCK for Sybase.
554 if (selectForUpdate
&& CanSelectForUpdate())
555 strcat(pSqlStmt
, " FOR UPDATE");
557 } // wxTable::GetSelectStmt()
559 /********** wxTable::getRec() **********/
560 bool wxTable::getRec(UWORD fetchType
)
564 #ifndef FWD_ONLY_CURSORS
565 // Fetch the NEXT, PREV, FIRST or LAST record, depending on fetchType
568 if ((retcode
= SQLExtendedFetch(hstmt
, fetchType
, 0, &cRowsFetched
, &rowStatus
)) != SQL_SUCCESS
)
569 if (retcode
== SQL_NO_DATA_FOUND
)
572 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
));
574 // Fetch the next record from the record set
576 retcode
= SQLFetch(hstmt
);
577 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
579 if (retcode
== SQL_NO_DATA_FOUND
)
582 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
));
586 // Completed successfully
589 } // wxTable::getRec()
591 /********** wxTable::GetRowNum() **********/
592 UWORD
wxTable::GetRowNum(void)
596 if (SQLGetStmtOption(hstmt
, SQL_ROW_NUMBER
, (UCHAR
*) &rowNum
) != SQL_SUCCESS
)
598 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
602 // Completed successfully
603 return((UWORD
) rowNum
);
605 } // wxTable::GetRowNum()
607 /********** wxTable::bindInsertParams() **********/
608 bool wxTable::bindInsertParams(void)
615 UDWORD precision
= 0;
618 // Bind each column (that can be inserted) of the table to a parameter marker
620 for (i
= 0; i
< noCols
; i
++)
622 if (! colDefs
[i
].InsertAllowed
)
624 switch(colDefs
[i
].DbDataType
)
626 case DB_DATA_TYPE_VARCHAR
:
627 fSqlType
= pDb
->typeInfVarchar
.FsqlType
;
628 precision
= colDefs
[i
].SzDataObj
;
630 colDefs
[i
].CbValue
= SQL_NTS
;
632 case DB_DATA_TYPE_INTEGER
:
633 fSqlType
= pDb
->typeInfInteger
.FsqlType
;
634 precision
= pDb
->typeInfInteger
.Precision
;
636 colDefs
[i
].CbValue
= 0;
638 case DB_DATA_TYPE_FLOAT
:
639 fSqlType
= pDb
->typeInfFloat
.FsqlType
;
640 precision
= pDb
->typeInfFloat
.Precision
;
641 scale
= pDb
->typeInfFloat
.MaximumScale
;
642 // SQL Sybase Anywhere v5.5 returned a negative number for the
643 // MaxScale. This caused ODBC to kick out an error on ibscale.
644 // I check for this here and set the scale = precision.
646 // scale = (short) precision;
647 colDefs
[i
].CbValue
= 0;
649 case DB_DATA_TYPE_DATE
:
650 fSqlType
= pDb
->typeInfDate
.FsqlType
;
651 precision
= pDb
->typeInfDate
.Precision
;
653 colDefs
[i
].CbValue
= 0;
659 colDefs
[i
].CbValue
= SQL_NULL_DATA
;
660 colDefs
[i
].Null
= FALSE
;
662 if (SQLBindParameter(hstmtInsert
, i
+1, SQL_PARAM_INPUT
, colDefs
[i
].SqlCtype
,
663 fSqlType
, precision
, scale
, (UCHAR
*) colDefs
[i
].PtrDataObj
,
664 precision
+1,&colDefs
[i
].CbValue
) != SQL_SUCCESS
)
665 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
));
668 // Completed successfully
671 } // wxTable::bindInsertParams()
673 /********** wxTable::bindUpdateParams() **********/
674 bool wxTable::bindUpdateParams(void)
681 UDWORD precision
= 0;
684 // Bind each UPDATEABLE column of the table to a parameter marker
686 for (i
= 0, colNo
= 1; i
< noCols
; i
++)
688 if (! colDefs
[i
].Updateable
)
690 switch(colDefs
[i
].DbDataType
)
692 case DB_DATA_TYPE_VARCHAR
:
693 fSqlType
= pDb
->typeInfVarchar
.FsqlType
;
694 precision
= colDefs
[i
].SzDataObj
;
696 colDefs
[i
].CbValue
= SQL_NTS
;
698 case DB_DATA_TYPE_INTEGER
:
699 fSqlType
= pDb
->typeInfInteger
.FsqlType
;
700 precision
= pDb
->typeInfInteger
.Precision
;
702 colDefs
[i
].CbValue
= 0;
704 case DB_DATA_TYPE_FLOAT
:
705 fSqlType
= pDb
->typeInfFloat
.FsqlType
;
706 precision
= pDb
->typeInfFloat
.Precision
;
707 scale
= pDb
->typeInfFloat
.MaximumScale
;
708 // SQL Sybase Anywhere v5.5 returned a negative number for the
709 // MaxScale. This caused ODBC to kick out an error on ibscale.
710 // I check for this here and set the scale = precision.
712 // scale = (short) precision;
713 colDefs
[i
].CbValue
= 0;
715 case DB_DATA_TYPE_DATE
:
716 fSqlType
= pDb
->typeInfDate
.FsqlType
;
717 precision
= pDb
->typeInfDate
.Precision
;
719 colDefs
[i
].CbValue
= 0;
722 if (SQLBindParameter(hstmtUpdate
, colNo
++, SQL_PARAM_INPUT
, colDefs
[i
].SqlCtype
,
723 fSqlType
, precision
, scale
, (UCHAR
*) colDefs
[i
].PtrDataObj
,
724 precision
+1, &colDefs
[i
].CbValue
) != SQL_SUCCESS
)
725 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
));
728 // Completed successfully
731 } // wxTable::bindUpdateParams()
733 /********** wxTable::bindCols() **********/
734 bool wxTable::bindCols(HSTMT cursor
)
738 // Bind each column of the table to a memory address for fetching data
740 for (i
= 0; i
< noCols
; i
++)
742 if (SQLBindCol(cursor
, i
+1, colDefs
[i
].SqlCtype
, (UCHAR
*) colDefs
[i
].PtrDataObj
,
743 colDefs
[i
].SzDataObj
, &cb
) != SQL_SUCCESS
)
744 return(pDb
->DispAllErrors(henv
, hdbc
, cursor
));
747 // Completed successfully
750 } // wxTable::bindCols()
752 /********** wxTable::CloseCursor() **********/
753 bool wxTable::CloseCursor(HSTMT cursor
)
755 if (SQLFreeStmt(cursor
, SQL_CLOSE
) != SQL_SUCCESS
)
756 return(pDb
->DispAllErrors(henv
, hdbc
, cursor
));
758 // Completed successfully
761 } // wxTable::CloseCursor()
763 /********** wxTable::CreateTable() **********/
764 bool wxTable::CreateTable(bool attemptDrop
)
770 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
772 #ifdef DBDEBUG_CONSOLE
773 cout
<< "Creating Table " << tableName
<< "..." << endl
;
777 if (attemptDrop
&& !DropTable())
781 #ifdef DBDEBUG_CONSOLE
782 for (i
= 0; i
< noCols
; i
++)
784 // Exclude derived columns since they are NOT part of the base table
785 if (colDefs
[i
].DerivedCol
)
787 cout
<< i
+ 1 << ": " << colDefs
[i
].ColName
<< "; ";
788 switch(colDefs
[i
].DbDataType
)
790 case DB_DATA_TYPE_VARCHAR
:
791 cout
<< pDb
->typeInfVarchar
.TypeName
<< "(" << colDefs
[i
].SzDataObj
<< ")";
793 case DB_DATA_TYPE_INTEGER
:
794 cout
<< pDb
->typeInfInteger
.TypeName
;
796 case DB_DATA_TYPE_FLOAT
:
797 cout
<< pDb
->typeInfFloat
.TypeName
;
799 case DB_DATA_TYPE_DATE
:
800 cout
<< pDb
->typeInfDate
.TypeName
;
807 // Build a CREATE TABLE string from the colDefs structure.
808 bool needComma
= FALSE
;
809 sprintf(sqlStmt
, "CREATE TABLE %s (", tableName
);
810 for (i
= 0; i
< noCols
; i
++)
812 // Exclude derived columns since they are NOT part of the base table
813 if (colDefs
[i
].DerivedCol
)
817 strcat(sqlStmt
, ",");
819 strcat(sqlStmt
, colDefs
[i
].ColName
);
820 strcat(sqlStmt
, " ");
822 switch(colDefs
[i
].DbDataType
)
824 case DB_DATA_TYPE_VARCHAR
:
825 strcat(sqlStmt
, pDb
->typeInfVarchar
.TypeName
); break;
826 case DB_DATA_TYPE_INTEGER
:
827 strcat(sqlStmt
, pDb
->typeInfInteger
.TypeName
); break;
828 case DB_DATA_TYPE_FLOAT
:
829 strcat(sqlStmt
, pDb
->typeInfFloat
.TypeName
); break;
830 case DB_DATA_TYPE_DATE
:
831 strcat(sqlStmt
, pDb
->typeInfDate
.TypeName
); break;
833 // For varchars, append the size of the string
834 if (colDefs
[i
].DbDataType
== DB_DATA_TYPE_VARCHAR
)
837 // strcat(sqlStmt, "(");
838 // strcat(sqlStmt, itoa(colDefs[i].SzDataObj, s, 10));
839 // strcat(sqlStmt, ")");
840 sprintf(s
, "(%d)", colDefs
[i
].SzDataObj
);
844 if (pDb
->Dbms() == dbmsSYBASE_ASE
|| pDb
->Dbms() == dbmsMY_SQL
)
846 if (colDefs
[i
].KeyField
)
848 strcat(sqlStmt
, " NOT NULL");
854 // If there is a primary key defined, include it in the create statement
855 for (i
= j
= 0; i
< noCols
; i
++)
857 if (colDefs
[i
].KeyField
)
863 if (j
&& pDb
->Dbms() != dbmsDBASE
) // Found a keyfield
865 if (pDb
->Dbms() != dbmsMY_SQL
)
867 strcat(sqlStmt
, ",CONSTRAINT ");
868 strcat(sqlStmt
, tableName
);
869 strcat(sqlStmt
, "_PIDX PRIMARY KEY (");
873 /* MySQL goes out on this one. We also declare the relevant key NON NULL above */
874 strcat(sqlStmt
, ", PRIMARY KEY (");
877 // List column name(s) of column(s) comprising the primary key
878 for (i
= j
= 0; i
< noCols
; i
++)
880 if (colDefs
[i
].KeyField
)
882 if (j
++) // Multi part key, comma separate names
883 strcat(sqlStmt
, ",");
884 strcat(sqlStmt
, colDefs
[i
].ColName
);
887 strcat(sqlStmt
, ")");
889 // Append the closing parentheses for the create table statement
890 strcat(sqlStmt
, ")");
892 pDb
->WriteSqlLog(sqlStmt
);
894 #ifdef DBDEBUG_CONSOLE
895 cout
<< endl
<< sqlStmt
<< endl
;
898 // Execute the CREATE TABLE statement
899 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
901 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
902 pDb
->RollbackTrans();
907 // Commit the transaction and close the cursor
908 if (! pDb
->CommitTrans())
910 if (! CloseCursor(hstmt
))
913 // Database table created successfully
916 } // wxTable::CreateTable()
918 /********** wxTable::DropTable() **********/
919 bool wxTable::DropTable()
921 // NOTE: This function returns TRUE if the Table does not exist, but
922 // only for identified databases. Code will need to be added
923 // below for any other databases when those databases are defined
924 // to handle this situation consistently
926 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
928 sprintf(sqlStmt
, "DROP TABLE %s", tableName
);
930 pDb
->WriteSqlLog(sqlStmt
);
932 #ifdef DBDEBUG_CONSOLE
933 cout
<< endl
<< sqlStmt
<< endl
;
936 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
938 // Check for "Base table not found" error and ignore
939 pDb
->GetNextError(henv
, hdbc
, hstmt
);
940 if (strcmp(pDb
->sqlState
,"S0002")) // "Base table not found"
942 // Check for product specific error codes
943 if (!((pDb
->Dbms() == dbmsSYBASE_ASA
&& !strcmp(pDb
->sqlState
,"42000")) || // 5.x (and lower?)
944 (pDb
->Dbms() == dbmsMY_SQL
&& !strcmp(pDb
->sqlState
,"S1000")) || // untested
945 (pDb
->Dbms() == dbmsPOSTGRES
&& !strcmp(pDb
->sqlState
,"08S01")))) // untested
947 pDb
->DispNextError();
948 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
949 pDb
->RollbackTrans();
956 // Commit the transaction and close the cursor
957 if (! pDb
->CommitTrans())
959 if (! CloseCursor(hstmt
))
963 } // wxTable::DropTable()
965 /********** wxTable::CreateIndex() **********/
966 bool wxTable::CreateIndex(char * idxName
, bool unique
, int noIdxCols
, CidxDef
*pIdxDefs
, bool attemptDrop
)
968 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
970 // Drop the index first
971 if (attemptDrop
&& !DropIndex(idxName
))
974 // Build a CREATE INDEX statement
975 strcpy(sqlStmt
, "CREATE ");
977 strcat(sqlStmt
, "UNIQUE ");
979 strcat(sqlStmt
, "INDEX ");
980 strcat(sqlStmt
, idxName
);
981 strcat(sqlStmt
, " ON ");
982 strcat(sqlStmt
, tableName
);
983 strcat(sqlStmt
, " (");
985 // Append list of columns making up index
987 for (i
= 0; i
< noIdxCols
; i
++)
989 strcat(sqlStmt
, pIdxDefs
[i
].ColName
);
990 /* Postgres doesn't cope with ASC */
991 if (pDb
->Dbms() != dbmsPOSTGRES
)
993 if (pIdxDefs
[i
].Ascending
)
994 strcat(sqlStmt
, " ASC");
996 strcat(sqlStmt
, " DESC");
999 if ((i
+ 1) < noIdxCols
)
1000 strcat(sqlStmt
, ",");
1003 // Append closing parentheses
1004 strcat(sqlStmt
, ")");
1006 pDb
->WriteSqlLog(sqlStmt
);
1008 #ifdef DBDEBUG_CONSOLE
1009 cout
<< endl
<< sqlStmt
<< endl
<< endl
;
1012 // Execute the CREATE INDEX statement
1013 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
1015 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
1016 pDb
->RollbackTrans();
1021 // Commit the transaction and close the cursor
1022 if (! pDb
->CommitTrans())
1024 if (! CloseCursor(hstmt
))
1027 // Index Created Successfully
1030 } // wxTable::CreateIndex()
1032 /********** wxTable::DropIndex() **********/
1033 bool wxTable::DropIndex(char * idxName
)
1035 // NOTE: This function returns TRUE if the Index does not exist, but
1036 // only for identified databases. Code will need to be added
1037 // below for any other databases when those databases are defined
1038 // to handle this situation consistently
1040 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1042 if (pDb
->Dbms() == dbmsACCESS
)
1043 sprintf(sqlStmt
, "DROP INDEX %s ON %s",idxName
,tableName
);
1044 else if (pDb
->Dbms() == dbmsSYBASE_ASE
)
1045 sprintf(sqlStmt
, "DROP INDEX %s.%s",tableName
,idxName
);
1047 sprintf(sqlStmt
, "DROP INDEX %s",idxName
);
1049 pDb
->WriteSqlLog(sqlStmt
);
1051 #ifdef DBDEBUG_CONSOLE
1052 cout
<< endl
<< sqlStmt
<< endl
;
1055 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
1057 // Check for "Index not found" error and ignore
1058 pDb
->GetNextError(henv
, hdbc
, hstmt
);
1059 if (strcmp(pDb
->sqlState
,"S0012")) // "Index not found"
1061 // Check for product specific error codes
1062 if (!((pDb
->Dbms() == dbmsSYBASE_ASA
&& !strcmp(pDb
->sqlState
,"42000")) || // v5.x (and lower?)
1063 (pDb
->Dbms() == dbmsSYBASE_ASE
&& !strcmp(pDb
->sqlState
,"S0002")) || // Base table not found
1064 (pDb
->Dbms() == dbmsMY_SQL
&& !strcmp(pDb
->sqlState
,"42S02")) // untested
1067 pDb
->DispNextError();
1068 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
1069 pDb
->RollbackTrans();
1076 // Commit the transaction and close the cursor
1077 if (! pDb
->CommitTrans())
1079 if (! CloseCursor(hstmt
))
1083 } // wxTable::DropIndex()
1085 /********** wxTable::Insert() **********/
1086 int wxTable::Insert(void)
1094 // Insert the record by executing the already prepared insert statement
1096 retcode
=SQLExecute(hstmtInsert
);
1097 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1099 // Check to see if integrity constraint was violated
1100 pDb
->GetNextError(henv
, hdbc
, hstmtInsert
);
1101 if (! strcmp(pDb
->sqlState
, "23000")) // Integrity constraint violated
1102 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1105 pDb
->DispNextError();
1106 pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
);
1111 // Record inserted into the datasource successfully
1114 } // wxTable::Insert()
1116 /********** wxTable::Update(pSqlStmt) **********/
1117 bool wxTable::Update(char *pSqlStmt
)
1123 pDb
->WriteSqlLog(pSqlStmt
);
1125 return(execUpdate(pSqlStmt
));
1127 } // wxTable::Update(pSqlStmt)
1129 /********** wxTable::Update() **********/
1130 bool wxTable::Update(void)
1136 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1138 // Build the SQL UPDATE statement
1139 GetUpdateStmt(sqlStmt
, DB_UPD_KEYFIELDS
);
1141 pDb
->WriteSqlLog(sqlStmt
);
1143 #ifdef DBDEBUG_CONSOLE
1144 cout
<< endl
<< sqlStmt
<< endl
<< endl
;
1147 // Execute the SQL UPDATE statement
1148 return(execUpdate(sqlStmt
));
1150 } // wxTable::Update()
1152 /********** wxTable::UpdateWhere() **********/
1153 bool wxTable::UpdateWhere(char *pWhereClause
)
1159 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1161 // Build the SQL UPDATE statement
1162 GetUpdateStmt(sqlStmt
, DB_UPD_WHERE
, pWhereClause
);
1164 pDb
->WriteSqlLog(sqlStmt
);
1166 #ifdef DBDEBUG_CONSOLE
1167 cout
<< endl
<< sqlStmt
<< endl
<< endl
;
1170 // Execute the SQL UPDATE statement
1171 return(execUpdate(sqlStmt
));
1173 } // wxTable::UpdateWhere()
1175 /********** wxTable::Delete() **********/
1176 bool wxTable::Delete(void)
1182 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1184 // Build the SQL DELETE statement
1185 GetDeleteStmt(sqlStmt
, DB_DEL_KEYFIELDS
);
1187 pDb
->WriteSqlLog(sqlStmt
);
1189 // Execute the SQL DELETE statement
1190 return(execDelete(sqlStmt
));
1192 } // wxTable::Delete()
1194 /********** wxTable::DeleteWhere() **********/
1195 bool wxTable::DeleteWhere(char *pWhereClause
)
1201 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1203 // Build the SQL DELETE statement
1204 GetDeleteStmt(sqlStmt
, DB_DEL_WHERE
, pWhereClause
);
1206 pDb
->WriteSqlLog(sqlStmt
);
1208 // Execute the SQL DELETE statement
1209 return(execDelete(sqlStmt
));
1211 } // wxTable::DeleteWhere()
1213 /********** wxTable::DeleteMatching() **********/
1214 bool wxTable::DeleteMatching(void)
1220 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1222 // Build the SQL DELETE statement
1223 GetDeleteStmt(sqlStmt
, DB_DEL_MATCHING
);
1225 pDb
->WriteSqlLog(sqlStmt
);
1227 // Execute the SQL DELETE statement
1228 return(execDelete(sqlStmt
));
1230 } // wxTable::DeleteMatching()
1232 /********** wxTable::execDelete() **********/
1233 bool wxTable::execDelete(char *pSqlStmt
)
1235 // Execute the DELETE statement
1236 if (SQLExecDirect(hstmtDelete
, (UCHAR FAR
*) pSqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
1237 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
));
1239 // Record deleted successfully
1242 } // wxTable::execDelete()
1244 /********** wxTable::execUpdate() **********/
1245 bool wxTable::execUpdate(char *pSqlStmt
)
1247 // Execute the UPDATE statement
1248 if (SQLExecDirect(hstmtUpdate
, (UCHAR FAR
*) pSqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
1249 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
));
1251 // Record deleted successfully
1254 } // wxTable::execUpdate()
1256 /********** wxTable::GetUpdateStmt() **********/
1257 void wxTable::GetUpdateStmt(char *pSqlStmt
, int typeOfUpd
, char *pWhereClause
)
1263 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
];
1264 bool firstColumn
= TRUE
;
1267 sprintf(pSqlStmt
, "UPDATE %s SET ", tableName
);
1269 // Append a list of columns to be updated
1271 for (i
= 0; i
< noCols
; i
++)
1273 // Only append Updateable columns
1274 if (colDefs
[i
].Updateable
)
1277 strcat(pSqlStmt
, ",");
1279 firstColumn
= FALSE
;
1280 strcat(pSqlStmt
, colDefs
[i
].ColName
);
1281 strcat(pSqlStmt
, " = ?");
1285 // Append the WHERE clause to the SQL UPDATE statement
1286 strcat(pSqlStmt
, " WHERE ");
1289 case DB_UPD_KEYFIELDS
:
1290 // If the datasource supports the ROWID column, build
1291 // the where on ROWID for efficiency purposes.
1292 // e.g. UPDATE PARTS SET Col1 = ?, Col2 = ? WHERE ROWID = '111.222.333'
1293 if (CanUpdByROWID())
1296 char rowid
[ROWID_LEN
];
1298 // Get the ROWID value. If not successful retreiving the ROWID,
1299 // simply fall down through the code and build the WHERE clause
1300 // based on the key fields.
1301 if (SQLGetData(hstmt
, noCols
+1, SQL_C_CHAR
, (UCHAR
*) rowid
, ROWID_LEN
, &cb
) == SQL_SUCCESS
)
1303 strcat(pSqlStmt
, "ROWID = '");
1304 strcat(pSqlStmt
, rowid
);
1305 strcat(pSqlStmt
, "'");
1309 // Unable to delete by ROWID, so build a WHERE
1310 // clause based on the keyfields.
1311 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
);
1312 strcat(pSqlStmt
, whereClause
);
1315 strcat(pSqlStmt
, pWhereClause
);
1319 } // GetUpdateStmt()
1321 /********** wxTable::GetDeleteStmt() **********/
1322 void wxTable::GetDeleteStmt(char *pSqlStmt
, int typeOfDel
, char *pWhereClause
)
1328 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
];
1332 // Handle the case of DeleteWhere() and the where clause is blank. It should
1333 // delete all records from the database in this case.
1334 if (typeOfDel
== DB_DEL_WHERE
&& (pWhereClause
== 0 || strlen(pWhereClause
) == 0))
1336 sprintf(pSqlStmt
, "DELETE FROM %s", tableName
);
1340 sprintf(pSqlStmt
, "DELETE FROM %s WHERE ", tableName
);
1342 // Append the WHERE clause to the SQL DELETE statement
1345 case DB_DEL_KEYFIELDS
:
1346 // If the datasource supports the ROWID column, build
1347 // the where on ROWID for efficiency purposes.
1348 // e.g. DELETE FROM PARTS WHERE ROWID = '111.222.333'
1349 if (CanUpdByROWID())
1352 char rowid
[ROWID_LEN
];
1354 // Get the ROWID value. If not successful retreiving the ROWID,
1355 // simply fall down through the code and build the WHERE clause
1356 // based on the key fields.
1357 if (SQLGetData(hstmt
, noCols
+1, SQL_C_CHAR
, (UCHAR
*) rowid
, ROWID_LEN
, &cb
) == SQL_SUCCESS
)
1359 strcat(pSqlStmt
, "ROWID = '");
1360 strcat(pSqlStmt
, rowid
);
1361 strcat(pSqlStmt
, "'");
1365 // Unable to delete by ROWID, so build a WHERE
1366 // clause based on the keyfields.
1367 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
);
1368 strcat(pSqlStmt
, whereClause
);
1371 strcat(pSqlStmt
, pWhereClause
);
1373 case DB_DEL_MATCHING
:
1374 GetWhereClause(whereClause
, DB_WHERE_MATCHING
);
1375 strcat(pSqlStmt
, whereClause
);
1379 } // GetDeleteStmt()
1381 /********** wxTable::GetWhereClause() **********/
1383 * Note: GetWhereClause() currently ignores timestamp columns.
1384 * They are not included as part of the where clause.
1387 void wxTable::GetWhereClause(char *pWhereClause
, int typeOfWhere
, char *qualTableName
)
1389 bool moreThanOneColumn
= FALSE
;
1392 // Loop through the columns building a where clause as you go
1394 for (i
= 0; i
< noCols
; i
++)
1396 // Determine if this column should be included in the WHERE clause
1397 if ((typeOfWhere
== DB_WHERE_KEYFIELDS
&& colDefs
[i
].KeyField
) ||
1398 (typeOfWhere
== DB_WHERE_MATCHING
&& (! IsColNull(i
))))
1400 // Skip over timestamp columns
1401 if (colDefs
[i
].SqlCtype
== SQL_C_TIMESTAMP
)
1403 // If there is more than 1 column, join them with the keyword "AND"
1404 if (moreThanOneColumn
)
1405 strcat(pWhereClause
, " AND ");
1407 moreThanOneColumn
= TRUE
;
1408 // Concatenate where phrase for the column
1409 if (qualTableName
&& strlen(qualTableName
))
1411 strcat(pWhereClause
, qualTableName
);
1412 strcat(pWhereClause
, ".");
1414 strcat(pWhereClause
, colDefs
[i
].ColName
);
1415 strcat(pWhereClause
, " = ");
1416 switch(colDefs
[i
].SqlCtype
)
1419 sprintf(colValue
, "'%s'", (UCHAR FAR
*) colDefs
[i
].PtrDataObj
);
1422 sprintf(colValue
, "%hi", *((SWORD
*) colDefs
[i
].PtrDataObj
));
1425 sprintf(colValue
, "%hu", *((UWORD
*) colDefs
[i
].PtrDataObj
));
1428 sprintf(colValue
, "%li", *((SDWORD
*) colDefs
[i
].PtrDataObj
));
1431 sprintf(colValue
, "%lu", *((UDWORD
*) colDefs
[i
].PtrDataObj
));
1434 sprintf(colValue
, "%.6f", *((SFLOAT
*) colDefs
[i
].PtrDataObj
));
1437 sprintf(colValue
, "%.6f", *((SDOUBLE
*) colDefs
[i
].PtrDataObj
));
1440 strcat(pWhereClause
, colValue
);
1444 } // wxTable::GetWhereClause()
1446 /********** wxTable::IsColNull() **********/
1447 bool wxTable::IsColNull(int colNo
)
1449 switch(colDefs
[colNo
].SqlCtype
)
1452 return(((UCHAR FAR
*) colDefs
[colNo
].PtrDataObj
)[0] == 0);
1454 return(( *((SWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1456 return(( *((UWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1458 return(( *((SDWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1460 return(( *((UDWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1462 return(( *((SFLOAT
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1464 return((*((SDOUBLE
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1465 case SQL_C_TIMESTAMP
:
1466 TIMESTAMP_STRUCT
*pDt
;
1467 pDt
= (TIMESTAMP_STRUCT
*) colDefs
[colNo
].PtrDataObj
;
1468 if (pDt
->year
== 0 && pDt
->month
== 0 && pDt
->day
== 0)
1476 } // wxTable::IsColNull()
1478 /********** wxTable::CanSelectForUpdate() **********/
1479 bool wxTable::CanSelectForUpdate(void)
1481 if (pDb
->Dbms() == dbmsMY_SQL
)
1484 if (pDb
->dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1489 } // wxTable::CanSelectForUpdate()
1491 /********** wxTable::CanUpdByROWID() **********/
1492 bool wxTable::CanUpdByROWID(void)
1495 //NOTE: Returning FALSE for now until this can be debugged,
1496 // as the ROWID is not getting updated correctly
1499 if (pDb
->Dbms() == dbmsORACLE
)
1504 } // wxTable::CanUpdByROWID()
1506 /********** wxTable::IsCursorClosedOnCommit() **********/
1507 bool wxTable::IsCursorClosedOnCommit(void)
1509 if (pDb
->dbInf
.cursorCommitBehavior
== SQL_CB_PRESERVE
)
1514 } // wxTable::IsCursorClosedOnCommit()
1516 /********** wxTable::ClearMemberVars() **********/
1517 void wxTable::ClearMemberVars(void)
1519 // Loop through the columns setting each member variable to zero
1521 for (i
= 0; i
< noCols
; i
++)
1523 switch(colDefs
[i
].SqlCtype
)
1526 ((UCHAR FAR
*) colDefs
[i
].PtrDataObj
)[0] = 0;
1529 *((SWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1532 *((UWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1535 *((SDWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1538 *((UDWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1541 *((SFLOAT
*) colDefs
[i
].PtrDataObj
) = 0.0f
;
1544 *((SDOUBLE
*) colDefs
[i
].PtrDataObj
) = 0.0f
;
1546 case SQL_C_TIMESTAMP
:
1547 TIMESTAMP_STRUCT
*pDt
;
1548 pDt
= (TIMESTAMP_STRUCT
*) colDefs
[i
].PtrDataObj
;
1560 } // wxTable::ClearMemberVars()
1562 /********** wxTable::SetQueryTimeout() **********/
1563 bool wxTable::SetQueryTimeout(UDWORD nSeconds
)
1565 if (SQLSetStmtOption(hstmtInsert
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1566 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
));
1567 if (SQLSetStmtOption(hstmtUpdate
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1568 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
));
1569 if (SQLSetStmtOption(hstmtDelete
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1570 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
));
1571 if (SQLSetStmtOption(hstmtInternal
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1572 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
));
1574 // Completed Successfully
1577 } // wxTable::SetQueryTimeout()
1579 /********** wxTable::SetColDefs() **********/
1580 void wxTable::SetColDefs (int index
, char *fieldName
, int dataType
, void *pData
,
1581 int cType
, int size
, bool keyField
, bool upd
,
1582 bool insAllow
, bool derivedCol
)
1584 if (!colDefs
) // May happen if the database connection fails
1587 if (strlen(fieldName
) > (unsigned int) DB_MAX_COLUMN_NAME_LEN
)
1589 strncpy (colDefs
[index
].ColName
, fieldName
, DB_MAX_COLUMN_NAME_LEN
);
1590 colDefs
[index
].ColName
[DB_MAX_COLUMN_NAME_LEN
] = 0;
1593 strcpy(colDefs
[index
].ColName
, fieldName
);
1595 colDefs
[index
].DbDataType
= dataType
;
1596 colDefs
[index
].PtrDataObj
= pData
;
1597 colDefs
[index
].SqlCtype
= cType
;
1598 colDefs
[index
].SzDataObj
= size
;
1599 colDefs
[index
].KeyField
= keyField
;
1600 colDefs
[index
].DerivedCol
= derivedCol
;
1601 // Derived columns by definition would NOT be "Insertable" or "Updateable"
1604 colDefs
[index
].Updateable
= FALSE
;
1605 colDefs
[index
].InsertAllowed
= FALSE
;
1609 colDefs
[index
].Updateable
= upd
;
1610 colDefs
[index
].InsertAllowed
= insAllow
;
1613 colDefs
[index
].Null
= FALSE
;
1615 } // wxTable::SetColDefs()
1617 /********** wxTable::SetCursor() **********/
1618 void wxTable::SetCursor(HSTMT
*hstmtActivate
)
1620 if (hstmtActivate
== DEFAULT_CURSOR
)
1621 hstmt
= *hstmtDefault
;
1623 hstmt
= *hstmtActivate
;
1625 } // wxTable::SetCursor()
1627 /********** wxTable::Count() **********/
1628 ULONG
wxTable::Count(void)
1631 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1634 // Build a "SELECT COUNT(*) FROM queryTableName [WHERE whereClause]" SQL Statement
1635 strcpy(sqlStmt
, "SELECT COUNT(*) FROM ");
1636 strcat(sqlStmt
, queryTableName
);
1638 if (from
&& strlen(from
))
1639 strcat(sqlStmt
, from
);
1641 // Add the where clause if one is provided
1642 if (where
&& strlen(where
))
1644 strcat(sqlStmt
, " WHERE ");
1645 strcat(sqlStmt
, where
);
1648 pDb
->WriteSqlLog(sqlStmt
);
1650 // Initialize the Count cursor if it's not already initialized
1653 hstmtCount
= NewCursor(FALSE
,FALSE
);
1659 // Execute the SQL statement
1660 if (SQLExecDirect(*hstmtCount
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
1662 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1667 if (SQLFetch(*hstmtCount
) != SQL_SUCCESS
)
1669 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1673 // Obtain the result
1674 if (SQLGetData(*hstmtCount
, 1, SQL_C_ULONG
, &l
, sizeof(l
), &cb
) != SQL_SUCCESS
)
1676 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1681 if (SQLFreeStmt(*hstmtCount
, SQL_CLOSE
) != SQL_SUCCESS
)
1682 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1684 // Return the record count
1687 } // wxTable::Count()
1689 /********** wxTable::Refresh() **********/
1690 bool wxTable::Refresh(void)
1694 // Switch to the internal cursor so any active cursors are not corrupted
1695 HSTMT currCursor
= GetCursor();
1696 hstmt
= hstmtInternal
;
1698 // Save the where and order by clauses
1699 char *saveWhere
= where
;
1700 char *saveOrderBy
= orderBy
;
1702 // Build a where clause to refetch the record with. Try and use the
1703 // ROWID if it's available, ow use the key fields.
1704 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
+1];
1705 strcpy(whereClause
, "");
1706 if (CanUpdByROWID())
1709 char rowid
[ROWID_LEN
+1];
1711 // Get the ROWID value. If not successful retreiving the ROWID,
1712 // simply fall down through the code and build the WHERE clause
1713 // based on the key fields.
1714 if (SQLGetData(hstmt
, noCols
+1, SQL_C_CHAR
, (UCHAR
*) rowid
, ROWID_LEN
, &cb
) == SQL_SUCCESS
)
1716 strcat(whereClause
, queryTableName
);
1717 strcat(whereClause
, ".ROWID = '");
1718 strcat(whereClause
, rowid
);
1719 strcat(whereClause
, "'");
1723 // If unable to use the ROWID, build a where clause from the keyfields
1724 if (strlen(whereClause
) == 0)
1725 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
, queryTableName
);
1727 // Requery the record
1728 where
= whereClause
;
1733 if (result
&& !GetNext())
1736 // Switch back to original cursor
1737 SetCursor(&currCursor
);
1739 // Free the internal cursor
1740 if (SQLFreeStmt(hstmtInternal
, SQL_CLOSE
) != SQL_SUCCESS
)
1741 pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
);
1743 // Restore the original where and order by clauses
1745 orderBy
= saveOrderBy
;
1749 } // wxTable::Refresh()
1751 /********** wxTable::SetNull(UINT colNo) **********/
1752 bool wxTable::SetNull(int colNo
)
1755 return(colDefs
[colNo
].Null
= TRUE
);
1759 } // wxTable::SetNull(UINT colNo)
1761 /********** wxTable::SetNull(char *colName) **********/
1762 bool wxTable::SetNull(char *colName
)
1765 for (i
= 0; i
< noCols
; i
++)
1767 if (!stricmp(colName
, colDefs
[i
].ColName
))
1772 return(colDefs
[i
].Null
= TRUE
);
1776 } // wxTable::SetNull(char *colName)
1778 /********** wxTable::NewCursor() **********/
1779 HSTMT
*wxTable::NewCursor(bool setCursor
, bool bindColumns
)
1781 HSTMT
*newHSTMT
= new HSTMT
;
1786 if (SQLAllocStmt(hdbc
, newHSTMT
) != SQL_SUCCESS
)
1788 pDb
->DispAllErrors(henv
, hdbc
);
1793 if (SQLSetStmtOption(*newHSTMT
, SQL_CURSOR_TYPE
, cursorType
) != SQL_SUCCESS
)
1795 pDb
->DispAllErrors(henv
, hdbc
, *newHSTMT
);
1802 if(!bindCols(*newHSTMT
))
1810 SetCursor(newHSTMT
);
1814 } // wxTable::NewCursor()
1816 /********** wxTable::DeleteCursor() **********/
1817 bool wxTable::DeleteCursor(HSTMT
*hstmtDel
)
1821 if (!hstmtDel
) // Cursor already deleted
1824 if (SQLFreeStmt(*hstmtDel
, SQL_DROP
) != SQL_SUCCESS
)
1826 pDb
->DispAllErrors(henv
, hdbc
);
1834 } // wxTable::DeleteCursor()
1836 #endif // wxUSE_ODBC