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
, 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 strcpy(tableName
, tblName
); // Table Name
130 strcpy(tablePath
, tblPath
); // Table Path - used for dBase files
132 if (qryTblName
) // Name of the table/view to query
133 strcpy(queryTableName
, qryTblName
);
135 strcpy(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 /********** wxTable::Open() **********/
313 bool wxTable::Open(void)
319 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
321 // Verify that the table exists in the database
322 if (!pDb
->TableExists(tableName
,NULL
,tablePath
))
325 sprintf(s
, "Error opening '%s', table/view does not exist in the database.", tableName
);
330 // Bind the member variables for field exchange between
331 // the wxTable object and the ODBC record.
334 if (!bindInsertParams()) // Inserts
336 if (!bindUpdateParams()) // Updates
339 if (!bindCols(*hstmtDefault
)) // Selects
341 if (!bindCols(hstmtInternal
)) // Internal use only
344 * Do NOT bind the hstmtCount cursor!!!
347 // Build an insert statement using parameter markers
348 if (!queryOnly
&& noCols
> 0)
350 bool needComma
= FALSE
;
351 sprintf(sqlStmt
, "INSERT INTO %s (", tableName
);
352 for (i
= 0; i
< noCols
; i
++)
354 if (! colDefs
[i
].InsertAllowed
)
357 strcat(sqlStmt
, ",");
358 strcat(sqlStmt
, colDefs
[i
].ColName
);
362 strcat(sqlStmt
, ") VALUES (");
363 for (i
= 0; i
< noCols
; i
++)
365 if (! colDefs
[i
].InsertAllowed
)
368 strcat(sqlStmt
, ",");
369 strcat(sqlStmt
, "?");
372 strcat(sqlStmt
, ")");
374 // pDb->WriteSqlLog(sqlStmt);
376 // Prepare the insert statement for execution
377 if (SQLPrepare(hstmtInsert
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
378 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
));
381 // Completed successfully
386 /********** wxTable::Query() **********/
387 bool wxTable::Query(bool forUpdate
, bool distinct
)
390 return(query(DB_SELECT_WHERE
, forUpdate
, distinct
));
392 } // wxTable::Query()
394 /********** wxTable::QueryBySqlStmt() **********/
395 bool wxTable::QueryBySqlStmt(char *pSqlStmt
)
397 pDb
->WriteSqlLog(pSqlStmt
);
399 return(query(DB_SELECT_STATEMENT
, FALSE
, FALSE
, pSqlStmt
));
401 } // wxTable::QueryBySqlStmt()
403 /********** wxTable::QueryMatching() **********/
404 bool wxTable::QueryMatching(bool forUpdate
, bool distinct
)
407 return(query(DB_SELECT_MATCHING
, forUpdate
, distinct
));
409 } // wxTable::QueryMatching()
411 /********** wxTable::QueryOnKeyFields() **********/
412 bool wxTable::QueryOnKeyFields(bool forUpdate
, bool distinct
)
415 return(query(DB_SELECT_KEYFIELDS
, forUpdate
, distinct
));
417 } // wxTable::QueryOnKeyFields()
419 /********** wxTable::query() **********/
420 bool wxTable::query(int queryType
, bool forUpdate
, bool distinct
, char *pSqlStmt
)
422 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
424 // Set the selectForUpdate member variable
426 // The user may wish to select for update, but the DBMS may not be capable
427 selectForUpdate
= CanSelectForUpdate();
429 selectForUpdate
= FALSE
;
431 // Set the SQL SELECT string
432 if (queryType
!= DB_SELECT_STATEMENT
) // A select statement was not passed in,
433 { // so generate a select statement.
434 GetSelectStmt(sqlStmt
, queryType
, distinct
);
435 pDb
->WriteSqlLog(sqlStmt
);
438 // Make sure the cursor is closed first
439 if (! CloseCursor(hstmt
))
442 // Execute the SQL SELECT statement
445 retcode
= SQLExecDirect(hstmt
, (UCHAR FAR
*) (queryType
== DB_SELECT_STATEMENT
? pSqlStmt
: sqlStmt
), SQL_NTS
);
446 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
447 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
));
449 // Completed successfully
452 } // wxTable::query()
454 /********** wxTable::GetSelectStmt() **********/
455 void wxTable::GetSelectStmt(char *pSqlStmt
, int typeOfSelect
, bool distinct
)
457 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
];
461 // Build a select statement to query the database
462 strcpy(pSqlStmt
, "SELECT ");
464 // SELECT DISTINCT values only?
466 strcat(pSqlStmt
, "DISTINCT ");
468 // Was a FROM clause specified to join tables to the base table?
469 // Available for ::Query() only!!!
470 bool appendFromClause
= FALSE
;
471 if (typeOfSelect
== DB_SELECT_WHERE
&& from
&& strlen(from
))
472 appendFromClause
= TRUE
;
474 // Add the column list
476 for (i
= 0; i
< noCols
; i
++)
478 // If joining tables, the base table column names must be qualified to avoid ambiguity
479 if (appendFromClause
)
481 strcat(pSqlStmt
, queryTableName
);
482 strcat(pSqlStmt
, ".");
484 strcat(pSqlStmt
, colDefs
[i
].ColName
);
486 strcat(pSqlStmt
, ",");
489 // If the datasource supports ROWID, get this column as well. Exception: Don't retrieve
490 // the ROWID if querying distinct records. The rowid will always be unique.
491 if (!distinct
&& CanUpdByROWID())
493 // If joining tables, the base table column names must be qualified to avoid ambiguity
494 if (appendFromClause
)
496 strcat(pSqlStmt
, ",");
497 strcat(pSqlStmt
, queryTableName
);
498 strcat(pSqlStmt
, ".ROWID");
501 strcat(pSqlStmt
, ",ROWID");
504 // Append the FROM tablename portion
505 strcat(pSqlStmt
, " FROM ");
506 strcat(pSqlStmt
, queryTableName
);
508 // Sybase uses the HOLDLOCK keyword to lock a record during query.
509 // The HOLDLOCK keyword follows the table name in the from clause.
510 // Each table in the from clause must specify HOLDLOCK or
511 // NOHOLDLOCK (the default). Note: The "FOR UPDATE" clause
512 // is parsed but ignored in SYBASE Transact-SQL.
513 if (selectForUpdate
&& (pDb
->Dbms() == dbmsSYBASE_ASA
|| pDb
->Dbms() == dbmsSYBASE_ASE
))
514 strcat(pSqlStmt
, " HOLDLOCK");
516 if (appendFromClause
)
517 strcat(pSqlStmt
, from
);
519 // Append the WHERE clause. Either append the where clause for the class
520 // or build a where clause. The typeOfSelect determines this.
523 case DB_SELECT_WHERE
:
524 if (where
&& strlen(where
)) // May not want a where clause!!!
526 strcat(pSqlStmt
, " WHERE ");
527 strcat(pSqlStmt
, where
);
530 case DB_SELECT_KEYFIELDS
:
531 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
);
532 if (strlen(whereClause
))
534 strcat(pSqlStmt
, " WHERE ");
535 strcat(pSqlStmt
, whereClause
);
538 case DB_SELECT_MATCHING
:
539 GetWhereClause(whereClause
, DB_WHERE_MATCHING
);
540 if (strlen(whereClause
))
542 strcat(pSqlStmt
, " WHERE ");
543 strcat(pSqlStmt
, whereClause
);
548 // Append the ORDER BY clause
549 if (orderBy
&& strlen(orderBy
))
551 strcat(pSqlStmt
, " ORDER BY ");
552 strcat(pSqlStmt
, orderBy
);
555 // SELECT FOR UPDATE if told to do so and the datasource is capable. Sybase
556 // parses the FOR UPDATE clause but ignores it. See the comment above on the
557 // HOLDLOCK for Sybase.
558 if (selectForUpdate
&& CanSelectForUpdate())
559 strcat(pSqlStmt
, " FOR UPDATE");
561 } // wxTable::GetSelectStmt()
563 /********** wxTable::getRec() **********/
564 bool wxTable::getRec(UWORD fetchType
)
568 #if !wxODBC_FWD_ONLY_CURSORS
570 // Fetch the NEXT, PREV, FIRST or LAST record, depending on fetchType
574 // if ((retcode = SQLExtendedFetch(hstmt, fetchType, 0, &cRowsFetched, &rowStatus)) != SQL_SUCCESS)
575 retcode
= SQLExtendedFetch(hstmt
, fetchType
, 0, &cRowsFetched
, &rowStatus
);
576 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
577 if (retcode
== SQL_NO_DATA_FOUND
)
580 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
));
583 // Fetch the next record from the record set
584 retcode
= SQLFetch(hstmt
);
585 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
587 if (retcode
== SQL_NO_DATA_FOUND
)
590 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
));
594 // Completed successfully
597 } // wxTable::getRec()
599 /********** wxTable::GetRowNum() **********/
600 UWORD
wxTable::GetRowNum(void)
604 if (SQLGetStmtOption(hstmt
, SQL_ROW_NUMBER
, (UCHAR
*) &rowNum
) != SQL_SUCCESS
)
606 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
610 // Completed successfully
611 return((UWORD
) rowNum
);
613 } // wxTable::GetRowNum()
615 /********** wxTable::bindInsertParams() **********/
616 bool wxTable::bindInsertParams(void)
623 UDWORD precision
= 0;
626 // Bind each column (that can be inserted) of the table to a parameter marker
628 for (i
= 0; i
< noCols
; i
++)
630 if (! colDefs
[i
].InsertAllowed
)
632 switch(colDefs
[i
].DbDataType
)
634 case DB_DATA_TYPE_VARCHAR
:
635 fSqlType
= pDb
->typeInfVarchar
.FsqlType
;
636 precision
= colDefs
[i
].SzDataObj
;
638 colDefs
[i
].CbValue
= SQL_NTS
;
640 case DB_DATA_TYPE_INTEGER
:
641 fSqlType
= pDb
->typeInfInteger
.FsqlType
;
642 precision
= pDb
->typeInfInteger
.Precision
;
644 colDefs
[i
].CbValue
= 0;
646 case DB_DATA_TYPE_FLOAT
:
647 fSqlType
= pDb
->typeInfFloat
.FsqlType
;
648 precision
= pDb
->typeInfFloat
.Precision
;
649 scale
= pDb
->typeInfFloat
.MaximumScale
;
650 // SQL Sybase Anywhere v5.5 returned a negative number for the
651 // MaxScale. This caused ODBC to kick out an error on ibscale.
652 // I check for this here and set the scale = precision.
654 // scale = (short) precision;
655 colDefs
[i
].CbValue
= 0;
657 case DB_DATA_TYPE_DATE
:
658 fSqlType
= pDb
->typeInfDate
.FsqlType
;
659 precision
= pDb
->typeInfDate
.Precision
;
661 colDefs
[i
].CbValue
= 0;
667 colDefs
[i
].CbValue
= SQL_NULL_DATA
;
668 colDefs
[i
].Null
= FALSE
;
670 if (SQLBindParameter(hstmtInsert
, i
+1, SQL_PARAM_INPUT
, colDefs
[i
].SqlCtype
,
671 fSqlType
, precision
, scale
, (UCHAR
*) colDefs
[i
].PtrDataObj
,
672 precision
+1,&colDefs
[i
].CbValue
) != SQL_SUCCESS
)
673 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
));
676 // Completed successfully
679 } // wxTable::bindInsertParams()
681 /********** wxTable::bindUpdateParams() **********/
682 bool wxTable::bindUpdateParams(void)
689 UDWORD precision
= 0;
692 // Bind each UPDATEABLE column of the table to a parameter marker
694 for (i
= 0, colNo
= 1; i
< noCols
; i
++)
696 if (! colDefs
[i
].Updateable
)
698 switch(colDefs
[i
].DbDataType
)
700 case DB_DATA_TYPE_VARCHAR
:
701 fSqlType
= pDb
->typeInfVarchar
.FsqlType
;
702 precision
= colDefs
[i
].SzDataObj
;
704 colDefs
[i
].CbValue
= SQL_NTS
;
706 case DB_DATA_TYPE_INTEGER
:
707 fSqlType
= pDb
->typeInfInteger
.FsqlType
;
708 precision
= pDb
->typeInfInteger
.Precision
;
710 colDefs
[i
].CbValue
= 0;
712 case DB_DATA_TYPE_FLOAT
:
713 fSqlType
= pDb
->typeInfFloat
.FsqlType
;
714 precision
= pDb
->typeInfFloat
.Precision
;
715 scale
= pDb
->typeInfFloat
.MaximumScale
;
716 // SQL Sybase Anywhere v5.5 returned a negative number for the
717 // MaxScale. This caused ODBC to kick out an error on ibscale.
718 // I check for this here and set the scale = precision.
720 // scale = (short) precision;
721 colDefs
[i
].CbValue
= 0;
723 case DB_DATA_TYPE_DATE
:
724 fSqlType
= pDb
->typeInfDate
.FsqlType
;
725 precision
= pDb
->typeInfDate
.Precision
;
727 colDefs
[i
].CbValue
= 0;
730 if (SQLBindParameter(hstmtUpdate
, colNo
++, SQL_PARAM_INPUT
, colDefs
[i
].SqlCtype
,
731 fSqlType
, precision
, scale
, (UCHAR
*) colDefs
[i
].PtrDataObj
,
732 precision
+1, &colDefs
[i
].CbValue
) != SQL_SUCCESS
)
733 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
));
736 // Completed successfully
739 } // wxTable::bindUpdateParams()
741 /********** wxTable::bindCols() **********/
742 bool wxTable::bindCols(HSTMT cursor
)
746 // Bind each column of the table to a memory address for fetching data
748 for (i
= 0; i
< noCols
; i
++)
750 if (SQLBindCol(cursor
, i
+1, colDefs
[i
].SqlCtype
, (UCHAR
*) colDefs
[i
].PtrDataObj
,
751 colDefs
[i
].SzDataObj
, &cb
) != SQL_SUCCESS
)
752 return(pDb
->DispAllErrors(henv
, hdbc
, cursor
));
755 // Completed successfully
758 } // wxTable::bindCols()
760 /********** wxTable::CloseCursor() **********/
761 bool wxTable::CloseCursor(HSTMT cursor
)
763 if (SQLFreeStmt(cursor
, SQL_CLOSE
) != SQL_SUCCESS
)
764 return(pDb
->DispAllErrors(henv
, hdbc
, cursor
));
766 // Completed successfully
769 } // wxTable::CloseCursor()
771 /********** wxTable::CreateTable() **********/
772 bool wxTable::CreateTable(bool attemptDrop
)
778 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
780 #ifdef DBDEBUG_CONSOLE
781 cout
<< "Creating Table " << tableName
<< "..." << endl
;
785 if (attemptDrop
&& !DropTable())
789 #ifdef DBDEBUG_CONSOLE
790 for (i
= 0; i
< noCols
; i
++)
792 // Exclude derived columns since they are NOT part of the base table
793 if (colDefs
[i
].DerivedCol
)
795 cout
<< i
+ 1 << ": " << colDefs
[i
].ColName
<< "; ";
796 switch(colDefs
[i
].DbDataType
)
798 case DB_DATA_TYPE_VARCHAR
:
799 cout
<< pDb
->typeInfVarchar
.TypeName
<< "(" << colDefs
[i
].SzDataObj
<< ")";
801 case DB_DATA_TYPE_INTEGER
:
802 cout
<< pDb
->typeInfInteger
.TypeName
;
804 case DB_DATA_TYPE_FLOAT
:
805 cout
<< pDb
->typeInfFloat
.TypeName
;
807 case DB_DATA_TYPE_DATE
:
808 cout
<< pDb
->typeInfDate
.TypeName
;
815 // Build a CREATE TABLE string from the colDefs structure.
816 bool needComma
= FALSE
;
817 sprintf(sqlStmt
, "CREATE TABLE %s (", tableName
);
818 for (i
= 0; i
< noCols
; i
++)
820 // Exclude derived columns since they are NOT part of the base table
821 if (colDefs
[i
].DerivedCol
)
825 strcat(sqlStmt
, ",");
827 strcat(sqlStmt
, colDefs
[i
].ColName
);
828 strcat(sqlStmt
, " ");
830 switch(colDefs
[i
].DbDataType
)
832 case DB_DATA_TYPE_VARCHAR
:
833 strcat(sqlStmt
, pDb
->typeInfVarchar
.TypeName
); break;
834 case DB_DATA_TYPE_INTEGER
:
835 strcat(sqlStmt
, pDb
->typeInfInteger
.TypeName
); break;
836 case DB_DATA_TYPE_FLOAT
:
837 strcat(sqlStmt
, pDb
->typeInfFloat
.TypeName
); break;
838 case DB_DATA_TYPE_DATE
:
839 strcat(sqlStmt
, pDb
->typeInfDate
.TypeName
); break;
841 // For varchars, append the size of the string
842 if (colDefs
[i
].DbDataType
== DB_DATA_TYPE_VARCHAR
)
845 // strcat(sqlStmt, "(");
846 // strcat(sqlStmt, itoa(colDefs[i].SzDataObj, s, 10));
847 // strcat(sqlStmt, ")");
848 sprintf(s
, "(%d)", colDefs
[i
].SzDataObj
);
852 if (pDb
->Dbms() == dbmsSYBASE_ASE
|| pDb
->Dbms() == dbmsMY_SQL
)
854 if (colDefs
[i
].KeyField
)
856 strcat(sqlStmt
, " NOT NULL");
862 // If there is a primary key defined, include it in the create statement
863 for (i
= j
= 0; i
< noCols
; i
++)
865 if (colDefs
[i
].KeyField
)
871 if (j
&& pDb
->Dbms() != dbmsDBASE
) // Found a keyfield
873 if (pDb
->Dbms() != dbmsMY_SQL
)
875 strcat(sqlStmt
, ",CONSTRAINT ");
876 strcat(sqlStmt
, tableName
);
877 strcat(sqlStmt
, "_PIDX PRIMARY KEY (");
881 /* MySQL goes out on this one. We also declare the relevant key NON NULL above */
882 strcat(sqlStmt
, ", PRIMARY KEY (");
885 // List column name(s) of column(s) comprising the primary key
886 for (i
= j
= 0; i
< noCols
; i
++)
888 if (colDefs
[i
].KeyField
)
890 if (j
++) // Multi part key, comma separate names
891 strcat(sqlStmt
, ",");
892 strcat(sqlStmt
, colDefs
[i
].ColName
);
895 strcat(sqlStmt
, ")");
897 // Append the closing parentheses for the create table statement
898 strcat(sqlStmt
, ")");
900 pDb
->WriteSqlLog(sqlStmt
);
902 #ifdef DBDEBUG_CONSOLE
903 cout
<< endl
<< sqlStmt
<< endl
;
906 // Execute the CREATE TABLE statement
907 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
909 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
910 pDb
->RollbackTrans();
915 // Commit the transaction and close the cursor
916 if (! pDb
->CommitTrans())
918 if (! CloseCursor(hstmt
))
921 // Database table created successfully
924 } // wxTable::CreateTable()
926 /********** wxTable::DropTable() **********/
927 bool wxTable::DropTable()
929 // NOTE: This function returns TRUE if the Table does not exist, but
930 // only for identified databases. Code will need to be added
931 // below for any other databases when those databases are defined
932 // to handle this situation consistently
934 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
936 sprintf(sqlStmt
, "DROP TABLE %s", tableName
);
938 pDb
->WriteSqlLog(sqlStmt
);
940 #ifdef DBDEBUG_CONSOLE
941 cout
<< endl
<< sqlStmt
<< endl
;
944 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
946 // Check for "Base table not found" error and ignore
947 pDb
->GetNextError(henv
, hdbc
, hstmt
);
948 if (wxStrcmp(pDb
->sqlState
,"S0002")) // "Base table not found"
950 // Check for product specific error codes
951 if (!((pDb
->Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(pDb
->sqlState
,"42000")) || // 5.x (and lower?)
952 (pDb
->Dbms() == dbmsMY_SQL
&& !wxStrcmp(pDb
->sqlState
,"S1000")) || // untested
953 (pDb
->Dbms() == dbmsPOSTGRES
&& !wxStrcmp(pDb
->sqlState
,"08S01")))) // untested
955 pDb
->DispNextError();
956 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
957 pDb
->RollbackTrans();
964 // Commit the transaction and close the cursor
965 if (! pDb
->CommitTrans())
967 if (! CloseCursor(hstmt
))
971 } // wxTable::DropTable()
973 /********** wxTable::CreateIndex() **********/
974 bool wxTable::CreateIndex(char * idxName
, bool unique
, int noIdxCols
, CidxDef
*pIdxDefs
, bool attemptDrop
)
976 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
978 // Drop the index first
979 if (attemptDrop
&& !DropIndex(idxName
))
982 // Build a CREATE INDEX statement
983 strcpy(sqlStmt
, "CREATE ");
985 strcat(sqlStmt
, "UNIQUE ");
987 strcat(sqlStmt
, "INDEX ");
988 strcat(sqlStmt
, idxName
);
989 strcat(sqlStmt
, " ON ");
990 strcat(sqlStmt
, tableName
);
991 strcat(sqlStmt
, " (");
993 // Append list of columns making up index
995 for (i
= 0; i
< noIdxCols
; i
++)
997 strcat(sqlStmt
, pIdxDefs
[i
].ColName
);
998 /* Postgres doesn't cope with ASC */
999 if (pDb
->Dbms() != dbmsPOSTGRES
)
1001 if (pIdxDefs
[i
].Ascending
)
1002 strcat(sqlStmt
, " ASC");
1004 strcat(sqlStmt
, " DESC");
1007 if ((i
+ 1) < noIdxCols
)
1008 strcat(sqlStmt
, ",");
1011 // Append closing parentheses
1012 strcat(sqlStmt
, ")");
1014 pDb
->WriteSqlLog(sqlStmt
);
1016 #ifdef DBDEBUG_CONSOLE
1017 cout
<< endl
<< sqlStmt
<< endl
<< endl
;
1020 // Execute the CREATE INDEX statement
1021 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
1023 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
1024 pDb
->RollbackTrans();
1029 // Commit the transaction and close the cursor
1030 if (! pDb
->CommitTrans())
1032 if (! CloseCursor(hstmt
))
1035 // Index Created Successfully
1038 } // wxTable::CreateIndex()
1040 /********** wxTable::DropIndex() **********/
1041 bool wxTable::DropIndex(char * idxName
)
1043 // NOTE: This function returns TRUE if the Index does not exist, but
1044 // only for identified databases. Code will need to be added
1045 // below for any other databases when those databases are defined
1046 // to handle this situation consistently
1048 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1050 if (pDb
->Dbms() == dbmsACCESS
)
1051 sprintf(sqlStmt
, "DROP INDEX %s ON %s",idxName
,tableName
);
1052 else if (pDb
->Dbms() == dbmsSYBASE_ASE
)
1053 sprintf(sqlStmt
, "DROP INDEX %s.%s",tableName
,idxName
);
1055 sprintf(sqlStmt
, "DROP INDEX %s",idxName
);
1057 pDb
->WriteSqlLog(sqlStmt
);
1059 #ifdef DBDEBUG_CONSOLE
1060 cout
<< endl
<< sqlStmt
<< endl
;
1063 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
1065 // Check for "Index not found" error and ignore
1066 pDb
->GetNextError(henv
, hdbc
, hstmt
);
1067 if (wxStrcmp(pDb
->sqlState
,"S0012")) // "Index not found"
1069 // Check for product specific error codes
1070 if (!((pDb
->Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(pDb
->sqlState
,"42000")) || // v5.x (and lower?)
1071 (pDb
->Dbms() == dbmsSYBASE_ASE
&& !wxStrcmp(pDb
->sqlState
,"S0002")) || // Base table not found
1072 (pDb
->Dbms() == dbmsMY_SQL
&& !wxStrcmp(pDb
->sqlState
,"42S02")) // untested
1075 pDb
->DispNextError();
1076 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
1077 pDb
->RollbackTrans();
1084 // Commit the transaction and close the cursor
1085 if (! pDb
->CommitTrans())
1087 if (! CloseCursor(hstmt
))
1091 } // wxTable::DropIndex()
1093 /********** wxTable::Insert() **********/
1094 int wxTable::Insert(void)
1102 // Insert the record by executing the already prepared insert statement
1104 retcode
=SQLExecute(hstmtInsert
);
1105 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1107 // Check to see if integrity constraint was violated
1108 pDb
->GetNextError(henv
, hdbc
, hstmtInsert
);
1109 if (! wxStrcmp(pDb
->sqlState
, "23000")) // Integrity constraint violated
1110 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1113 pDb
->DispNextError();
1114 pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
);
1119 // Record inserted into the datasource successfully
1122 } // wxTable::Insert()
1124 /********** wxTable::Update(pSqlStmt) **********/
1125 bool wxTable::Update(char *pSqlStmt
)
1131 pDb
->WriteSqlLog(pSqlStmt
);
1133 return(execUpdate(pSqlStmt
));
1135 } // wxTable::Update(pSqlStmt)
1137 /********** wxTable::Update() **********/
1138 bool wxTable::Update(void)
1144 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1146 // Build the SQL UPDATE statement
1147 GetUpdateStmt(sqlStmt
, DB_UPD_KEYFIELDS
);
1149 pDb
->WriteSqlLog(sqlStmt
);
1151 #ifdef DBDEBUG_CONSOLE
1152 cout
<< endl
<< sqlStmt
<< endl
<< endl
;
1155 // Execute the SQL UPDATE statement
1156 return(execUpdate(sqlStmt
));
1158 } // wxTable::Update()
1160 /********** wxTable::UpdateWhere() **********/
1161 bool wxTable::UpdateWhere(char *pWhereClause
)
1167 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1169 // Build the SQL UPDATE statement
1170 GetUpdateStmt(sqlStmt
, DB_UPD_WHERE
, pWhereClause
);
1172 pDb
->WriteSqlLog(sqlStmt
);
1174 #ifdef DBDEBUG_CONSOLE
1175 cout
<< endl
<< sqlStmt
<< endl
<< endl
;
1178 // Execute the SQL UPDATE statement
1179 return(execUpdate(sqlStmt
));
1181 } // wxTable::UpdateWhere()
1183 /********** wxTable::Delete() **********/
1184 bool wxTable::Delete(void)
1190 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1192 // Build the SQL DELETE statement
1193 GetDeleteStmt(sqlStmt
, DB_DEL_KEYFIELDS
);
1195 pDb
->WriteSqlLog(sqlStmt
);
1197 // Execute the SQL DELETE statement
1198 return(execDelete(sqlStmt
));
1200 } // wxTable::Delete()
1202 /********** wxTable::DeleteWhere() **********/
1203 bool wxTable::DeleteWhere(char *pWhereClause
)
1209 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1211 // Build the SQL DELETE statement
1212 GetDeleteStmt(sqlStmt
, DB_DEL_WHERE
, pWhereClause
);
1214 pDb
->WriteSqlLog(sqlStmt
);
1216 // Execute the SQL DELETE statement
1217 return(execDelete(sqlStmt
));
1219 } // wxTable::DeleteWhere()
1221 /********** wxTable::DeleteMatching() **********/
1222 bool wxTable::DeleteMatching(void)
1228 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1230 // Build the SQL DELETE statement
1231 GetDeleteStmt(sqlStmt
, DB_DEL_MATCHING
);
1233 pDb
->WriteSqlLog(sqlStmt
);
1235 // Execute the SQL DELETE statement
1236 return(execDelete(sqlStmt
));
1238 } // wxTable::DeleteMatching()
1240 /********** wxTable::execDelete() **********/
1241 bool wxTable::execDelete(char *pSqlStmt
)
1243 // Execute the DELETE statement
1244 if (SQLExecDirect(hstmtDelete
, (UCHAR FAR
*) pSqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
1245 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
));
1247 // Record deleted successfully
1250 } // wxTable::execDelete()
1252 /********** wxTable::execUpdate() **********/
1253 bool wxTable::execUpdate(char *pSqlStmt
)
1255 // Execute the UPDATE statement
1256 if (SQLExecDirect(hstmtUpdate
, (UCHAR FAR
*) pSqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
1257 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
));
1259 // Record deleted successfully
1262 } // wxTable::execUpdate()
1264 /********** wxTable::GetUpdateStmt() **********/
1265 void wxTable::GetUpdateStmt(char *pSqlStmt
, int typeOfUpd
, char *pWhereClause
)
1271 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
];
1272 bool firstColumn
= TRUE
;
1275 sprintf(pSqlStmt
, "UPDATE %s SET ", tableName
);
1277 // Append a list of columns to be updated
1279 for (i
= 0; i
< noCols
; i
++)
1281 // Only append Updateable columns
1282 if (colDefs
[i
].Updateable
)
1285 strcat(pSqlStmt
, ",");
1287 firstColumn
= FALSE
;
1288 strcat(pSqlStmt
, colDefs
[i
].ColName
);
1289 strcat(pSqlStmt
, " = ?");
1293 // Append the WHERE clause to the SQL UPDATE statement
1294 strcat(pSqlStmt
, " WHERE ");
1297 case DB_UPD_KEYFIELDS
:
1298 // If the datasource supports the ROWID column, build
1299 // the where on ROWID for efficiency purposes.
1300 // e.g. UPDATE PARTS SET Col1 = ?, Col2 = ? WHERE ROWID = '111.222.333'
1301 if (CanUpdByROWID())
1304 char rowid
[ROWID_LEN
];
1306 // Get the ROWID value. If not successful retreiving the ROWID,
1307 // simply fall down through the code and build the WHERE clause
1308 // based on the key fields.
1309 if (SQLGetData(hstmt
, noCols
+1, SQL_C_CHAR
, (UCHAR
*) rowid
, ROWID_LEN
, &cb
) == SQL_SUCCESS
)
1311 strcat(pSqlStmt
, "ROWID = '");
1312 strcat(pSqlStmt
, rowid
);
1313 strcat(pSqlStmt
, "'");
1317 // Unable to delete by ROWID, so build a WHERE
1318 // clause based on the keyfields.
1319 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
);
1320 strcat(pSqlStmt
, whereClause
);
1323 strcat(pSqlStmt
, pWhereClause
);
1327 } // GetUpdateStmt()
1329 /********** wxTable::GetDeleteStmt() **********/
1330 void wxTable::GetDeleteStmt(char *pSqlStmt
, int typeOfDel
, char *pWhereClause
)
1336 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
];
1340 // Handle the case of DeleteWhere() and the where clause is blank. It should
1341 // delete all records from the database in this case.
1342 if (typeOfDel
== DB_DEL_WHERE
&& (pWhereClause
== 0 || strlen(pWhereClause
) == 0))
1344 sprintf(pSqlStmt
, "DELETE FROM %s", tableName
);
1348 sprintf(pSqlStmt
, "DELETE FROM %s WHERE ", tableName
);
1350 // Append the WHERE clause to the SQL DELETE statement
1353 case DB_DEL_KEYFIELDS
:
1354 // If the datasource supports the ROWID column, build
1355 // the where on ROWID for efficiency purposes.
1356 // e.g. DELETE FROM PARTS WHERE ROWID = '111.222.333'
1357 if (CanUpdByROWID())
1360 char rowid
[ROWID_LEN
];
1362 // Get the ROWID value. If not successful retreiving the ROWID,
1363 // simply fall down through the code and build the WHERE clause
1364 // based on the key fields.
1365 if (SQLGetData(hstmt
, noCols
+1, SQL_C_CHAR
, (UCHAR
*) rowid
, ROWID_LEN
, &cb
) == SQL_SUCCESS
)
1367 strcat(pSqlStmt
, "ROWID = '");
1368 strcat(pSqlStmt
, rowid
);
1369 strcat(pSqlStmt
, "'");
1373 // Unable to delete by ROWID, so build a WHERE
1374 // clause based on the keyfields.
1375 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
);
1376 strcat(pSqlStmt
, whereClause
);
1379 strcat(pSqlStmt
, pWhereClause
);
1381 case DB_DEL_MATCHING
:
1382 GetWhereClause(whereClause
, DB_WHERE_MATCHING
);
1383 strcat(pSqlStmt
, whereClause
);
1387 } // GetDeleteStmt()
1389 /********** wxTable::GetWhereClause() **********/
1391 * Note: GetWhereClause() currently ignores timestamp columns.
1392 * They are not included as part of the where clause.
1395 void wxTable::GetWhereClause(char *pWhereClause
, int typeOfWhere
, char *qualTableName
)
1397 bool moreThanOneColumn
= FALSE
;
1400 // Loop through the columns building a where clause as you go
1402 for (i
= 0; i
< noCols
; i
++)
1404 // Determine if this column should be included in the WHERE clause
1405 if ((typeOfWhere
== DB_WHERE_KEYFIELDS
&& colDefs
[i
].KeyField
) ||
1406 (typeOfWhere
== DB_WHERE_MATCHING
&& (! IsColNull(i
))))
1408 // Skip over timestamp columns
1409 if (colDefs
[i
].SqlCtype
== SQL_C_TIMESTAMP
)
1411 // If there is more than 1 column, join them with the keyword "AND"
1412 if (moreThanOneColumn
)
1413 strcat(pWhereClause
, " AND ");
1415 moreThanOneColumn
= TRUE
;
1416 // Concatenate where phrase for the column
1417 if (qualTableName
&& strlen(qualTableName
))
1419 strcat(pWhereClause
, qualTableName
);
1420 strcat(pWhereClause
, ".");
1422 strcat(pWhereClause
, colDefs
[i
].ColName
);
1423 strcat(pWhereClause
, " = ");
1424 switch(colDefs
[i
].SqlCtype
)
1427 sprintf(colValue
, "'%s'", (UCHAR FAR
*) colDefs
[i
].PtrDataObj
);
1430 sprintf(colValue
, "%hi", *((SWORD
*) colDefs
[i
].PtrDataObj
));
1433 sprintf(colValue
, "%hu", *((UWORD
*) colDefs
[i
].PtrDataObj
));
1436 sprintf(colValue
, "%li", *((SDWORD
*) colDefs
[i
].PtrDataObj
));
1439 sprintf(colValue
, "%lu", *((UDWORD
*) colDefs
[i
].PtrDataObj
));
1442 sprintf(colValue
, "%.6f", *((SFLOAT
*) colDefs
[i
].PtrDataObj
));
1445 sprintf(colValue
, "%.6f", *((SDOUBLE
*) colDefs
[i
].PtrDataObj
));
1448 strcat(pWhereClause
, colValue
);
1452 } // wxTable::GetWhereClause()
1454 /********** wxTable::IsColNull() **********/
1455 bool wxTable::IsColNull(int colNo
)
1457 switch(colDefs
[colNo
].SqlCtype
)
1460 return(((UCHAR FAR
*) colDefs
[colNo
].PtrDataObj
)[0] == 0);
1462 return(( *((SWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1464 return(( *((UWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1466 return(( *((SDWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1468 return(( *((UDWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1470 return(( *((SFLOAT
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1472 return((*((SDOUBLE
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1473 case SQL_C_TIMESTAMP
:
1474 TIMESTAMP_STRUCT
*pDt
;
1475 pDt
= (TIMESTAMP_STRUCT
*) colDefs
[colNo
].PtrDataObj
;
1476 if (pDt
->year
== 0 && pDt
->month
== 0 && pDt
->day
== 0)
1484 } // wxTable::IsColNull()
1486 /********** wxTable::CanSelectForUpdate() **********/
1487 bool wxTable::CanSelectForUpdate(void)
1489 if (pDb
->Dbms() == dbmsMY_SQL
)
1492 if (pDb
->dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1497 } // wxTable::CanSelectForUpdate()
1499 /********** wxTable::CanUpdByROWID() **********/
1500 bool wxTable::CanUpdByROWID(void)
1503 //NOTE: Returning FALSE for now until this can be debugged,
1504 // as the ROWID is not getting updated correctly
1507 if (pDb
->Dbms() == dbmsORACLE
)
1512 } // wxTable::CanUpdByROWID()
1514 /********** wxTable::IsCursorClosedOnCommit() **********/
1515 bool wxTable::IsCursorClosedOnCommit(void)
1517 if (pDb
->dbInf
.cursorCommitBehavior
== SQL_CB_PRESERVE
)
1522 } // wxTable::IsCursorClosedOnCommit()
1524 /********** wxTable::ClearMemberVars() **********/
1525 void wxTable::ClearMemberVars(void)
1527 // Loop through the columns setting each member variable to zero
1529 for (i
= 0; i
< noCols
; i
++)
1531 switch(colDefs
[i
].SqlCtype
)
1534 ((UCHAR FAR
*) colDefs
[i
].PtrDataObj
)[0] = 0;
1537 *((SWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1540 *((UWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1543 *((SDWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1546 *((UDWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1549 *((SFLOAT
*) colDefs
[i
].PtrDataObj
) = 0.0f
;
1552 *((SDOUBLE
*) colDefs
[i
].PtrDataObj
) = 0.0f
;
1554 case SQL_C_TIMESTAMP
:
1555 TIMESTAMP_STRUCT
*pDt
;
1556 pDt
= (TIMESTAMP_STRUCT
*) colDefs
[i
].PtrDataObj
;
1568 } // wxTable::ClearMemberVars()
1570 /********** wxTable::SetQueryTimeout() **********/
1571 bool wxTable::SetQueryTimeout(UDWORD nSeconds
)
1573 if (SQLSetStmtOption(hstmtInsert
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1574 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
));
1575 if (SQLSetStmtOption(hstmtUpdate
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1576 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
));
1577 if (SQLSetStmtOption(hstmtDelete
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1578 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
));
1579 if (SQLSetStmtOption(hstmtInternal
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1580 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
));
1582 // Completed Successfully
1585 } // wxTable::SetQueryTimeout()
1587 /********** wxTable::SetColDefs() **********/
1588 void wxTable::SetColDefs (int index
, char *fieldName
, int dataType
, void *pData
,
1589 int cType
, int size
, bool keyField
, bool upd
,
1590 bool insAllow
, bool derivedCol
)
1592 if (!colDefs
) // May happen if the database connection fails
1595 if (strlen(fieldName
) > (unsigned int) DB_MAX_COLUMN_NAME_LEN
)
1597 strncpy (colDefs
[index
].ColName
, fieldName
, DB_MAX_COLUMN_NAME_LEN
);
1598 colDefs
[index
].ColName
[DB_MAX_COLUMN_NAME_LEN
] = 0;
1601 strcpy(colDefs
[index
].ColName
, fieldName
);
1603 colDefs
[index
].DbDataType
= dataType
;
1604 colDefs
[index
].PtrDataObj
= pData
;
1605 colDefs
[index
].SqlCtype
= cType
;
1606 colDefs
[index
].SzDataObj
= size
;
1607 colDefs
[index
].KeyField
= keyField
;
1608 colDefs
[index
].DerivedCol
= derivedCol
;
1609 // Derived columns by definition would NOT be "Insertable" or "Updateable"
1612 colDefs
[index
].Updateable
= FALSE
;
1613 colDefs
[index
].InsertAllowed
= FALSE
;
1617 colDefs
[index
].Updateable
= upd
;
1618 colDefs
[index
].InsertAllowed
= insAllow
;
1621 colDefs
[index
].Null
= FALSE
;
1623 } // wxTable::SetColDefs()
1625 /********** wxTable::SetCursor() **********/
1626 void wxTable::SetCursor(HSTMT
*hstmtActivate
)
1628 if (hstmtActivate
== DEFAULT_CURSOR
)
1629 hstmt
= *hstmtDefault
;
1631 hstmt
= *hstmtActivate
;
1633 } // wxTable::SetCursor()
1635 /********** wxTable::Count() **********/
1636 ULONG
wxTable::Count(void)
1639 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1642 // Build a "SELECT COUNT(*) FROM queryTableName [WHERE whereClause]" SQL Statement
1643 strcpy(sqlStmt
, "SELECT COUNT(*) FROM ");
1644 strcat(sqlStmt
, queryTableName
);
1646 if (from
&& strlen(from
))
1647 strcat(sqlStmt
, from
);
1649 // Add the where clause if one is provided
1650 if (where
&& strlen(where
))
1652 strcat(sqlStmt
, " WHERE ");
1653 strcat(sqlStmt
, where
);
1656 pDb
->WriteSqlLog(sqlStmt
);
1658 // Initialize the Count cursor if it's not already initialized
1661 hstmtCount
= NewCursor(FALSE
,FALSE
);
1667 // Execute the SQL statement
1668 if (SQLExecDirect(*hstmtCount
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
1670 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1675 if (SQLFetch(*hstmtCount
) != SQL_SUCCESS
)
1677 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1681 // Obtain the result
1682 if (SQLGetData(*hstmtCount
, 1, SQL_C_ULONG
, &l
, sizeof(l
), &cb
) != SQL_SUCCESS
)
1684 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1689 if (SQLFreeStmt(*hstmtCount
, SQL_CLOSE
) != SQL_SUCCESS
)
1690 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1692 // Return the record count
1695 } // wxTable::Count()
1697 /********** wxTable::Refresh() **********/
1698 bool wxTable::Refresh(void)
1702 // Switch to the internal cursor so any active cursors are not corrupted
1703 HSTMT currCursor
= GetCursor();
1704 hstmt
= hstmtInternal
;
1706 // Save the where and order by clauses
1707 char *saveWhere
= where
;
1708 char *saveOrderBy
= orderBy
;
1710 // Build a where clause to refetch the record with. Try and use the
1711 // ROWID if it's available, ow use the key fields.
1712 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
+1];
1713 strcpy(whereClause
, "");
1714 if (CanUpdByROWID())
1717 char rowid
[ROWID_LEN
+1];
1719 // Get the ROWID value. If not successful retreiving the ROWID,
1720 // simply fall down through the code and build the WHERE clause
1721 // based on the key fields.
1722 if (SQLGetData(hstmt
, noCols
+1, SQL_C_CHAR
, (UCHAR
*) rowid
, ROWID_LEN
, &cb
) == SQL_SUCCESS
)
1724 strcat(whereClause
, queryTableName
);
1725 strcat(whereClause
, ".ROWID = '");
1726 strcat(whereClause
, rowid
);
1727 strcat(whereClause
, "'");
1731 // If unable to use the ROWID, build a where clause from the keyfields
1732 if (strlen(whereClause
) == 0)
1733 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
, queryTableName
);
1735 // Requery the record
1736 where
= whereClause
;
1741 if (result
&& !GetNext())
1744 // Switch back to original cursor
1745 SetCursor(&currCursor
);
1747 // Free the internal cursor
1748 if (SQLFreeStmt(hstmtInternal
, SQL_CLOSE
) != SQL_SUCCESS
)
1749 pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
);
1751 // Restore the original where and order by clauses
1753 orderBy
= saveOrderBy
;
1757 } // wxTable::Refresh()
1759 /********** wxTable::SetNull(UINT colNo) **********/
1760 bool wxTable::SetNull(int colNo
)
1763 return(colDefs
[colNo
].Null
= TRUE
);
1767 } // wxTable::SetNull(UINT colNo)
1769 /********** wxTable::SetNull(char *colName) **********/
1770 bool wxTable::SetNull(char *colName
)
1773 for (i
= 0; i
< noCols
; i
++)
1775 if (!wxStricmp(colName
, colDefs
[i
].ColName
))
1780 return(colDefs
[i
].Null
= TRUE
);
1784 } // wxTable::SetNull(char *colName)
1786 /********** wxTable::NewCursor() **********/
1787 HSTMT
*wxTable::NewCursor(bool setCursor
, bool bindColumns
)
1789 HSTMT
*newHSTMT
= new HSTMT
;
1794 if (SQLAllocStmt(hdbc
, newHSTMT
) != SQL_SUCCESS
)
1796 pDb
->DispAllErrors(henv
, hdbc
);
1801 if (SQLSetStmtOption(*newHSTMT
, SQL_CURSOR_TYPE
, cursorType
) != SQL_SUCCESS
)
1803 pDb
->DispAllErrors(henv
, hdbc
, *newHSTMT
);
1810 if(!bindCols(*newHSTMT
))
1818 SetCursor(newHSTMT
);
1822 } // wxTable::NewCursor()
1824 /********** wxTable::DeleteCursor() **********/
1825 bool wxTable::DeleteCursor(HSTMT
*hstmtDel
)
1829 if (!hstmtDel
) // Cursor already deleted
1832 if (SQLFreeStmt(*hstmtDel
, SQL_DROP
) != SQL_SUCCESS
)
1834 pDb
->DispAllErrors(henv
, hdbc
);
1842 } // wxTable::DeleteCursor()
1844 #endif // wxUSE_ODBC