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"
86 #define stricmp _stricmp
87 #define strnicmp _strnicmp
89 int strcasecmp(const char *str_1
, const char *str_2
) ;
90 int strncasecmp(const char *str_1
, const char *str_2
, size_t maxchar
) ;
91 #define stricmp strcasecmp
92 #define strnicmp strncasecmp
97 // The HPUX preprocessor lines below were commented out on 8/20/97
98 // because macros.h currently redefines DEBUG and is unneeded.
100 // # include <macros.h>
103 # include <sys/minmax.h>
107 ULONG lastTableID
= 0;
115 /********** wxTable::wxTable() **********/
116 wxTable::wxTable(wxDB
*pwxDB
, const char *tblName
, const int nCols
,
117 const char *qryTblName
, bool qryOnly
, char *tblPath
)
119 pDb
= pwxDB
; // Pointer to the wxDB object
123 hstmtDefault
= 0; // Initialized below
124 hstmtCount
= 0; // Initialized first time it is needed
131 noCols
= nCols
; // No. of cols in the table
132 where
= 0; // Where clause
133 orderBy
= 0; // Order By clause
134 from
= 0; // From clause
135 selectForUpdate
= FALSE
; // SELECT ... FOR UPDATE; Indicates whether to include the FOR UPDATE phrase
140 strcpy(tableName
, tblName
); // Table Name
142 strcpy(tablePath
, tblPath
); // Table Path - used for dBase files
144 if (qryTblName
) // Name of the table/view to query
145 strcpy(queryTableName
, qryTblName
);
147 strcpy(queryTableName
, tblName
);
149 // assert(pDb); // Assert is placed after table name is assigned for error reporting reasons
156 tableID
= ++lastTableID
;
157 sprintf(s
, "wxTable constructor (%-20s) tableID:[%6lu] pDb:[%p]", tblName
,tableID
,pDb
);
160 CstructTablesInUse
*tableInUse
;
161 tableInUse
= new CstructTablesInUse();
162 tableInUse
->tableName
= tblName
;
163 tableInUse
->tableID
= tableID
;
164 tableInUse
->pDb
= pDb
;
165 TablesInUse
.Append(tableInUse
);
170 // Grab the HENV and HDBC from the wxDB object
174 // Allocate space for column definitions
176 colDefs
= new CcolDef
[noCols
]; // Points to the first column defintion
178 // Allocate statement handles for the table
181 // Allocate a separate statement handle for performing inserts
182 if (SQLAllocStmt(hdbc
, &hstmtInsert
) != SQL_SUCCESS
)
183 pDb
->DispAllErrors(henv
, hdbc
);
184 // Allocate a separate statement handle for performing deletes
185 if (SQLAllocStmt(hdbc
, &hstmtDelete
) != SQL_SUCCESS
)
186 pDb
->DispAllErrors(henv
, hdbc
);
187 // Allocate a separate statement handle for performing updates
188 if (SQLAllocStmt(hdbc
, &hstmtUpdate
) != SQL_SUCCESS
)
189 pDb
->DispAllErrors(henv
, hdbc
);
191 // Allocate a separate statement handle for internal use
192 if (SQLAllocStmt(hdbc
, &hstmtInternal
) != SQL_SUCCESS
)
193 pDb
->DispAllErrors(henv
, hdbc
);
195 // Set the cursor type for the statement handles
196 cursorType
= SQL_CURSOR_STATIC
;
197 if (SQLSetStmtOption(hstmtInternal
, SQL_CURSOR_TYPE
, cursorType
) != SQL_SUCCESS
)
199 // Check to see if cursor type is supported
200 pDb
->GetNextError(henv
, hdbc
, hstmtInternal
);
201 if (! wxStrcmp(pDb
->sqlState
, "01S02")) // Option Value Changed
203 // Datasource does not support static cursors. Driver
204 // will substitute a cursor type. Call SQLGetStmtOption()
205 // to determine which cursor type was selected.
206 if (SQLGetStmtOption(hstmtInternal
, SQL_CURSOR_TYPE
, &cursorType
) != SQL_SUCCESS
)
207 pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
);
208 #ifdef DBDEBUG_CONSOLE
209 cout
<< "Static cursor changed to: ";
212 case SQL_CURSOR_FORWARD_ONLY
:
213 cout
<< "Forward Only"; break;
214 case SQL_CURSOR_STATIC
:
215 cout
<< "Static"; break;
216 case SQL_CURSOR_KEYSET_DRIVEN
:
217 cout
<< "Keyset Driven"; break;
218 case SQL_CURSOR_DYNAMIC
:
219 cout
<< "Dynamic"; break;
221 cout
<< endl
<< endl
;
226 pDb
->DispNextError();
227 pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
);
230 #ifdef DBDEBUG_CONSOLE
232 cout
<< "Cursor Type set to STATIC" << endl
<< endl
;
237 // Set the cursor type for the INSERT statement handle
238 if (SQLSetStmtOption(hstmtInsert
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
)
239 pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
);
240 // Set the cursor type for the DELETE statement handle
241 if (SQLSetStmtOption(hstmtDelete
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
)
242 pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
);
243 // Set the cursor type for the UPDATE statement handle
244 if (SQLSetStmtOption(hstmtUpdate
, SQL_CURSOR_TYPE
, SQL_CURSOR_FORWARD_ONLY
) != SQL_SUCCESS
)
245 pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
);
248 // Make the default cursor the active cursor
249 hstmtDefault
= NewCursor(FALSE
,FALSE
);
250 assert(hstmtDefault
);
251 hstmt
= *hstmtDefault
;
253 } // wxTable::wxTable()
255 /********** wxTable::~wxTable() **********/
261 sprintf(s
, "wxTable destructor (%-20s) tableID:[%6lu] pDb:[%p]", tableName
,tableID
,pDb
);
270 pNode
= TablesInUse
.First();
271 while (pNode
&& !found
)
273 if (((CstructTablesInUse
*)pNode
->Data())->tableID
== tableID
)
276 if (!TablesInUse
.DeleteNode(pNode
))
277 wxMessageBox (s
,"Unable to delete node!");
280 pNode
= pNode
->Next();
285 sprintf(msg
,"Unable to find the tableID in the linked\nlist of tables in use.\n\n%s",s
);
286 wxMessageBox (msg
,"NOTICE...");
291 // Decrement the wxDB table count
295 // Delete memory allocated for column definitions
299 // Free statement handles
303 if (SQLFreeStmt(hstmtInsert
, SQL_DROP
) != SQL_SUCCESS
)
304 pDb
->DispAllErrors(henv
, hdbc
);
306 if (SQLFreeStmt(hstmtDelete
, SQL_DROP
) != SQL_SUCCESS
)
307 pDb
->DispAllErrors(henv
, hdbc
);
309 if (SQLFreeStmt(hstmtUpdate
, SQL_DROP
) != SQL_SUCCESS
)
310 pDb
->DispAllErrors(henv
, hdbc
);
313 if (SQLFreeStmt(hstmtInternal
, SQL_DROP
) != SQL_SUCCESS
)
314 pDb
->DispAllErrors(henv
, hdbc
);
316 // Delete dynamically allocated cursors
318 DeleteCursor(hstmtDefault
);
320 DeleteCursor(hstmtCount
);
322 } // wxTable::~wxTable()
324 /********** wxTable::Open() **********/
325 bool wxTable::Open(void)
331 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
333 // Verify that the table exists in the database
334 if (!pDb
->TableExists(tableName
,NULL
,tablePath
))
337 sprintf(s
, "Error opening '%s', table/view does not exist in the database.", tableName
);
342 // Bind the member variables for field exchange between
343 // the wxTable object and the ODBC record.
346 if (!bindInsertParams()) // Inserts
348 if (!bindUpdateParams()) // Updates
351 if (!bindCols(*hstmtDefault
)) // Selects
353 if (!bindCols(hstmtInternal
)) // Internal use only
356 * Do NOT bind the hstmtCount cursor!!!
359 // Build an insert statement using parameter markers
360 if (!queryOnly
&& noCols
> 0)
362 bool needComma
= FALSE
;
363 sprintf(sqlStmt
, "INSERT INTO %s (", tableName
);
364 for (i
= 0; i
< noCols
; i
++)
366 if (! colDefs
[i
].InsertAllowed
)
369 strcat(sqlStmt
, ",");
370 strcat(sqlStmt
, colDefs
[i
].ColName
);
374 strcat(sqlStmt
, ") VALUES (");
375 for (i
= 0; i
< noCols
; i
++)
377 if (! colDefs
[i
].InsertAllowed
)
380 strcat(sqlStmt
, ",");
381 strcat(sqlStmt
, "?");
384 strcat(sqlStmt
, ")");
386 // pDb->WriteSqlLog(sqlStmt);
388 // Prepare the insert statement for execution
389 if (SQLPrepare(hstmtInsert
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
390 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
));
393 // Completed successfully
398 /********** wxTable::Query() **********/
399 bool wxTable::Query(bool forUpdate
, bool distinct
)
402 return(query(DB_SELECT_WHERE
, forUpdate
, distinct
));
404 } // wxTable::Query()
406 /********** wxTable::QueryBySqlStmt() **********/
407 bool wxTable::QueryBySqlStmt(char *pSqlStmt
)
409 pDb
->WriteSqlLog(pSqlStmt
);
411 return(query(DB_SELECT_STATEMENT
, FALSE
, FALSE
, pSqlStmt
));
413 } // wxTable::QueryBySqlStmt()
415 /********** wxTable::QueryMatching() **********/
416 bool wxTable::QueryMatching(bool forUpdate
, bool distinct
)
419 return(query(DB_SELECT_MATCHING
, forUpdate
, distinct
));
421 } // wxTable::QueryMatching()
423 /********** wxTable::QueryOnKeyFields() **********/
424 bool wxTable::QueryOnKeyFields(bool forUpdate
, bool distinct
)
427 return(query(DB_SELECT_KEYFIELDS
, forUpdate
, distinct
));
429 } // wxTable::QueryOnKeyFields()
431 /********** wxTable::query() **********/
432 bool wxTable::query(int queryType
, bool forUpdate
, bool distinct
, char *pSqlStmt
)
434 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
436 // Set the selectForUpdate member variable
438 // The user may wish to select for update, but the DBMS may not be capable
439 selectForUpdate
= CanSelectForUpdate();
441 selectForUpdate
= FALSE
;
443 // Set the SQL SELECT string
444 if (queryType
!= DB_SELECT_STATEMENT
) // A select statement was not passed in,
445 { // so generate a select statement.
446 GetSelectStmt(sqlStmt
, queryType
, distinct
);
447 pDb
->WriteSqlLog(sqlStmt
);
450 // Make sure the cursor is closed first
451 if (! CloseCursor(hstmt
))
454 // Execute the SQL SELECT statement
457 retcode
= SQLExecDirect(hstmt
, (UCHAR FAR
*) (queryType
== DB_SELECT_STATEMENT
? pSqlStmt
: sqlStmt
), SQL_NTS
);
458 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
459 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
));
461 // Completed successfully
464 } // wxTable::query()
466 /********** wxTable::GetSelectStmt() **********/
467 void wxTable::GetSelectStmt(char *pSqlStmt
, int typeOfSelect
, bool distinct
)
469 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
];
473 // Build a select statement to query the database
474 strcpy(pSqlStmt
, "SELECT ");
476 // SELECT DISTINCT values only?
478 strcat(pSqlStmt
, "DISTINCT ");
480 // Was a FROM clause specified to join tables to the base table?
481 // Available for ::Query() only!!!
482 bool appendFromClause
= FALSE
;
483 if (typeOfSelect
== DB_SELECT_WHERE
&& from
&& strlen(from
))
484 appendFromClause
= TRUE
;
486 // Add the column list
488 for (i
= 0; i
< noCols
; i
++)
490 // If joining tables, the base table column names must be qualified to avoid ambiguity
491 if (appendFromClause
)
493 strcat(pSqlStmt
, queryTableName
);
494 strcat(pSqlStmt
, ".");
496 strcat(pSqlStmt
, colDefs
[i
].ColName
);
498 strcat(pSqlStmt
, ",");
501 // If the datasource supports ROWID, get this column as well. Exception: Don't retrieve
502 // the ROWID if querying distinct records. The rowid will always be unique.
503 if (!distinct
&& CanUpdByROWID())
505 // If joining tables, the base table column names must be qualified to avoid ambiguity
506 if (appendFromClause
)
508 strcat(pSqlStmt
, ",");
509 strcat(pSqlStmt
, queryTableName
);
510 strcat(pSqlStmt
, ".ROWID");
513 strcat(pSqlStmt
, ",ROWID");
516 // Append the FROM tablename portion
517 strcat(pSqlStmt
, " FROM ");
518 strcat(pSqlStmt
, queryTableName
);
520 // Sybase uses the HOLDLOCK keyword to lock a record during query.
521 // The HOLDLOCK keyword follows the table name in the from clause.
522 // Each table in the from clause must specify HOLDLOCK or
523 // NOHOLDLOCK (the default). Note: The "FOR UPDATE" clause
524 // is parsed but ignored in SYBASE Transact-SQL.
525 if (selectForUpdate
&& (pDb
->Dbms() == dbmsSYBASE_ASA
|| pDb
->Dbms() == dbmsSYBASE_ASE
))
526 strcat(pSqlStmt
, " HOLDLOCK");
528 if (appendFromClause
)
529 strcat(pSqlStmt
, from
);
531 // Append the WHERE clause. Either append the where clause for the class
532 // or build a where clause. The typeOfSelect determines this.
535 case DB_SELECT_WHERE
:
536 if (where
&& strlen(where
)) // May not want a where clause!!!
538 strcat(pSqlStmt
, " WHERE ");
539 strcat(pSqlStmt
, where
);
542 case DB_SELECT_KEYFIELDS
:
543 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
);
544 if (strlen(whereClause
))
546 strcat(pSqlStmt
, " WHERE ");
547 strcat(pSqlStmt
, whereClause
);
550 case DB_SELECT_MATCHING
:
551 GetWhereClause(whereClause
, DB_WHERE_MATCHING
);
552 if (strlen(whereClause
))
554 strcat(pSqlStmt
, " WHERE ");
555 strcat(pSqlStmt
, whereClause
);
560 // Append the ORDER BY clause
561 if (orderBy
&& strlen(orderBy
))
563 strcat(pSqlStmt
, " ORDER BY ");
564 strcat(pSqlStmt
, orderBy
);
567 // SELECT FOR UPDATE if told to do so and the datasource is capable. Sybase
568 // parses the FOR UPDATE clause but ignores it. See the comment above on the
569 // HOLDLOCK for Sybase.
570 if (selectForUpdate
&& CanSelectForUpdate())
571 strcat(pSqlStmt
, " FOR UPDATE");
573 } // wxTable::GetSelectStmt()
575 /********** wxTable::getRec() **********/
576 bool wxTable::getRec(UWORD fetchType
)
580 #if !wxODBC_FWD_ONLY_CURSORS
582 // Fetch the NEXT, PREV, FIRST or LAST record, depending on fetchType
586 // if ((retcode = SQLExtendedFetch(hstmt, fetchType, 0, &cRowsFetched, &rowStatus)) != SQL_SUCCESS)
587 retcode
= SQLExtendedFetch(hstmt
, fetchType
, 0, &cRowsFetched
, &rowStatus
);
588 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
589 if (retcode
== SQL_NO_DATA_FOUND
)
592 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
));
595 // Fetch the next record from the record set
596 retcode
= SQLFetch(hstmt
);
597 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
599 if (retcode
== SQL_NO_DATA_FOUND
)
602 return(pDb
->DispAllErrors(henv
, hdbc
, hstmt
));
606 // Completed successfully
609 } // wxTable::getRec()
611 /********** wxTable::GetRowNum() **********/
612 UWORD
wxTable::GetRowNum(void)
616 if (SQLGetStmtOption(hstmt
, SQL_ROW_NUMBER
, (UCHAR
*) &rowNum
) != SQL_SUCCESS
)
618 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
622 // Completed successfully
623 return((UWORD
) rowNum
);
625 } // wxTable::GetRowNum()
627 /********** wxTable::bindInsertParams() **********/
628 bool wxTable::bindInsertParams(void)
635 UDWORD precision
= 0;
638 // Bind each column (that can be inserted) of the table to a parameter marker
640 for (i
= 0; i
< noCols
; i
++)
642 if (! colDefs
[i
].InsertAllowed
)
644 switch(colDefs
[i
].DbDataType
)
646 case DB_DATA_TYPE_VARCHAR
:
647 fSqlType
= pDb
->typeInfVarchar
.FsqlType
;
648 precision
= colDefs
[i
].SzDataObj
;
650 colDefs
[i
].CbValue
= SQL_NTS
;
652 case DB_DATA_TYPE_INTEGER
:
653 fSqlType
= pDb
->typeInfInteger
.FsqlType
;
654 precision
= pDb
->typeInfInteger
.Precision
;
656 colDefs
[i
].CbValue
= 0;
658 case DB_DATA_TYPE_FLOAT
:
659 fSqlType
= pDb
->typeInfFloat
.FsqlType
;
660 precision
= pDb
->typeInfFloat
.Precision
;
661 scale
= pDb
->typeInfFloat
.MaximumScale
;
662 // SQL Sybase Anywhere v5.5 returned a negative number for the
663 // MaxScale. This caused ODBC to kick out an error on ibscale.
664 // I check for this here and set the scale = precision.
666 // scale = (short) precision;
667 colDefs
[i
].CbValue
= 0;
669 case DB_DATA_TYPE_DATE
:
670 fSqlType
= pDb
->typeInfDate
.FsqlType
;
671 precision
= pDb
->typeInfDate
.Precision
;
673 colDefs
[i
].CbValue
= 0;
679 colDefs
[i
].CbValue
= SQL_NULL_DATA
;
680 colDefs
[i
].Null
= FALSE
;
682 if (SQLBindParameter(hstmtInsert
, i
+1, SQL_PARAM_INPUT
, colDefs
[i
].SqlCtype
,
683 fSqlType
, precision
, scale
, (UCHAR
*) colDefs
[i
].PtrDataObj
,
684 precision
+1,&colDefs
[i
].CbValue
) != SQL_SUCCESS
)
685 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
));
688 // Completed successfully
691 } // wxTable::bindInsertParams()
693 /********** wxTable::bindUpdateParams() **********/
694 bool wxTable::bindUpdateParams(void)
701 UDWORD precision
= 0;
704 // Bind each UPDATEABLE column of the table to a parameter marker
706 for (i
= 0, colNo
= 1; i
< noCols
; i
++)
708 if (! colDefs
[i
].Updateable
)
710 switch(colDefs
[i
].DbDataType
)
712 case DB_DATA_TYPE_VARCHAR
:
713 fSqlType
= pDb
->typeInfVarchar
.FsqlType
;
714 precision
= colDefs
[i
].SzDataObj
;
716 colDefs
[i
].CbValue
= SQL_NTS
;
718 case DB_DATA_TYPE_INTEGER
:
719 fSqlType
= pDb
->typeInfInteger
.FsqlType
;
720 precision
= pDb
->typeInfInteger
.Precision
;
722 colDefs
[i
].CbValue
= 0;
724 case DB_DATA_TYPE_FLOAT
:
725 fSqlType
= pDb
->typeInfFloat
.FsqlType
;
726 precision
= pDb
->typeInfFloat
.Precision
;
727 scale
= pDb
->typeInfFloat
.MaximumScale
;
728 // SQL Sybase Anywhere v5.5 returned a negative number for the
729 // MaxScale. This caused ODBC to kick out an error on ibscale.
730 // I check for this here and set the scale = precision.
732 // scale = (short) precision;
733 colDefs
[i
].CbValue
= 0;
735 case DB_DATA_TYPE_DATE
:
736 fSqlType
= pDb
->typeInfDate
.FsqlType
;
737 precision
= pDb
->typeInfDate
.Precision
;
739 colDefs
[i
].CbValue
= 0;
742 if (SQLBindParameter(hstmtUpdate
, colNo
++, SQL_PARAM_INPUT
, colDefs
[i
].SqlCtype
,
743 fSqlType
, precision
, scale
, (UCHAR
*) colDefs
[i
].PtrDataObj
,
744 precision
+1, &colDefs
[i
].CbValue
) != SQL_SUCCESS
)
745 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
));
748 // Completed successfully
751 } // wxTable::bindUpdateParams()
753 /********** wxTable::bindCols() **********/
754 bool wxTable::bindCols(HSTMT cursor
)
758 // Bind each column of the table to a memory address for fetching data
760 for (i
= 0; i
< noCols
; i
++)
762 if (SQLBindCol(cursor
, i
+1, colDefs
[i
].SqlCtype
, (UCHAR
*) colDefs
[i
].PtrDataObj
,
763 colDefs
[i
].SzDataObj
, &cb
) != SQL_SUCCESS
)
764 return(pDb
->DispAllErrors(henv
, hdbc
, cursor
));
767 // Completed successfully
770 } // wxTable::bindCols()
772 /********** wxTable::CloseCursor() **********/
773 bool wxTable::CloseCursor(HSTMT cursor
)
775 if (SQLFreeStmt(cursor
, SQL_CLOSE
) != SQL_SUCCESS
)
776 return(pDb
->DispAllErrors(henv
, hdbc
, cursor
));
778 // Completed successfully
781 } // wxTable::CloseCursor()
783 /********** wxTable::CreateTable() **********/
784 bool wxTable::CreateTable(bool attemptDrop
)
790 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
792 #ifdef DBDEBUG_CONSOLE
793 cout
<< "Creating Table " << tableName
<< "..." << endl
;
797 if (attemptDrop
&& !DropTable())
801 #ifdef DBDEBUG_CONSOLE
802 for (i
= 0; i
< noCols
; i
++)
804 // Exclude derived columns since they are NOT part of the base table
805 if (colDefs
[i
].DerivedCol
)
807 cout
<< i
+ 1 << ": " << colDefs
[i
].ColName
<< "; ";
808 switch(colDefs
[i
].DbDataType
)
810 case DB_DATA_TYPE_VARCHAR
:
811 cout
<< pDb
->typeInfVarchar
.TypeName
<< "(" << colDefs
[i
].SzDataObj
<< ")";
813 case DB_DATA_TYPE_INTEGER
:
814 cout
<< pDb
->typeInfInteger
.TypeName
;
816 case DB_DATA_TYPE_FLOAT
:
817 cout
<< pDb
->typeInfFloat
.TypeName
;
819 case DB_DATA_TYPE_DATE
:
820 cout
<< pDb
->typeInfDate
.TypeName
;
827 // Build a CREATE TABLE string from the colDefs structure.
828 bool needComma
= FALSE
;
829 sprintf(sqlStmt
, "CREATE TABLE %s (", tableName
);
830 for (i
= 0; i
< noCols
; i
++)
832 // Exclude derived columns since they are NOT part of the base table
833 if (colDefs
[i
].DerivedCol
)
837 strcat(sqlStmt
, ",");
839 strcat(sqlStmt
, colDefs
[i
].ColName
);
840 strcat(sqlStmt
, " ");
842 switch(colDefs
[i
].DbDataType
)
844 case DB_DATA_TYPE_VARCHAR
:
845 strcat(sqlStmt
, pDb
->typeInfVarchar
.TypeName
); break;
846 case DB_DATA_TYPE_INTEGER
:
847 strcat(sqlStmt
, pDb
->typeInfInteger
.TypeName
); break;
848 case DB_DATA_TYPE_FLOAT
:
849 strcat(sqlStmt
, pDb
->typeInfFloat
.TypeName
); break;
850 case DB_DATA_TYPE_DATE
:
851 strcat(sqlStmt
, pDb
->typeInfDate
.TypeName
); break;
853 // For varchars, append the size of the string
854 if (colDefs
[i
].DbDataType
== DB_DATA_TYPE_VARCHAR
)
857 // strcat(sqlStmt, "(");
858 // strcat(sqlStmt, itoa(colDefs[i].SzDataObj, s, 10));
859 // strcat(sqlStmt, ")");
860 sprintf(s
, "(%d)", colDefs
[i
].SzDataObj
);
864 if (pDb
->Dbms() == dbmsSYBASE_ASE
|| pDb
->Dbms() == dbmsMY_SQL
)
866 if (colDefs
[i
].KeyField
)
868 strcat(sqlStmt
, " NOT NULL");
874 // If there is a primary key defined, include it in the create statement
875 for (i
= j
= 0; i
< noCols
; i
++)
877 if (colDefs
[i
].KeyField
)
883 if (j
&& pDb
->Dbms() != dbmsDBASE
) // Found a keyfield
885 if (pDb
->Dbms() != dbmsMY_SQL
)
887 strcat(sqlStmt
, ",CONSTRAINT ");
888 strcat(sqlStmt
, tableName
);
889 strcat(sqlStmt
, "_PIDX PRIMARY KEY (");
893 /* MySQL goes out on this one. We also declare the relevant key NON NULL above */
894 strcat(sqlStmt
, ", PRIMARY KEY (");
897 // List column name(s) of column(s) comprising the primary key
898 for (i
= j
= 0; i
< noCols
; i
++)
900 if (colDefs
[i
].KeyField
)
902 if (j
++) // Multi part key, comma separate names
903 strcat(sqlStmt
, ",");
904 strcat(sqlStmt
, colDefs
[i
].ColName
);
907 strcat(sqlStmt
, ")");
909 // Append the closing parentheses for the create table statement
910 strcat(sqlStmt
, ")");
912 pDb
->WriteSqlLog(sqlStmt
);
914 #ifdef DBDEBUG_CONSOLE
915 cout
<< endl
<< sqlStmt
<< endl
;
918 // Execute the CREATE TABLE statement
919 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
921 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
922 pDb
->RollbackTrans();
927 // Commit the transaction and close the cursor
928 if (! pDb
->CommitTrans())
930 if (! CloseCursor(hstmt
))
933 // Database table created successfully
936 } // wxTable::CreateTable()
938 /********** wxTable::DropTable() **********/
939 bool wxTable::DropTable()
941 // NOTE: This function returns TRUE if the Table does not exist, but
942 // only for identified databases. Code will need to be added
943 // below for any other databases when those databases are defined
944 // to handle this situation consistently
946 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
948 sprintf(sqlStmt
, "DROP TABLE %s", tableName
);
950 pDb
->WriteSqlLog(sqlStmt
);
952 #ifdef DBDEBUG_CONSOLE
953 cout
<< endl
<< sqlStmt
<< endl
;
956 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
958 // Check for "Base table not found" error and ignore
959 pDb
->GetNextError(henv
, hdbc
, hstmt
);
960 if (wxStrcmp(pDb
->sqlState
,"S0002")) // "Base table not found"
962 // Check for product specific error codes
963 if (!((pDb
->Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(pDb
->sqlState
,"42000")) || // 5.x (and lower?)
964 (pDb
->Dbms() == dbmsMY_SQL
&& !wxStrcmp(pDb
->sqlState
,"S1000")) || // untested
965 (pDb
->Dbms() == dbmsPOSTGRES
&& !wxStrcmp(pDb
->sqlState
,"08S01")))) // untested
967 pDb
->DispNextError();
968 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
969 pDb
->RollbackTrans();
976 // Commit the transaction and close the cursor
977 if (! pDb
->CommitTrans())
979 if (! CloseCursor(hstmt
))
983 } // wxTable::DropTable()
985 /********** wxTable::CreateIndex() **********/
986 bool wxTable::CreateIndex(char * idxName
, bool unique
, int noIdxCols
, CidxDef
*pIdxDefs
, bool attemptDrop
)
988 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
990 // Drop the index first
991 if (attemptDrop
&& !DropIndex(idxName
))
994 // Build a CREATE INDEX statement
995 strcpy(sqlStmt
, "CREATE ");
997 strcat(sqlStmt
, "UNIQUE ");
999 strcat(sqlStmt
, "INDEX ");
1000 strcat(sqlStmt
, idxName
);
1001 strcat(sqlStmt
, " ON ");
1002 strcat(sqlStmt
, tableName
);
1003 strcat(sqlStmt
, " (");
1005 // Append list of columns making up index
1007 for (i
= 0; i
< noIdxCols
; i
++)
1009 strcat(sqlStmt
, pIdxDefs
[i
].ColName
);
1010 /* Postgres doesn't cope with ASC */
1011 if (pDb
->Dbms() != dbmsPOSTGRES
)
1013 if (pIdxDefs
[i
].Ascending
)
1014 strcat(sqlStmt
, " ASC");
1016 strcat(sqlStmt
, " DESC");
1019 if ((i
+ 1) < noIdxCols
)
1020 strcat(sqlStmt
, ",");
1023 // Append closing parentheses
1024 strcat(sqlStmt
, ")");
1026 pDb
->WriteSqlLog(sqlStmt
);
1028 #ifdef DBDEBUG_CONSOLE
1029 cout
<< endl
<< sqlStmt
<< endl
<< endl
;
1032 // Execute the CREATE INDEX statement
1033 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
1035 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
1036 pDb
->RollbackTrans();
1041 // Commit the transaction and close the cursor
1042 if (! pDb
->CommitTrans())
1044 if (! CloseCursor(hstmt
))
1047 // Index Created Successfully
1050 } // wxTable::CreateIndex()
1052 /********** wxTable::DropIndex() **********/
1053 bool wxTable::DropIndex(char * idxName
)
1055 // NOTE: This function returns TRUE if the Index does not exist, but
1056 // only for identified databases. Code will need to be added
1057 // below for any other databases when those databases are defined
1058 // to handle this situation consistently
1060 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1062 if (pDb
->Dbms() == dbmsACCESS
)
1063 sprintf(sqlStmt
, "DROP INDEX %s ON %s",idxName
,tableName
);
1064 else if (pDb
->Dbms() == dbmsSYBASE_ASE
)
1065 sprintf(sqlStmt
, "DROP INDEX %s.%s",tableName
,idxName
);
1067 sprintf(sqlStmt
, "DROP INDEX %s",idxName
);
1069 pDb
->WriteSqlLog(sqlStmt
);
1071 #ifdef DBDEBUG_CONSOLE
1072 cout
<< endl
<< sqlStmt
<< endl
;
1075 if (SQLExecDirect(hstmt
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
1077 // Check for "Index not found" error and ignore
1078 pDb
->GetNextError(henv
, hdbc
, hstmt
);
1079 if (wxStrcmp(pDb
->sqlState
,"S0012")) // "Index not found"
1081 // Check for product specific error codes
1082 if (!((pDb
->Dbms() == dbmsSYBASE_ASA
&& !wxStrcmp(pDb
->sqlState
,"42000")) || // v5.x (and lower?)
1083 (pDb
->Dbms() == dbmsSYBASE_ASE
&& !wxStrcmp(pDb
->sqlState
,"S0002")) || // Base table not found
1084 (pDb
->Dbms() == dbmsMY_SQL
&& !wxStrcmp(pDb
->sqlState
,"42S02")) // untested
1087 pDb
->DispNextError();
1088 pDb
->DispAllErrors(henv
, hdbc
, hstmt
);
1089 pDb
->RollbackTrans();
1096 // Commit the transaction and close the cursor
1097 if (! pDb
->CommitTrans())
1099 if (! CloseCursor(hstmt
))
1103 } // wxTable::DropIndex()
1105 /********** wxTable::Insert() **********/
1106 int wxTable::Insert(void)
1114 // Insert the record by executing the already prepared insert statement
1116 retcode
=SQLExecute(hstmtInsert
);
1117 if (retcode
!= SQL_SUCCESS
&& retcode
!= SQL_SUCCESS_WITH_INFO
)
1119 // Check to see if integrity constraint was violated
1120 pDb
->GetNextError(henv
, hdbc
, hstmtInsert
);
1121 if (! wxStrcmp(pDb
->sqlState
, "23000")) // Integrity constraint violated
1122 return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL
);
1125 pDb
->DispNextError();
1126 pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
);
1131 // Record inserted into the datasource successfully
1134 } // wxTable::Insert()
1136 /********** wxTable::Update(pSqlStmt) **********/
1137 bool wxTable::Update(char *pSqlStmt
)
1143 pDb
->WriteSqlLog(pSqlStmt
);
1145 return(execUpdate(pSqlStmt
));
1147 } // wxTable::Update(pSqlStmt)
1149 /********** wxTable::Update() **********/
1150 bool wxTable::Update(void)
1156 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1158 // Build the SQL UPDATE statement
1159 GetUpdateStmt(sqlStmt
, DB_UPD_KEYFIELDS
);
1161 pDb
->WriteSqlLog(sqlStmt
);
1163 #ifdef DBDEBUG_CONSOLE
1164 cout
<< endl
<< sqlStmt
<< endl
<< endl
;
1167 // Execute the SQL UPDATE statement
1168 return(execUpdate(sqlStmt
));
1170 } // wxTable::Update()
1172 /********** wxTable::UpdateWhere() **********/
1173 bool wxTable::UpdateWhere(char *pWhereClause
)
1179 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1181 // Build the SQL UPDATE statement
1182 GetUpdateStmt(sqlStmt
, DB_UPD_WHERE
, pWhereClause
);
1184 pDb
->WriteSqlLog(sqlStmt
);
1186 #ifdef DBDEBUG_CONSOLE
1187 cout
<< endl
<< sqlStmt
<< endl
<< endl
;
1190 // Execute the SQL UPDATE statement
1191 return(execUpdate(sqlStmt
));
1193 } // wxTable::UpdateWhere()
1195 /********** wxTable::Delete() **********/
1196 bool wxTable::Delete(void)
1202 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1204 // Build the SQL DELETE statement
1205 GetDeleteStmt(sqlStmt
, DB_DEL_KEYFIELDS
);
1207 pDb
->WriteSqlLog(sqlStmt
);
1209 // Execute the SQL DELETE statement
1210 return(execDelete(sqlStmt
));
1212 } // wxTable::Delete()
1214 /********** wxTable::DeleteWhere() **********/
1215 bool wxTable::DeleteWhere(char *pWhereClause
)
1221 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1223 // Build the SQL DELETE statement
1224 GetDeleteStmt(sqlStmt
, DB_DEL_WHERE
, pWhereClause
);
1226 pDb
->WriteSqlLog(sqlStmt
);
1228 // Execute the SQL DELETE statement
1229 return(execDelete(sqlStmt
));
1231 } // wxTable::DeleteWhere()
1233 /********** wxTable::DeleteMatching() **********/
1234 bool wxTable::DeleteMatching(void)
1240 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1242 // Build the SQL DELETE statement
1243 GetDeleteStmt(sqlStmt
, DB_DEL_MATCHING
);
1245 pDb
->WriteSqlLog(sqlStmt
);
1247 // Execute the SQL DELETE statement
1248 return(execDelete(sqlStmt
));
1250 } // wxTable::DeleteMatching()
1252 /********** wxTable::execDelete() **********/
1253 bool wxTable::execDelete(char *pSqlStmt
)
1255 // Execute the DELETE statement
1256 if (SQLExecDirect(hstmtDelete
, (UCHAR FAR
*) pSqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
1257 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
));
1259 // Record deleted successfully
1262 } // wxTable::execDelete()
1264 /********** wxTable::execUpdate() **********/
1265 bool wxTable::execUpdate(char *pSqlStmt
)
1267 // Execute the UPDATE statement
1268 if (SQLExecDirect(hstmtUpdate
, (UCHAR FAR
*) pSqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
1269 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
));
1271 // Record deleted successfully
1274 } // wxTable::execUpdate()
1276 /********** wxTable::GetUpdateStmt() **********/
1277 void wxTable::GetUpdateStmt(char *pSqlStmt
, int typeOfUpd
, char *pWhereClause
)
1283 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
];
1284 bool firstColumn
= TRUE
;
1287 sprintf(pSqlStmt
, "UPDATE %s SET ", tableName
);
1289 // Append a list of columns to be updated
1291 for (i
= 0; i
< noCols
; i
++)
1293 // Only append Updateable columns
1294 if (colDefs
[i
].Updateable
)
1297 strcat(pSqlStmt
, ",");
1299 firstColumn
= FALSE
;
1300 strcat(pSqlStmt
, colDefs
[i
].ColName
);
1301 strcat(pSqlStmt
, " = ?");
1305 // Append the WHERE clause to the SQL UPDATE statement
1306 strcat(pSqlStmt
, " WHERE ");
1309 case DB_UPD_KEYFIELDS
:
1310 // If the datasource supports the ROWID column, build
1311 // the where on ROWID for efficiency purposes.
1312 // e.g. UPDATE PARTS SET Col1 = ?, Col2 = ? WHERE ROWID = '111.222.333'
1313 if (CanUpdByROWID())
1316 char rowid
[ROWID_LEN
];
1318 // Get the ROWID value. If not successful retreiving the ROWID,
1319 // simply fall down through the code and build the WHERE clause
1320 // based on the key fields.
1321 if (SQLGetData(hstmt
, noCols
+1, SQL_C_CHAR
, (UCHAR
*) rowid
, ROWID_LEN
, &cb
) == SQL_SUCCESS
)
1323 strcat(pSqlStmt
, "ROWID = '");
1324 strcat(pSqlStmt
, rowid
);
1325 strcat(pSqlStmt
, "'");
1329 // Unable to delete by ROWID, so build a WHERE
1330 // clause based on the keyfields.
1331 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
);
1332 strcat(pSqlStmt
, whereClause
);
1335 strcat(pSqlStmt
, pWhereClause
);
1339 } // GetUpdateStmt()
1341 /********** wxTable::GetDeleteStmt() **********/
1342 void wxTable::GetDeleteStmt(char *pSqlStmt
, int typeOfDel
, char *pWhereClause
)
1348 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
];
1352 // Handle the case of DeleteWhere() and the where clause is blank. It should
1353 // delete all records from the database in this case.
1354 if (typeOfDel
== DB_DEL_WHERE
&& (pWhereClause
== 0 || strlen(pWhereClause
) == 0))
1356 sprintf(pSqlStmt
, "DELETE FROM %s", tableName
);
1360 sprintf(pSqlStmt
, "DELETE FROM %s WHERE ", tableName
);
1362 // Append the WHERE clause to the SQL DELETE statement
1365 case DB_DEL_KEYFIELDS
:
1366 // If the datasource supports the ROWID column, build
1367 // the where on ROWID for efficiency purposes.
1368 // e.g. DELETE FROM PARTS WHERE ROWID = '111.222.333'
1369 if (CanUpdByROWID())
1372 char rowid
[ROWID_LEN
];
1374 // Get the ROWID value. If not successful retreiving the ROWID,
1375 // simply fall down through the code and build the WHERE clause
1376 // based on the key fields.
1377 if (SQLGetData(hstmt
, noCols
+1, SQL_C_CHAR
, (UCHAR
*) rowid
, ROWID_LEN
, &cb
) == SQL_SUCCESS
)
1379 strcat(pSqlStmt
, "ROWID = '");
1380 strcat(pSqlStmt
, rowid
);
1381 strcat(pSqlStmt
, "'");
1385 // Unable to delete by ROWID, so build a WHERE
1386 // clause based on the keyfields.
1387 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
);
1388 strcat(pSqlStmt
, whereClause
);
1391 strcat(pSqlStmt
, pWhereClause
);
1393 case DB_DEL_MATCHING
:
1394 GetWhereClause(whereClause
, DB_WHERE_MATCHING
);
1395 strcat(pSqlStmt
, whereClause
);
1399 } // GetDeleteStmt()
1401 /********** wxTable::GetWhereClause() **********/
1403 * Note: GetWhereClause() currently ignores timestamp columns.
1404 * They are not included as part of the where clause.
1407 void wxTable::GetWhereClause(char *pWhereClause
, int typeOfWhere
, char *qualTableName
)
1409 bool moreThanOneColumn
= FALSE
;
1412 // Loop through the columns building a where clause as you go
1414 for (i
= 0; i
< noCols
; i
++)
1416 // Determine if this column should be included in the WHERE clause
1417 if ((typeOfWhere
== DB_WHERE_KEYFIELDS
&& colDefs
[i
].KeyField
) ||
1418 (typeOfWhere
== DB_WHERE_MATCHING
&& (! IsColNull(i
))))
1420 // Skip over timestamp columns
1421 if (colDefs
[i
].SqlCtype
== SQL_C_TIMESTAMP
)
1423 // If there is more than 1 column, join them with the keyword "AND"
1424 if (moreThanOneColumn
)
1425 strcat(pWhereClause
, " AND ");
1427 moreThanOneColumn
= TRUE
;
1428 // Concatenate where phrase for the column
1429 if (qualTableName
&& strlen(qualTableName
))
1431 strcat(pWhereClause
, qualTableName
);
1432 strcat(pWhereClause
, ".");
1434 strcat(pWhereClause
, colDefs
[i
].ColName
);
1435 strcat(pWhereClause
, " = ");
1436 switch(colDefs
[i
].SqlCtype
)
1439 sprintf(colValue
, "'%s'", (UCHAR FAR
*) colDefs
[i
].PtrDataObj
);
1442 sprintf(colValue
, "%hi", *((SWORD
*) colDefs
[i
].PtrDataObj
));
1445 sprintf(colValue
, "%hu", *((UWORD
*) colDefs
[i
].PtrDataObj
));
1448 sprintf(colValue
, "%li", *((SDWORD
*) colDefs
[i
].PtrDataObj
));
1451 sprintf(colValue
, "%lu", *((UDWORD
*) colDefs
[i
].PtrDataObj
));
1454 sprintf(colValue
, "%.6f", *((SFLOAT
*) colDefs
[i
].PtrDataObj
));
1457 sprintf(colValue
, "%.6f", *((SDOUBLE
*) colDefs
[i
].PtrDataObj
));
1460 strcat(pWhereClause
, colValue
);
1464 } // wxTable::GetWhereClause()
1466 /********** wxTable::IsColNull() **********/
1467 bool wxTable::IsColNull(int colNo
)
1469 switch(colDefs
[colNo
].SqlCtype
)
1472 return(((UCHAR FAR
*) colDefs
[colNo
].PtrDataObj
)[0] == 0);
1474 return(( *((SWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1476 return(( *((UWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1478 return(( *((SDWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1480 return(( *((UDWORD
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1482 return(( *((SFLOAT
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1484 return((*((SDOUBLE
*) colDefs
[colNo
].PtrDataObj
)) == 0);
1485 case SQL_C_TIMESTAMP
:
1486 TIMESTAMP_STRUCT
*pDt
;
1487 pDt
= (TIMESTAMP_STRUCT
*) colDefs
[colNo
].PtrDataObj
;
1488 if (pDt
->year
== 0 && pDt
->month
== 0 && pDt
->day
== 0)
1496 } // wxTable::IsColNull()
1498 /********** wxTable::CanSelectForUpdate() **********/
1499 bool wxTable::CanSelectForUpdate(void)
1501 if (pDb
->Dbms() == dbmsMY_SQL
)
1504 if (pDb
->dbInf
.posStmts
& SQL_PS_SELECT_FOR_UPDATE
)
1509 } // wxTable::CanSelectForUpdate()
1511 /********** wxTable::CanUpdByROWID() **********/
1512 bool wxTable::CanUpdByROWID(void)
1515 //NOTE: Returning FALSE for now until this can be debugged,
1516 // as the ROWID is not getting updated correctly
1519 if (pDb
->Dbms() == dbmsORACLE
)
1524 } // wxTable::CanUpdByROWID()
1526 /********** wxTable::IsCursorClosedOnCommit() **********/
1527 bool wxTable::IsCursorClosedOnCommit(void)
1529 if (pDb
->dbInf
.cursorCommitBehavior
== SQL_CB_PRESERVE
)
1534 } // wxTable::IsCursorClosedOnCommit()
1536 /********** wxTable::ClearMemberVars() **********/
1537 void wxTable::ClearMemberVars(void)
1539 // Loop through the columns setting each member variable to zero
1541 for (i
= 0; i
< noCols
; i
++)
1543 switch(colDefs
[i
].SqlCtype
)
1546 ((UCHAR FAR
*) colDefs
[i
].PtrDataObj
)[0] = 0;
1549 *((SWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1552 *((UWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1555 *((SDWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1558 *((UDWORD
*) colDefs
[i
].PtrDataObj
) = 0;
1561 *((SFLOAT
*) colDefs
[i
].PtrDataObj
) = 0.0f
;
1564 *((SDOUBLE
*) colDefs
[i
].PtrDataObj
) = 0.0f
;
1566 case SQL_C_TIMESTAMP
:
1567 TIMESTAMP_STRUCT
*pDt
;
1568 pDt
= (TIMESTAMP_STRUCT
*) colDefs
[i
].PtrDataObj
;
1580 } // wxTable::ClearMemberVars()
1582 /********** wxTable::SetQueryTimeout() **********/
1583 bool wxTable::SetQueryTimeout(UDWORD nSeconds
)
1585 if (SQLSetStmtOption(hstmtInsert
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1586 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInsert
));
1587 if (SQLSetStmtOption(hstmtUpdate
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1588 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtUpdate
));
1589 if (SQLSetStmtOption(hstmtDelete
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1590 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtDelete
));
1591 if (SQLSetStmtOption(hstmtInternal
, SQL_QUERY_TIMEOUT
, nSeconds
) != SQL_SUCCESS
)
1592 return(pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
));
1594 // Completed Successfully
1597 } // wxTable::SetQueryTimeout()
1599 /********** wxTable::SetColDefs() **********/
1600 void wxTable::SetColDefs (int index
, char *fieldName
, int dataType
, void *pData
,
1601 int cType
, int size
, bool keyField
, bool upd
,
1602 bool insAllow
, bool derivedCol
)
1604 if (!colDefs
) // May happen if the database connection fails
1607 if (strlen(fieldName
) > (unsigned int) DB_MAX_COLUMN_NAME_LEN
)
1609 strncpy (colDefs
[index
].ColName
, fieldName
, DB_MAX_COLUMN_NAME_LEN
);
1610 colDefs
[index
].ColName
[DB_MAX_COLUMN_NAME_LEN
] = 0;
1613 strcpy(colDefs
[index
].ColName
, fieldName
);
1615 colDefs
[index
].DbDataType
= dataType
;
1616 colDefs
[index
].PtrDataObj
= pData
;
1617 colDefs
[index
].SqlCtype
= cType
;
1618 colDefs
[index
].SzDataObj
= size
;
1619 colDefs
[index
].KeyField
= keyField
;
1620 colDefs
[index
].DerivedCol
= derivedCol
;
1621 // Derived columns by definition would NOT be "Insertable" or "Updateable"
1624 colDefs
[index
].Updateable
= FALSE
;
1625 colDefs
[index
].InsertAllowed
= FALSE
;
1629 colDefs
[index
].Updateable
= upd
;
1630 colDefs
[index
].InsertAllowed
= insAllow
;
1633 colDefs
[index
].Null
= FALSE
;
1635 } // wxTable::SetColDefs()
1637 /********** wxTable::SetCursor() **********/
1638 void wxTable::SetCursor(HSTMT
*hstmtActivate
)
1640 if (hstmtActivate
== DEFAULT_CURSOR
)
1641 hstmt
= *hstmtDefault
;
1643 hstmt
= *hstmtActivate
;
1645 } // wxTable::SetCursor()
1647 /********** wxTable::Count() **********/
1648 ULONG
wxTable::Count(void)
1651 char sqlStmt
[DB_MAX_STATEMENT_LEN
];
1654 // Build a "SELECT COUNT(*) FROM queryTableName [WHERE whereClause]" SQL Statement
1655 strcpy(sqlStmt
, "SELECT COUNT(*) FROM ");
1656 strcat(sqlStmt
, queryTableName
);
1658 if (from
&& strlen(from
))
1659 strcat(sqlStmt
, from
);
1661 // Add the where clause if one is provided
1662 if (where
&& strlen(where
))
1664 strcat(sqlStmt
, " WHERE ");
1665 strcat(sqlStmt
, where
);
1668 pDb
->WriteSqlLog(sqlStmt
);
1670 // Initialize the Count cursor if it's not already initialized
1673 hstmtCount
= NewCursor(FALSE
,FALSE
);
1679 // Execute the SQL statement
1680 if (SQLExecDirect(*hstmtCount
, (UCHAR FAR
*) sqlStmt
, SQL_NTS
) != SQL_SUCCESS
)
1682 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1687 if (SQLFetch(*hstmtCount
) != SQL_SUCCESS
)
1689 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1693 // Obtain the result
1694 if (SQLGetData(*hstmtCount
, 1, SQL_C_ULONG
, &l
, sizeof(l
), &cb
) != SQL_SUCCESS
)
1696 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1701 if (SQLFreeStmt(*hstmtCount
, SQL_CLOSE
) != SQL_SUCCESS
)
1702 pDb
->DispAllErrors(henv
, hdbc
, *hstmtCount
);
1704 // Return the record count
1707 } // wxTable::Count()
1709 /********** wxTable::Refresh() **********/
1710 bool wxTable::Refresh(void)
1714 // Switch to the internal cursor so any active cursors are not corrupted
1715 HSTMT currCursor
= GetCursor();
1716 hstmt
= hstmtInternal
;
1718 // Save the where and order by clauses
1719 char *saveWhere
= where
;
1720 char *saveOrderBy
= orderBy
;
1722 // Build a where clause to refetch the record with. Try and use the
1723 // ROWID if it's available, ow use the key fields.
1724 char whereClause
[DB_MAX_WHERE_CLAUSE_LEN
+1];
1725 strcpy(whereClause
, "");
1726 if (CanUpdByROWID())
1729 char rowid
[ROWID_LEN
+1];
1731 // Get the ROWID value. If not successful retreiving the ROWID,
1732 // simply fall down through the code and build the WHERE clause
1733 // based on the key fields.
1734 if (SQLGetData(hstmt
, noCols
+1, SQL_C_CHAR
, (UCHAR
*) rowid
, ROWID_LEN
, &cb
) == SQL_SUCCESS
)
1736 strcat(whereClause
, queryTableName
);
1737 strcat(whereClause
, ".ROWID = '");
1738 strcat(whereClause
, rowid
);
1739 strcat(whereClause
, "'");
1743 // If unable to use the ROWID, build a where clause from the keyfields
1744 if (strlen(whereClause
) == 0)
1745 GetWhereClause(whereClause
, DB_WHERE_KEYFIELDS
, queryTableName
);
1747 // Requery the record
1748 where
= whereClause
;
1753 if (result
&& !GetNext())
1756 // Switch back to original cursor
1757 SetCursor(&currCursor
);
1759 // Free the internal cursor
1760 if (SQLFreeStmt(hstmtInternal
, SQL_CLOSE
) != SQL_SUCCESS
)
1761 pDb
->DispAllErrors(henv
, hdbc
, hstmtInternal
);
1763 // Restore the original where and order by clauses
1765 orderBy
= saveOrderBy
;
1769 } // wxTable::Refresh()
1771 /********** wxTable::SetNull(UINT colNo) **********/
1772 bool wxTable::SetNull(int colNo
)
1775 return(colDefs
[colNo
].Null
= TRUE
);
1779 } // wxTable::SetNull(UINT colNo)
1781 /********** wxTable::SetNull(char *colName) **********/
1782 bool wxTable::SetNull(char *colName
)
1785 for (i
= 0; i
< noCols
; i
++)
1787 if (!wxStricmp(colName
, colDefs
[i
].ColName
))
1792 return(colDefs
[i
].Null
= TRUE
);
1796 } // wxTable::SetNull(char *colName)
1798 /********** wxTable::NewCursor() **********/
1799 HSTMT
*wxTable::NewCursor(bool setCursor
, bool bindColumns
)
1801 HSTMT
*newHSTMT
= new HSTMT
;
1806 if (SQLAllocStmt(hdbc
, newHSTMT
) != SQL_SUCCESS
)
1808 pDb
->DispAllErrors(henv
, hdbc
);
1813 if (SQLSetStmtOption(*newHSTMT
, SQL_CURSOR_TYPE
, cursorType
) != SQL_SUCCESS
)
1815 pDb
->DispAllErrors(henv
, hdbc
, *newHSTMT
);
1822 if(!bindCols(*newHSTMT
))
1830 SetCursor(newHSTMT
);
1834 } // wxTable::NewCursor()
1836 /********** wxTable::DeleteCursor() **********/
1837 bool wxTable::DeleteCursor(HSTMT
*hstmtDel
)
1841 if (!hstmtDel
) // Cursor already deleted
1844 if (SQLFreeStmt(*hstmtDel
, SQL_DROP
) != SQL_SUCCESS
)
1846 pDb
->DispAllErrors(henv
, hdbc
);
1854 } // wxTable::DeleteCursor()
1856 #endif // wxUSE_ODBC