--- /dev/null
+///////////////////////////////////////////////////////////////////////////////
+// Name: db.h
+// Purpose: Header file wxDB class. The wxDB class represents a connection
+// to an ODBC data source. The wxDB class allows operations on the data
+// source such as opening and closing the data source.
+// Author: Doug Card
+// Modified by:
+// Created: 9.96
+// RCS-ID: $Id$
+// Copyright: (c) 1996 Remstar International, Inc.
+// Licence: wxWindows licence, plus:
+// Notice: This class library and its intellectual design are free of charge for use,
+// modification, enhancement, debugging under the following conditions:
+// 1) These classes may only be used as part of the implementation of a
+// wxWindows-based application
+// 2) All enhancements and bug fixes are to be submitted back to the wxWindows
+// user groups free of all charges for use with the wxWindows library.
+// 3) These classes may not be distributed as part of any other class library,
+// DLL, text (written or electronic), other than a complete distribution of
+// the wxWindows GUI development toolkit.
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+// SYNOPSIS START
+// SYNOPSIS STOP
+*/
+
+#ifndef DB_DOT_H
+#define DB_DOT_H
+
+#ifdef __GNUG__
+#pragma interface "db.h"
+#endif
+
+#if defined(wx_msw) || defined(WIN32)
+#include <windows.h>
+#endif
+
+#define ODBCVER 0x0250
+#include <sql.h>
+#include <sqlext.h>
+
+enum enumDummy {enumDum1};
+
+#define SQL_C_BOOLEAN (sizeof(int) == 2 ? SQL_C_USHORT : SQL_C_ULONG)
+#define SQL_C_ENUM (sizeof(enumDummy) == 2 ? SQL_C_USHORT : SQL_C_ULONG) //glt 2-21-97
+
+/*
+#ifndef Bool
+#define Bool int
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+*/
+
+// Database Globals
+const DB_TYPE_NAME_LEN = 40;
+const DB_MAX_STATEMENT_LEN = 2048;
+const DB_MAX_WHERE_CLAUSE_LEN = 1024;
+const DB_MAX_ERROR_MSG_LEN = 512;
+const DB_MAX_ERROR_HISTORY = 5;
+const DB_MAX_TABLE_NAME_LEN = 128;
+const DB_MAX_COLUMN_NAME_LEN = 128;
+
+const DB_DATA_TYPE_VARCHAR = 1;
+const DB_DATA_TYPE_INTEGER = 2;
+const DB_DATA_TYPE_FLOAT = 3;
+const DB_DATA_TYPE_DATE = 4;
+
+const DB_SELECT_KEYFIELDS = 1;
+const DB_SELECT_WHERE = 2;
+const DB_SELECT_MATCHING = 3;
+const DB_SELECT_STATEMENT = 4;
+
+const DB_UPD_KEYFIELDS = 1;
+const DB_UPD_WHERE = 2;
+
+const DB_DEL_KEYFIELDS = 1;
+const DB_DEL_WHERE = 2;
+const DB_DEL_MATCHING = 3;
+
+const DB_WHERE_KEYFIELDS = 1;
+const DB_WHERE_MATCHING = 2;
+
+const DB_CURSOR0 = 0;
+const DB_CURSOR1 = 1;
+const DB_CURSOR2 = 2;
+//const DB_CURSOR3 = 3;
+//const DB_CURSOR4 = 4;
+//const DB_CURSOR5 = 5;
+
+const DB_GRANT_SELECT = 1;
+const DB_GRANT_INSERT = 2;
+const DB_GRANT_UPDATE = 4;
+const DB_GRANT_DELETE = 8;
+const DB_GRANT_ALL = DB_GRANT_SELECT | DB_GRANT_INSERT | DB_GRANT_UPDATE | DB_GRANT_DELETE;
+
+// ODBC Error codes (derived from ODBC SqlState codes)
+enum ODBC_ERRORS
+{
+ DB_FAILURE = 0,
+ DB_SUCCESS = 1,
+ DB_ERR_NOT_IN_USE,
+ DB_ERR_GENERAL_WARNING, // SqlState = '01000'
+ DB_ERR_DISCONNECT_ERROR, // SqlState = '01002'
+ DB_ERR_DATA_TRUNCATED, // SqlState = '01004'
+ DB_ERR_PRIV_NOT_REVOKED, // SqlState = '01006'
+ DB_ERR_INVALID_CONN_STR_ATTR, // SqlState = '01S00'
+ DB_ERR_ERROR_IN_ROW, // SqlState = '01S01'
+ DB_ERR_OPTION_VALUE_CHANGED, // SqlState = '01S02'
+ DB_ERR_NO_ROWS_UPD_OR_DEL, // SqlState = '01S03'
+ DB_ERR_MULTI_ROWS_UPD_OR_DEL, // SqlState = '01S04'
+ DB_ERR_WRONG_NO_OF_PARAMS, // SqlState = '07001'
+ DB_ERR_DATA_TYPE_ATTR_VIOL, // SqlState = '07006'
+ DB_ERR_UNABLE_TO_CONNECT, // SqlState = '08001'
+ DB_ERR_CONNECTION_IN_USE, // SqlState = '08002'
+ DB_ERR_CONNECTION_NOT_OPEN, // SqlState = '08003'
+ DB_ERR_REJECTED_CONNECTION, // SqlState = '08004'
+ DB_ERR_CONN_FAIL_IN_TRANS, // SqlState = '08007'
+ DB_ERR_COMM_LINK_FAILURE, // SqlState = '08S01'
+ DB_ERR_INSERT_VALUE_LIST_MISMATCH, // SqlState = '21S01'
+ DB_ERR_DERIVED_TABLE_MISMATCH, // SqlState = '21S02'
+ DB_ERR_STRING_RIGHT_TRUNC, // SqlState = '22001'
+ DB_ERR_NUMERIC_VALUE_OUT_OF_RNG, // SqlState = '22003'
+ DB_ERR_ERROR_IN_ASSIGNMENT, // SqlState = '22005'
+ DB_ERR_DATETIME_FLD_OVERFLOW, // SqlState = '22008'
+ DB_ERR_DIVIDE_BY_ZERO, // SqlState = '22012'
+ DB_ERR_STR_DATA_LENGTH_MISMATCH, // SqlState = '22026'
+ DB_ERR_INTEGRITY_CONSTRAINT_VIOL, // SqlState = '23000'
+ DB_ERR_INVALID_CURSOR_STATE, // SqlState = '24000'
+ DB_ERR_INVALID_TRANS_STATE, // SqlState = '25000'
+ DB_ERR_INVALID_AUTH_SPEC, // SqlState = '28000'
+ DB_ERR_INVALID_CURSOR_NAME, // SqlState = '34000'
+ DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL, // SqlState = '37000'
+ DB_ERR_DUPLICATE_CURSOR_NAME, // SqlState = '3C000'
+ DB_ERR_SERIALIZATION_FAILURE, // SqlState = '40001'
+ DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2, // SqlState = '42000'
+ DB_ERR_OPERATION_ABORTED, // SqlState = '70100'
+ DB_ERR_UNSUPPORTED_FUNCTION, // SqlState = 'IM001'
+ DB_ERR_NO_DATA_SOURCE, // SqlState = 'IM002'
+ DB_ERR_DRIVER_LOAD_ERROR, // SqlState = 'IM003'
+ DB_ERR_SQLALLOCENV_FAILED, // SqlState = 'IM004'
+ DB_ERR_SQLALLOCCONNECT_FAILED, // SqlState = 'IM005'
+ DB_ERR_SQLSETCONNECTOPTION_FAILED, // SqlState = 'IM006'
+ DB_ERR_NO_DATA_SOURCE_DLG_PROHIB, // SqlState = 'IM007'
+ DB_ERR_DIALOG_FAILED, // SqlState = 'IM008'
+ DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL, // SqlState = 'IM009'
+ DB_ERR_DATA_SOURCE_NAME_TOO_LONG, // SqlState = 'IM010'
+ DB_ERR_DRIVER_NAME_TOO_LONG, // SqlState = 'IM011'
+ DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR, // SqlState = 'IM012'
+ DB_ERR_TRACE_FILE_ERROR, // SqlState = 'IM013'
+ DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS, // SqlState = 'S0001'
+ DB_ERR_TABLE_NOT_FOUND, // SqlState = 'S0002'
+ DB_ERR_INDEX_ALREADY_EXISTS, // SqlState = 'S0011'
+ DB_ERR_INDEX_NOT_FOUND, // SqlState = 'S0012'
+ DB_ERR_COLUMN_ALREADY_EXISTS, // SqlState = 'S0021'
+ DB_ERR_COLUMN_NOT_FOUND, // SqlState = 'S0022'
+ DB_ERR_NO_DEFAULT_FOR_COLUMN, // SqlState = 'S0023'
+ DB_ERR_GENERAL_ERROR, // SqlState = 'S1000'
+ DB_ERR_MEMORY_ALLOCATION_FAILURE, // SqlState = 'S1001'
+ DB_ERR_INVALID_COLUMN_NUMBER, // SqlState = 'S1002'
+ DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE, // SqlState = 'S1003'
+ DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE, // SqlState = 'S1004'
+ DB_ERR_OPERATION_CANCELLED, // SqlState = 'S1008'
+ DB_ERR_INVALID_ARGUMENT_VALUE, // SqlState = 'S1009'
+ DB_ERR_FUNCTION_SEQUENCE_ERROR, // SqlState = 'S1010'
+ DB_ERR_OPERATION_INVALID_AT_THIS_TIME, // SqlState = 'S1011'
+ DB_ERR_INVALID_TRANS_OPERATION_CODE, // SqlState = 'S1012'
+ DB_ERR_NO_CURSOR_NAME_AVAIL, // SqlState = 'S1015'
+ DB_ERR_INVALID_STR_OR_BUF_LEN, // SqlState = 'S1090'
+ DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE, // SqlState = 'S1091'
+ DB_ERR_OPTION_TYPE_OUT_OF_RANGE, // SqlState = 'S1092'
+ DB_ERR_INVALID_PARAM_NO, // SqlState = 'S1093'
+ DB_ERR_INVALID_SCALE_VALUE, // SqlState = 'S1094'
+ DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE, // SqlState = 'S1095'
+ DB_ERR_INF_TYPE_OUT_OF_RANGE, // SqlState = 'S1096'
+ DB_ERR_COLUMN_TYPE_OUT_OF_RANGE, // SqlState = 'S1097'
+ DB_ERR_SCOPE_TYPE_OUT_OF_RANGE, // SqlState = 'S1098'
+ DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE, // SqlState = 'S1099'
+ DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE, // SqlState = 'S1100'
+ DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE, // SqlState = 'S1101'
+ DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE, // SqlState = 'S1103'
+ DB_ERR_INVALID_PRECISION_VALUE, // SqlState = 'S1104'
+ DB_ERR_INVALID_PARAM_TYPE, // SqlState = 'S1105'
+ DB_ERR_FETCH_TYPE_OUT_OF_RANGE, // SqlState = 'S1106'
+ DB_ERR_ROW_VALUE_OUT_OF_RANGE, // SqlState = 'S1107'
+ DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE, // SqlState = 'S1108'
+ DB_ERR_INVALID_CURSOR_POSITION, // SqlState = 'S1109'
+ DB_ERR_INVALID_DRIVER_COMPLETION, // SqlState = 'S1110'
+ DB_ERR_INVALID_BOOKMARK_VALUE, // SqlState = 'S1111'
+ DB_ERR_DRIVER_NOT_CAPABLE, // SqlState = 'S1C00'
+ DB_ERR_TIMEOUT_EXPIRED // SqlState = 'S1T00'
+};
+
+struct DbStuff
+{
+ HENV Henv;
+ char Dsn[SQL_MAX_DSN_LENGTH+1]; // Data Source Name
+ char Uid[20]; // User ID
+ char AuthStr[20]; // Authorization string (password)
+};
+
+typedef struct
+{
+ char TypeName[DB_TYPE_NAME_LEN];
+ int FsqlType;
+ long Precision;
+ short CaseSensitive;
+// short MinimumScale;
+ short MaximumScale;
+} SqlTypeInfo;
+
+class CcolInf
+{
+public:
+ char tableName[DB_MAX_TABLE_NAME_LEN+1];
+ char colName[DB_MAX_COLUMN_NAME_LEN+1];
+ int sqlDataType;
+};
+
+class wxDB
+{
+private:
+
+ // Private data
+ bool dbIsOpen;
+ char *dsn; // Data source name
+ char *uid; // User ID
+ char *authStr; // Authorization string (password)
+
+ // Private member functions
+ bool getDbInfo(void);
+ bool getDataTypeInfo(SWORD fSqlType, SqlTypeInfo &structSQLTypeInfo);
+ bool setConnectionOptions(void);
+ void logError(char *errMsg, char *SQLState);
+
+public:
+
+ // The following structure contains database information gathered from the
+ // datasource when the datasource is first opened.
+ struct
+ {
+ char dbmsName[40]; // Name of the dbms product
+ char dbmsVer[20]; // Version # of the dbms product
+ char driverName[40]; // Driver name
+ char odbcVer[20]; // ODBC version of the driver
+ char drvMgrOdbcVer[20]; // ODBC version of the driver manager
+ char driverVer[40]; // Driver version
+ char serverName[40]; // Server Name, typically a connect string
+ char databaseName[128]; // Database filename
+ char outerJoins[2]; // Indicates whether the data source supports outer joins
+ char procedureSupport[2]; // Indicates whether the data source supports stored procedures
+ UWORD maxConnections; // Maximum # of connections the data source supports
+ UWORD maxStmts; // Maximum # of HSTMTs per HDBC
+ UWORD apiConfLvl; // ODBC API conformance level
+ UWORD cliConfLvl; // Indicates whether the data source is SAG compliant
+ UWORD sqlConfLvl; // SQL conformance level
+ UWORD cursorCommitBehavior; // Indicates how cursors are affected by a db commit
+ UWORD cursorRollbackBehavior; // Indicates how cursors are affected by a db rollback
+ UWORD supportNotNullClause; // Indicates if data source supports NOT NULL clause
+ char supportIEF[2]; // Integrity Enhancement Facility (Referential Integrity)
+ UDWORD txnIsolation; // Default transaction isolation level supported by the driver
+ UDWORD txnIsolationOptions; // Transaction isolation level options available
+ UDWORD fetchDirections; // Fetch directions supported
+ UDWORD lockTypes; // Lock types supported in SQLSetPos
+ UDWORD posOperations; // Position operations supported in SQLSetPos
+ UDWORD posStmts; // Position statements supported
+ UDWORD scrollConcurrency; // Concurrency control options supported for scrollable cursors
+ UDWORD scrollOptions; // Scroll Options supported for scrollable cursors
+ UDWORD staticSensitivity; // Indicates if additions, deletions and updates can be detected
+ UWORD txnCapable; // Indicates if the data source supports transactions
+ UDWORD loginTimeout; // Number seconds to wait for a login request
+ } dbInf;
+
+ // ODBC handles
+ HENV henv; // ODBC Environment handle
+ HDBC hdbc; // ODBC DB Connection handle
+ HSTMT hstmt; // ODBC Statement handle
+
+ // ODBC Error Inf.
+ char sqlState[20];
+ SDWORD nativeError;
+ char errorMsg[SQL_MAX_MESSAGE_LENGTH];
+ SWORD cbErrorMsg;
+ char errorList[DB_MAX_ERROR_HISTORY][DB_MAX_ERROR_MSG_LEN];
+ int DB_STATUS;
+
+ //Error reporting mode
+ bool silent;
+
+ // Inf. about logical data types VARCHAR, INTEGER, FLOAT and DATE.
+ // This inf. is obtained from the ODBC driver by use of the
+ // SQLGetTypeInfo() function. The key piece of inf. is the
+ // type name the data source uses for each logical data type.
+ // e.g. VARCHAR; Oracle calls it VARCHAR2.
+ SqlTypeInfo typeInfVarchar, typeInfInteger, typeInfFloat, typeInfDate;
+
+ // Public member functions
+ wxDB(HENV &aHenv);
+ bool Open(char *Dsn, char *Uid, char *AuthStr); // Data Source Name, User ID, Password
+ void Close(void);
+ bool CommitTrans(void);
+ bool RollbackTrans(void);
+ bool DispAllErrors(HENV aHenv, HDBC aHdbc = SQL_NULL_HDBC, HSTMT aHstmt = SQL_NULL_HSTMT);
+ bool GetNextError(HENV aHenv, HDBC aHdbc = SQL_NULL_HDBC, HSTMT aHstmt = SQL_NULL_HSTMT);
+ void DispNextError(void);
+ bool CreateView(char *viewName, char *colList, char *pSqlStmt);
+ bool ExecSql(char *pSqlStmt);
+ bool Grant(int privileges, char *tableName, char *userList = "PUBLIC");
+ int TranslateSqlState(char *SQLState);
+ CcolInf *GetColumns(char *tableName[]);
+ char *GetDatabaseName(void) {return dbInf.dbmsName;}
+ char *GetDataSource(void) {return dsn;}
+ char *GetUsername(void) {return uid;}
+ char *GetPassword(void) {return authStr;}
+ bool IsOpen(void) {return dbIsOpen;}
+ HENV GetHENV(void) {return henv;}
+ HDBC GetHDBC(void) {return hdbc;}
+ HSTMT GetHSTMT(void) {return hstmt;}
+ bool TableExists(char *tableName); // Table name can refer to a table, view, alias or synonym
+ void LogError(char *errMsg, char *SQLState = 0) {logError(errMsg, SQLState);}
+
+}; // wxDB
+
+// This structure forms a node in a linked list. The linked list of "DbList" objects
+// keeps track of allocated database connections. This allows the application to
+// open more than one database connection through ODBC for multiple transaction support
+// or for multiple database support.
+
+struct DbList
+{
+ DbList *PtrPrev; // Pointer to previous item in the list
+ char Dsn[SQL_MAX_DSN_LENGTH+1]; // Data Source Name
+ wxDB *PtrDb; // Pointer to the wxDB object
+ bool Free; // Is item free or in use?
+ DbList *PtrNext; // Pointer to next item in the list
+};
+
+// The following routines allow a user to get new database connections, free them
+// for other code segments to use, or close all of them when the application has
+// completed.
+
+wxDB *GetDbConnection(DbStuff *pDbStuff);
+bool FreeDbConnection(wxDB *pDb);
+void CloseDbConnections(void);
+int NumberDbConnectionsInUse(void);
+
+// This routine allows you to query a driver manager
+// for a list of available datasources. Call this routine
+// the first time using SQL_FETCH_FIRST. Continue to call it
+// using SQL_FETCH_NEXT until you've exhausted the list.
+bool GetDataSource(HENV henv, char *Dsn, SWORD DsnMax, char *DsDesc, SWORD DsDescMax,
+ UWORD direction = SQL_FETCH_NEXT);
+
+#endif
+
--- /dev/null
+///////////////////////////////////////////////////////////////////////////////
+// Name: table.h
+// Purpose: Declaration of the wxTable class.
+// Author: Doug Card
+// Modified by:
+// Created: 9.96
+// RCS-ID: $Id$
+// Copyright: (c) 1996 Remstar International, Inc.
+// Licence: wxWindows licence, plus:
+// Notice: This class library and its intellectual design are free of charge for use,
+// modification, enhancement, debugging under the following conditions:
+// 1) These classes may only be used as part of the implementation of a
+// wxWindows-based application
+// 2) All enhancements and bug fixes are to be submitted back to the wxWindows
+// user groups free of all charges for use with the wxWindows library.
+// 3) These classes may not be distributed as part of any other class library,
+// DLL, text (written or electronic), other than a complete distribution of
+// the wxWindows GUI development toolkit.
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+// SYNOPSIS START
+// SYNOPSIS STOP
+*/
+
+#ifndef TABLE_DOT_H
+#define TABLE_DOT_H
+
+#ifdef __GNUG__
+#pragma interface "dbtable.h"
+#endif
+
+#include "wx/db.h"
+
+const ROWID_LEN = 24; // 18 is the max, 24 is in case it gets larger
+
+// The following class is used to define a column of a table.
+// The wxTable constructor will dynamically allocate as many of
+// these as there are columns in the table. The class derived
+// from wxTable must initialize these column definitions in it's
+// constructor. These column definitions provide inf. to the
+// wxTable class which allows it to create a table in the data
+// source, exchange data between the data source and the C++
+// object, and so on.
+
+class CcolDef
+{
+public:
+ char ColName[DB_MAX_COLUMN_NAME_LEN+1]; // Column Name glt 4/19/97 added one for the null terminator
+ int DbDataType; // Logical Data Type; e.g. DB_DATA_TYPE_INTEGER
+ int SqlCtype; // C data type; e.g. SQL_C_LONG
+ void *PtrDataObj; // Address of the data object
+ int SzDataObj; // Size, in bytes, of the data object
+ bool KeyField; // TRUE if this column is part of the PRIMARY KEY to the table; Date fields should NOT be KeyFields.
+ bool Updateable; // Specifies whether this column is updateable
+ bool InsertAllowed; // Specifies whether this column should be included in an INSERT statement
+ bool DerivedCol; // Specifies whether this column is a derived value
+ SDWORD CbValue; // Internal use only!!!
+}; // CcolDef
+
+// This structure is used when creating secondary indexes.
+class CidxDef
+{
+public:
+ char ColName[DB_MAX_COLUMN_NAME_LEN+1]; // Column Name glt 4/19/97 added one for the null terminator
+ bool Ascending;
+}; // CidxDef
+
+class wxTable
+{
+private:
+
+ // Private member variables
+ int currCursorNo;
+
+ // Private member functions
+ bool bindInsertParams(void);
+ bool bindUpdateParams(void);
+ bool bindCols(HSTMT cursor);
+ bool getRec(UWORD fetchType);
+ bool execDelete(char *pSqlStmt);
+ bool execUpdate(char *pSqlStmt);
+ bool query(int queryType, bool forUpdate, bool distinct, char *pSqlStmt = 0);
+
+public:
+
+ // Pointer to the database object this table belongs to
+ wxDB *pDb;
+
+ // ODBC Handles
+ HENV henv; // ODBC Environment handle
+ HDBC hdbc; // ODBC DB Connection handle
+ HSTMT hstmt; // ODBC Statement handle
+// HSTMT c0, c1, c2, c3, c4, c5; // Cursors 0 through 5
+ HSTMT c0, c1, c2; // Limited to Cursors 0 through 2 for now
+ HSTMT hstmtInsert; // ODBC Statement handle used specifically for inserts
+ HSTMT hstmtDelete; // ODBC Statement handle used specifically for deletes
+ HSTMT hstmtUpdate; // ODBC Statement handle used specifically for updates
+ HSTMT hstmtCount; // ODBC Statement handle used specifically for COUNT(*)
+
+ // Table Inf.
+ char tableName[DB_MAX_TABLE_NAME_LEN+1]; // Table name
+ char queryTableName[DB_MAX_TABLE_NAME_LEN+1]; // Query Table Name
+ int noCols; // # of columns in the table
+
+ // Column Definitions
+ CcolDef *colDefs; // Array of CcolDef structures
+
+ // Where and Order By clauses
+ char *where; // Standard SQL where clause, minus the word WHERE
+ char *orderBy; // Standard SQL order by clause, minus the ORDER BY
+
+ // Flags
+ bool selectForUpdate;
+
+ // Public member functions
+ wxTable(wxDB *pwxDB, const char *tblName, const int nCols, const char *qryTblName = 0);
+ ~wxTable();
+ bool Open(void);
+ bool CreateTable(void);
+ bool CreateIndex(char * idxName, bool unique, int noIdxCols, CidxDef *pIdxDefs);
+ bool CloseCursor(HSTMT cursor);
+ int Insert(void);
+ bool Update(void);
+ bool Update(char *pSqlStmt);
+ bool UpdateWhere(char *pWhereClause);
+ bool Delete(void);
+ bool DeleteWhere(char *pWhereClause);
+ bool DeleteMatching(void);
+ bool Query(bool forUpdate = FALSE, bool distinct = FALSE);
+ bool QueryBySqlStmt(char *pSqlStmt);
+ bool QueryMatching(bool forUpdate = FALSE, bool distinct = FALSE);
+ bool QueryOnKeyFields(bool forUpdate = FALSE, bool distinct = FALSE);
+ bool GetNext(void) { return(getRec(SQL_FETCH_NEXT)); }
+ bool operator++(int) { return(getRec(SQL_FETCH_NEXT)); }
+#ifndef FWD_ONLY_CURSORS
+ bool GetPrev(void) { return(getRec(SQL_FETCH_PRIOR)); }
+ bool operator--(int) { return(getRec(SQL_FETCH_PRIOR)); }
+ bool GetFirst(void) { return(getRec(SQL_FETCH_FIRST)); }
+ bool GetLast(void) { return(getRec(SQL_FETCH_LAST)); }
+#endif
+ bool IsCursorClosedOnCommit(void);
+ bool IsColNull(int colNo);
+ UWORD GetRowNum(void);
+ void GetSelectStmt(char *pSqlStmt, int typeOfSelect, bool distinct);
+ void GetDeleteStmt(char *pSqlStmt, int typeOfDel, char *pWhereClause = 0);
+ void GetUpdateStmt(char *pSqlStmt, int typeOfUpd, char *pWhereClause = 0);
+ void GetWhereClause(char *pWhereClause, int typeOfWhere);
+ bool CanSelectForUpdate(void);
+ bool CanUpdByROWID(void);
+ void ClearMemberVars(void);
+ bool SetQueryTimeout(UDWORD nSeconds);
+ void SetColDefs (int index, char *fieldName, int dataType, void *pData, int cType,
+ int size, bool keyField = FALSE, bool upd = TRUE,
+ bool insAllow = TRUE, bool derivedCol = FALSE);
+ bool SetCursor(int cursorNo = DB_CURSOR0);
+ int GetCursor(void) { return(currCursorNo); }
+ ULONG Count(void);
+ int DB_STATUS(void) { return(pDb->DB_STATUS); }
+ bool Refresh(void);
+
+}; // wxTable
+
+#endif
+
#define USE_ODBC 1
// Define 1 to use ODBC classes
-#define USE_ODBC_IN_MSW_ONLY 1
-
-#if USE_ODBC && USE_ODBC_IN_MSW_ONLY
-#undef USE_ODBC
-#define USE_ODBC 0
-#endif
-
#define USE_IOSTREAMH 1
// VC++ 4.2 and above allows <iostream> and <iostream.h>
// but you can't mix them. Set to 1 for <iostream.h>,
--- /dev/null
+///////////////////////////////////////////////////////////////////////////////
+// Name: dbtest.cpp
+// Purpose: wxWindows database demo app
+// Author: George Tasker
+// Modified by:
+// Created: 1998
+// RCS-ID: $Id$
+// Copyright: (c) 1998 Remstar International, Inc.
+// Licence: wxWindows licence
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * SYNOPSIS START
+
+ This sample program demonstrates the cross-platform ODBC database classes
+ donated by the development team at Remstar International.
+
+ The table this sample is based on is developer contact table, and shows
+ some of the simple uses of the database classes wxDB and wxTable.
+
+
+
+ * SYNOPSIS END
+ */
+
+#ifdef __GNUG__
+#pragma implementation "dbtest.h"
+#endif
+
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif //__BORLANDC__
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif //WX_PRECOMP
+
+IMPLEMENT_APP(DatabaseDemoApp)
+
+#include <stdio.h> // Included strictly for reading the text file with the database parameters
+
+#include <wx/db.h> // Required in the file which will get the data source connection
+#include <wx/dbtable.h> // Has the wxTable object from which all data objects will inherit their data table functionality
+
+extern DbList *PtrBegDbList; // from wx_db.cpp, used in getting back error results from db connections
+
+#include "dbtest.h" // Header file for this demonstration program
+#include "listdb.h" // Code to support the "Lookup" button on the editor dialog
+extern char ListDB_Selection[]; // Used to return the first column value for the selected line from the listDB routines
+extern char ListDB_Selection2[]; // Used to return the second column value for the selected line from the listDB routines
+
+DatabaseDemoFrame *DemoFrame; // Pointer to the main frame
+
+
+// This statement initializes the whole application and calls OnInit
+DatabaseDemoApp DatabaseDemoApp;
+
+
+/* Pointer to the main database connection used in the program. This
+ * pointer would normally be used for doing things as database lookups
+ * for user login names and passwords, getting workstation settings, etc.
+ * ---> IMPORTANT <---
+ *
+ * For each database object created which uses this wxDB pointer
+ * connection to the database, when a CommitTrans() or RollBackTrans()
+ * will commit or rollback EVERY object which uses this wxDB pointer.
+ *
+ * To allow each table object (those derived from wxTable) to be
+ * individually committed or rolled back, you MUST use a different
+ * instance of wxDB in the constructor of the table. Doing so creates
+ * more overhead, and will use more database connections (some DBs have
+ * connection limits...), so use connections sparringly.
+ *
+ * It is recommended that one "main" database connection be created for
+ * the entire program to use for READ-ONLY database accesses, but for each
+ * table object which will do a CommitTrans() or RollbackTrans() that a
+ * new wxDB object be created and used for it.
+ */
+wxDB *READONLY_DB;
+
+
+/*
+ * This function will return the exact string(s) from the database engine
+ * indicating all error conditions which have just occured during the
+ * last call to the database engine.
+ *
+ * This demo uses the returned string by displaying it in a wxMessageBox. The
+ * formatting therefore is not the greatest, but this is just a demo, not a
+ * finished product. :-) gt
+ *
+ * NOTE: The value returned by this function is for temporary use only and
+ * should be copied for long term use
+ */
+char *GetExtendedDBErrorMsg(char *ErrFile, int ErrLine)
+{
+ static wxString msg;
+
+ wxString tStr;
+
+ if (ErrFile || ErrLine)
+ {
+ msg += "\n";
+ msg.Append ('-',80);
+ msg += "\nFile: ";
+ msg += ErrFile;
+ msg += " Line: ";
+ tStr.sprintf("%d",ErrLine);
+ msg += tStr.GetData();
+ msg += "\n";
+ }
+
+ msg.Append ('-',80);
+ msg.Append ("\nODBC ERRORS\n");
+ msg.Append ('-',80);
+ msg += "\n";
+ // Scan through each database connection displaying
+ // any ODBC errors that have occured.
+ for (DbList *pDbList = PtrBegDbList; pDbList; pDbList = pDbList->PtrNext)
+ {
+ // Skip over any free connections
+ if (pDbList->Free)
+ continue;
+ // Display errors for this connection
+ for (int i = 0; i < DB_MAX_ERROR_HISTORY; i++)
+ {
+ if (pDbList->PtrDb->errorList[i])
+ {
+ msg.Append(pDbList->PtrDb->errorList[i]);
+ if (strcmp(pDbList->PtrDb->errorList[i],"") != 0)
+ msg.Append("\n");
+ }
+ }
+ }
+ msg += "\n";
+
+ return msg.GetData();
+} // GetExtendedDBErrorMsg
+
+
+
+// `Main program' equivalent, creating windows and returning main app frame
+wxFrame *DatabaseDemoApp::OnInit(void)
+{
+ // Create the main frame window
+ DemoFrame = new DatabaseDemoFrame(NULL, "wxWindows Database Demo", 50, 50, 537, 480);
+
+ // Give it an icon
+#ifdef __WXMSW__
+ DemoFrame->SetIcon(wxIcon("db_icon"));
+#endif
+#ifdef __X__
+ DemoFrame->SetIcon(wxIcon("db.xbm"));
+#endif
+
+ // Make a menubar
+ wxMenu *file_menu = new wxMenu;
+ file_menu->Append(FILE_CREATE, "&Create contact table");
+ file_menu->Append(FILE_EXIT, "E&xit");
+
+ wxMenu *edit_menu = new wxMenu;
+ edit_menu->Append(EDIT_PARAMETERS, "&Parameters...");
+
+ wxMenu *about_menu = new wxMenu;
+ about_menu->Append(ABOUT_DEMO, "&About");
+
+ wxMenuBar *menu_bar = new wxMenuBar;
+ menu_bar->Append(file_menu, "&File");
+ menu_bar->Append(edit_menu, "&Edit");
+ menu_bar->Append(about_menu, "&About");
+ DemoFrame->SetMenuBar(menu_bar);
+
+ // Initialize the ODBC Environment for Database Operations
+ if (SQLAllocEnv(&DbConnectInf.Henv) != SQL_SUCCESS)
+ {
+ wxMessageBox("A problem occured while trying to get a connection to the data source","DB CONNECTION ERROR",wxOK | wxICON_EXCLAMATION);
+ return NULL;
+ }
+
+ FILE *paramFile;
+ if ((paramFile = fopen(paramFilename, "r")) == NULL)
+ {
+ wxString tStr;
+ tStr.sprintf("Unable to open the parameter file '%s' for reading.\n\nYou must specify the data source, user name, and\npassword that will be used and save those settings.",paramFilename);
+ wxMessageBox(tStr.GetData(),"File I/O Error...",wxOK | wxICON_EXCLAMATION);
+ DemoFrame->BuildParameterDialog(NULL);
+ if ((paramFile = fopen(paramFilename, "r")) == NULL)
+ return FALSE;
+ }
+
+ char buffer[1000+1];
+ fgets(buffer, sizeof(params.ODBCSource), paramFile);
+ buffer[strlen(buffer)-1] = '\0';
+ strcpy(params.ODBCSource,buffer);
+
+ fgets(buffer, sizeof(params.UserName), paramFile);
+ buffer[strlen(buffer)-1] = '\0';
+ strcpy(params.UserName,buffer);
+
+ fgets(buffer, sizeof(params.Password), paramFile);
+ buffer[strlen(buffer)-1] = '\0';
+ strcpy(params.Password,buffer);
+
+ fclose(paramFile);
+
+ // Connect to datasource
+ strcpy(DbConnectInf.Dsn, params.ODBCSource); // ODBC data source name (created with ODBC Administrator under Win95/NT)
+ strcpy(DbConnectInf.Uid, params.UserName); // database username - must already exist in the data source
+ strcpy(DbConnectInf.AuthStr, params.Password); // password database username
+ READONLY_DB = GetDbConnection(&DbConnectInf);
+ if (READONLY_DB == 0)
+ {
+ wxMessageBox("Unable to connect to the data source.\n\nCheck the name of your data source to verify it has been correctly entered/spelled.\n\nWith some databases, the user name and password must\nbe created with full rights to the CONTACT table prior to making a connection\n(using tools provided by the database manufacturer)", "DB CONNECTION ERROR...",wxOK | wxICON_EXCLAMATION);
+ DemoFrame->BuildParameterDialog(NULL);
+ strcpy(DbConnectInf.Dsn, "");
+ strcpy(DbConnectInf.Uid, "");
+ strcpy(DbConnectInf.AuthStr, "");
+ wxMessageBox("Now exiting program.\n\nRestart program to try any new settings.","Notice...",wxOK | wxICON_INFORMATION);
+ return(FALSE);
+ }
+
+ DemoFrame->BuildEditorDialog();
+
+ // Show the frame
+ DemoFrame->Show(TRUE);
+
+ // Return the main frame window
+ return DemoFrame;
+} // DatabaseDemoApp::OnInit()
+
+
+
+// DatabaseDemoFrame constructor
+DatabaseDemoFrame::DatabaseDemoFrame(wxFrame *frame, char *title, int x, int y, int w, int h):
+ wxFrame(frame, title, x, y, w, h)
+{
+// Put any code in necessary for initializing the main frame here
+}
+
+
+// Intercept menu commands
+void DatabaseDemoFrame::OnMenuCommand(int id)
+{
+ switch (id)
+ {
+ case FILE_CREATE:
+ CreateDataTable();
+ break;
+ case FILE_EXIT:
+ Close();
+ break;
+ case EDIT_PARAMETERS:
+ if ((pEditorDlg->mode != mCreate) && (pEditorDlg->mode != mEdit))
+ BuildParameterDialog(this);
+ else
+ wxMessageBox("Cannot change database parameters while creating or editing a record","Notice...",wxOK | wxICON_INFORMATION);
+ break;
+ case ABOUT_DEMO:
+ wxMessageBox("wxWindows sample program for database classes\n\nContributed on 27 July 1998","About...",wxOK | wxICON_INFORMATION);
+ break;
+ }
+} // DatabaseDemoFrame::OnMenuCommand()
+
+
+Bool DatabaseDemoFrame::OnClose(void)
+{
+ // Put any additional checking necessary to make certain it is alright
+ // to close the program here that is not done elsewhere
+
+ return TRUE;
+} // DatabaseDemoFrame::OnClose()
+
+
+void DatabaseDemoFrame::CreateDataTable()
+{
+ Bool Ok = (wxMessageBox("Any data currently residing in the table will be erased.\n\nAre you sure?","Confirm",wxYES_NO|wxICON_QUESTION) == wxYES);
+
+ if (!Ok)
+ return;
+
+ wxBeginBusyCursor();
+
+ Bool success = TRUE;
+
+ Ccontact *Contact = new Ccontact();
+ if (!Contact)
+ {
+ wxEndBusyCursor();
+ wxMessageBox("Error allocating memory for 'Ccontact'object.\n\nTable was not created.","Error...",wxOK | wxICON_EXCLAMATION);
+ return;
+ }
+
+ if (!Contact->CreateTable())
+ {
+ wxEndBusyCursor();
+ wxString tStr;
+ tStr = "Error creating CONTACTS table.\nTable was not created.\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ success = FALSE;
+ }
+ else
+ {
+ if (!Contact->CreateIndexes())
+ {
+ wxEndBusyCursor();
+ wxString tStr;
+ tStr = "Error creating CONTACTS indexes.\nIndexes will be unavailable.\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ success = FALSE;
+ }
+ }
+ wxEndBusyCursor();
+
+ delete Contact;
+
+ if (success)
+ wxMessageBox("Table and index(es) were successfully created.","Notice...",wxOK | wxICON_INFORMATION);
+} // DatabaseDemoFrame::CreateDataTable()
+
+
+void DatabaseDemoFrame::BuildEditorDialog()
+{
+ pEditorDlg = new CeditorDlg(this);
+ if (!pEditorDlg)
+ wxMessageBox("Unable to create the editor dialog for some reason","Error...",wxOK | wxICON_EXCLAMATION);
+} // DatabaseDemoFrame::BuildEditorDialog()
+
+
+void DatabaseDemoFrame::BuildParameterDialog(wxWindow *parent)
+{
+ pParamDlg = new CparameterDlg(parent);
+
+ if (!pParamDlg)
+ wxMessageBox("Unable to create the parameter dialog for some reason","Error...",wxOK | wxICON_EXCLAMATION);
+} // DatabaseDemoFrame::BuildParameterDialog()
+
+
+/*
+ * Constructor note: If no wxDB object is passed in, a new connection to the database
+ * is created for this instance of Ccontact. This can be a slow process depending
+ * on the database engine being used, and some database engines have a limit on the
+ * number of connections (either hard limits, or license restricted) so care should
+ * be used to use as few connections as is necessary.
+ * IMPORTANT: Objects which share a wxDB pointer are ALL acted upon whenever a member
+ * function of pDb is called (i.e. CommitTrans() or RollbackTrans(), so if modifying
+ * or creating a table objects which use the same pDb, know that all the objects
+ * will be committed or rolled back when any of the objects has this function call made.
+ */
+Ccontact::Ccontact (wxDB *pwxDB) : wxTable(pwxDB ? pwxDB : GetDbConnection(&DbConnectInf),CONTACT_TABLE_NAME,CONTACT_NO_COLS)
+{
+ // This is used to represent whether the database connection should be released
+ // when this instance of the object is deleted. If using the same connection
+ // for multiple instance of database objects, then the connection should only be
+ // released when the last database instance using the connection is deleted
+ freeDbConn = !pwxDB;
+
+ SetupColumns();
+
+} // Ccontact Constructor
+
+
+void Ccontact::Initialize()
+{
+ Name[0] = 0;
+ Addr1[0] = 0;
+ Addr2[0] = 0;
+ City[0] = 0;
+ State[0] = 0;
+ PostalCode[0] = 0;
+ Country[0] = 0;
+ JoinDate.year = 1980;
+ JoinDate.month = 1;
+ JoinDate.day = 1;
+ JoinDate.hour = 0;
+ JoinDate.minute = 0;
+ JoinDate.second = 0;
+ JoinDate.fraction = 0;
+ NativeLanguage = langENGLISH;
+ IsDeveloper = FALSE;
+ Contributions = 0;
+ LinesOfCode = 0L;
+} // Ccontact::Initialize
+
+
+Ccontact::~Ccontact()
+{
+ if (freeDbConn)
+ {
+ if (!FreeDbConnection(pDb))
+ {
+ wxString tStr;
+ tStr = "Unable to Free the Ccontact data table handle\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ }
+ }
+} // Ccontract destructor
+
+
+/*
+ * Handles setting up all the connections for the interface from the wxTable
+ * functions to interface to the data structure used to store records in
+ * memory, and for all the column definitions that define the table structure
+ */
+void Ccontact::SetupColumns()
+{
+ SetColDefs ( 0,"NAME", DB_DATA_TYPE_VARCHAR, Name, SQL_C_CHAR, sizeof(Name), TRUE, TRUE); // Primary index
+ SetColDefs ( 1,"ADDRESS1", DB_DATA_TYPE_VARCHAR, Addr1, SQL_C_CHAR, sizeof(Addr1), FALSE,TRUE);
+ SetColDefs ( 2,"ADDRESS2", DB_DATA_TYPE_VARCHAR, Addr2, SQL_C_CHAR, sizeof(Addr2), FALSE,TRUE);
+ SetColDefs ( 3,"CITY", DB_DATA_TYPE_VARCHAR, City, SQL_C_CHAR, sizeof(City), FALSE,TRUE);
+ SetColDefs ( 4,"STATE", DB_DATA_TYPE_VARCHAR, State, SQL_C_CHAR, sizeof(State), FALSE,TRUE);
+ SetColDefs ( 5,"POSTAL_CODE", DB_DATA_TYPE_VARCHAR, PostalCode, SQL_C_CHAR, sizeof(PostalCode), FALSE,TRUE);
+ SetColDefs ( 6,"COUNTRY", DB_DATA_TYPE_VARCHAR, Country, SQL_C_CHAR, sizeof(Country), FALSE,TRUE);
+ SetColDefs ( 7,"JOIN_DATE", DB_DATA_TYPE_DATE, &JoinDate, SQL_C_TIMESTAMP, sizeof(JoinDate), FALSE,TRUE);
+ SetColDefs ( 8,"NATIVE_LANGUAGE", DB_DATA_TYPE_INTEGER, &NativeLanguage, SQL_C_ENUM, sizeof(NativeLanguage), FALSE,TRUE);
+ SetColDefs ( 9,"IS_DEVELOPER", DB_DATA_TYPE_INTEGER, &IsDeveloper, SQL_C_BOOLEAN, sizeof(Bool), FALSE,TRUE);
+ SetColDefs (10,"CONTRIBUTIONS", DB_DATA_TYPE_INTEGER, &Contributions, SQL_C_USHORT, sizeof(Contributions), FALSE,TRUE);
+ SetColDefs (11,"LINES_OF_CODE", DB_DATA_TYPE_INTEGER, &LinesOfCode, SQL_C_ULONG, sizeof(LinesOfCode), FALSE,TRUE);
+} // Ccontact::SetupColumns
+
+
+Bool Ccontact::CreateIndexes(void)
+{
+ // This index could easily be accomplished with an "orderBy" clause,
+ // but is done to show how to construct a non-primary index.
+ wxString indexName;
+ CidxDef idxDef[2];
+
+ Bool Ok = TRUE;
+
+ strcpy(idxDef[0].ColName, "IS_DEVELOPER");
+ idxDef[0].Ascending = TRUE;
+
+ strcpy(idxDef[1].ColName, "NAME");
+ idxDef[1].Ascending = TRUE;
+
+ indexName = CONTACT_TABLE_NAME;
+ indexName += "_IDX1";
+ Ok = CreateIndex(indexName.GetData(), TRUE, 2, idxDef);
+
+ return Ok;
+} // Ccontact::CreateIndexes()
+
+
+/*
+ * Having a function to do a query on the primary key (and possibly others) is
+ * very efficient and tighter coding so that it is available where ever the object
+ * is. Great for use with multiple tables when not using views or outer joins
+ */
+Bool Ccontact::FetchByName(char *name)
+{
+ whereStr.sprintf("NAME = '%s'",name);
+ where = this->whereStr.GetData();
+ orderBy = 0;
+
+ if (!Query())
+ return(FALSE);
+
+ // Fetch the record
+ return(GetNext());
+
+} // Ccontact::FetchByName()
+
+
+/*
+ *
+ * ************* DIALOGS ***************
+ *
+ */
+
+
+/* CeditorDlg constructor
+ *
+ * Creates the dialog used for creating/editing/deleting/copying a Ccontact object.
+ * This dialog actually is drawn in the main frame of the program
+ *
+ * An instance of Ccontact is created - "Contact" - which is used to hold the Ccontact
+ * object that is currently being worked with.
+ */
+CeditorDlg::CeditorDlg(wxWindow *parent) : wxPanel (parent, 1, 1, 460, 455)
+{
+ // Since the ::OnCommand() function is overridden, this prevents the widget
+ // detection in ::OnCommand() until all widgets have been initialized to prevent
+ // uninitialized pointers from crashing the program
+ widgetPtrsSet = FALSE;
+
+ // Create the data structure and a new database connection.
+ // (As there is not a pDb being passed in the constructor, a new database
+ // connection is created)
+ Contact = new Ccontact();
+
+ if (!Contact)
+ {
+ wxMessageBox("Unable to instantiate an instance of Ccontact","Error...",wxOK | wxICON_EXCLAMATION);
+ return;
+ }
+
+ // Check if the table exists or not. If it doesn't, ask the user if they want to
+ // create the table. Continue trying to create the table until it exists, or user aborts
+ while (!Contact->pDb->TableExists((char *)CONTACT_TABLE_NAME))
+ {
+ wxString tStr;
+ tStr.sprintf("Unable to open the table '%s'.\n\nTable may need to be created...?\n\n",CONTACT_TABLE_NAME);
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+
+ Bool createTable = (wxMessageBox("Do you wish to try to create/clear the CONTACTS table?","Confirm",wxYES_NO|wxICON_QUESTION) == wxYES);
+
+ if (!createTable)
+ {
+ delete Contact;
+ Close();
+ DemoFrame->Close();
+ return;
+ }
+ else
+ DemoFrame->CreateDataTable();
+ }
+
+ // Tables must be "opened" before anything other than creating/deleting table can be done
+ if (!Contact->Open())
+ {
+ // Table does exist, there was some problem opening it. Currently this should
+ // never fail, except in the case of the table not exisiting. Open() basically
+ // only sets up variable/pointer values, other than checking for table existence.
+ if (Contact->pDb->TableExists((char *)CONTACT_TABLE_NAME))
+ {
+ wxString tStr;
+ tStr.sprintf("Unable to open the table '%s'.\n\n",CONTACT_TABLE_NAME);
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ delete Contact;
+ Close();
+ DemoFrame->Close();
+ return;
+ }
+ }
+
+ // Build the dialog
+ SetLabelPosition(wxHORIZONTAL);
+
+ wxFont *ButtonFont = new wxFont(12,wxSWISS,wxNORMAL,wxBOLD);
+ wxFont *TextFont = new wxFont(12,wxSWISS,wxNORMAL,wxNORMAL);
+
+ SetButtonFont(ButtonFont);
+ SetLabelFont(TextFont);
+ SetLabelPosition(wxVERTICAL);
+
+ wxGroupBox *FunctionGrp = new wxGroupBox(this, "", 15, 1, 497, 69, 0, "FunctionGrp");
+ wxGroupBox *SearchGrp = new wxGroupBox(this, "", 417, 1, 95, 242, 0, "SearchGrp");
+
+ pCreateBtn = new wxButton(this, NULL, "&Create", 25, 21, 70, 35, 0, "CreateBtn");
+ pEditBtn = new wxButton(this, NULL, "&Edit", 102, 21, 70, 35, 0, "EditBtn");
+ pDeleteBtn = new wxButton(this, NULL, "&Delete", 179, 21, 70, 35, 0, "DeleteBtn");
+ pCopyBtn = new wxButton(this, NULL, "Cop&y", 256, 21, 70, 35, 0, "CopyBtn");
+ pSaveBtn = new wxButton(this, NULL, "&Save", 333, 21, 70, 35, 0, "SaveBtn");
+ pCancelBtn = new wxButton(this, NULL, "C&ancel", 430, 21, 70, 35, 0, "CancelBtn");
+
+ pPrevBtn = new wxButton(this, NULL, "<< &Prev",430, 81, 70, 35, 0, "PrevBtn");
+ pNextBtn = new wxButton(this, NULL, "&Next >>",430, 121, 70, 35, 0, "NextBtn");
+ pQueryBtn = new wxButton(this, NULL, "&Query", 430, 161, 70, 35, 0, "QueryBtn");
+ pResetBtn = new wxButton(this, NULL, "&Reset", 430, 200, 70, 35, 0, "ResetBtn");
+
+ pNameMsg = new wxMessage(this, "Name:", 17, 80, -1, -1, 0, "NameMsg");
+ pNameTxt = new wxText(this, NULL, "", "", 17, 97, 308, 25, 0, "NameTxt");
+ pNameListBtn = new wxButton(this, NULL, "&Lookup", 333, 99, 70, 24, 0, "LookupBtn");
+
+ pAddress1Msg = new wxMessage(this, "Address:", 17, 130, -1, -1, 0, "Address1Msg");
+ pAddress1Txt = new wxText(this, NULL, "", "", 17, 147, 308, 25, 0, "Address1Txt");
+
+ pAddress2Msg = new wxMessage(this, "Address:", 17, 180, -1, -1, 0, "Address2Msg");
+ pAddress2Txt = new wxText(this, NULL, "", "", 17, 197, 308, 25, 0, "Address2Txt");
+
+ pCityMsg = new wxMessage(this, "City:", 17, 230, -1, -1, 0, "CityMsg");
+ pCityTxt = new wxText(this, NULL, "", "", 17, 247, 225, 25, 0, "CityTxt");
+
+ pStateMsg = new wxMessage(this, "State:", 250, 230, -1, -1, 0, "StateMsg");
+ pStateTxt = new wxText(this, NULL, "", "", 250, 247, 153, 25, 0, "StateTxt");
+
+ pCountryMsg = new wxMessage(this, "Country:", 17, 280, -1, -1, 0, "CountryMsg");
+ pCountryTxt = new wxText(this, NULL, "", "", 17, 297, 225, 25, 0, "CountryTxt");
+
+ pPostalCodeMsg = new wxMessage(this, "Postal Code:", 250, 280, -1, -1, 0, "PostalCodeMsg");
+ pPostalCodeTxt = new wxText(this, NULL, "", "", 250, 297, 153, 25, 0, "PostalCodeTxt");
+
+ char *choice_strings[5];
+ choice_strings[0] = "English";
+ choice_strings[1] = "French";
+ choice_strings[2] = "German";
+ choice_strings[3] = "Spanish";
+ choice_strings[4] = "Other";
+ pNativeLangChoice = new wxChoice(this, NULL, "",17, 346, 277, -1, 5, choice_strings);
+ pNativeLangMsg = new wxMessage(this, "Native language:", 17, 330, -1, -1, 0, "NativeLangMsg");
+
+ char *radio_strings[2];
+ radio_strings[0] = "No";
+ radio_strings[1] = "Yes";
+ pDeveloperRadio = new wxRadioBox(this,NULL,"Developer:",303,330,-1,-1,2,radio_strings,2,wxHORIZONTAL|wxFLAT);
+
+ pJoinDateMsg = new wxMessage(this, "Date joined:", 17, 380, -1, -1, 0, "JoinDateMsg");
+ pJoinDateTxt = new wxText(this, NULL, "", "", 17, 397, 150, 25, 0, "JoinDateTxt");
+
+ pContribMsg = new wxMessage(this, "Contributions:", 175, 380, -1, -1, 0, "ContribMsg");
+ pContribTxt = new wxText(this, NULL, "", "", 175, 397, 120, 25, 0, "ContribTxt");
+
+ pLinesMsg = new wxMessage(this, "Lines of code:", 303, 380, -1, -1, 0, "LinesMsg");
+ pLinesTxt = new wxText(this, NULL, "", "", 303, 397, 100, 25, 0, "LinesTxt");
+
+ // Now that all the widgets on the panel are created, its safe to allow ::OnCommand() to
+ // handle all widget processing
+ widgetPtrsSet = TRUE;
+
+ // Setup the orderBy and where clauses to return back a single record as the result set,
+ // as there will only be one record being shown on the dialog at a time, this optimizes
+ // network traffic by only returning a one row result
+
+ Contact->orderBy = "NAME"; // field name to sort by
+
+ // The wxString "whereStr" is not a member of the wxTable object, it is a member variable
+ // specifically in the Ccontact class. It is used here for simpler construction of a varying
+ // length string, and then after the string is built, the wxTable member variable "where" is
+ // assigned the pointer to the constructed string.
+ //
+ // The constructed where clause below has a sub-query within it "SELECT MIN(NAME) FROM %s"
+ // to achieve a single row (in this case the first name in alphabetical order).
+ Contact->whereStr.sprintf("NAME = (SELECT MIN(NAME) FROM %s)",Contact->tableName);
+
+ // NOTE: GetData() returns a pointer which may not be valid later, so this is short term use only
+ Contact->where = Contact->whereStr.GetData();
+
+ // Perform the Query to get the result set.
+ // NOTE: If there are no rows returned, that is a valid result, so Query() would return TRUE.
+ // Only if there is a database error will Query() come back as FALSE
+ if (!Contact->Query())
+ {
+ wxString tStr;
+ tStr = "ODBC error during Query()\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ GetParent()->Close();
+ return;
+ }
+
+ // Since Query succeeded, now get the row that was returned
+ if (!Contact->GetNext())
+ // If the GetNext() failed at this point, then there are no rows to retrieve,
+ // so clear the values in the members of "Contact" so that PutData() blanks the
+ // widgets on the dialog
+ Contact->Initialize();
+
+ SetMode(mView);
+ PutData();
+
+ Show(TRUE);
+} // CeditorDlg constructor
+
+
+Bool CeditorDlg::OnClose()
+{
+ // Clean up time
+ if ((mode != mCreate) && (mode != mEdit))
+ {
+ if (Contact)
+ delete Contact;
+ return TRUE;
+ }
+ else
+ {
+ wxMessageBox("Must finish processing the current record being created/modified before exiting","Notice...",wxOK | wxICON_INFORMATION);
+ return FALSE;
+ }
+} // CeditorDlg::OnClose()
+
+
+void CeditorDlg::OnCommand(wxWindow& win, wxCommandEvent& event)
+{
+ wxString widgetName;
+
+ widgetName = win.GetName();
+
+ if (!widgetPtrsSet)
+ return;
+
+ if (widgetName == pCreateBtn->GetName())
+ {
+ Contact->Initialize();
+ PutData();
+ SetMode( mCreate );
+ pNameTxt->SetValue("");
+ pNameTxt->SetFocus();
+ return;
+ }
+
+ if (widgetName == pEditBtn->GetName())
+ {
+ saveName = Contact->Name;
+ SetMode( mEdit );
+ pNameTxt->SetFocus();
+ return;
+ }
+
+ if (widgetName == pCopyBtn->GetName())
+ {
+ SetMode(mCreate);
+ pNameTxt->SetValue("");
+ pNameTxt->SetFocus();
+ return;
+ }
+
+ if (widgetName == pDeleteBtn->GetName())
+ {
+ Bool Ok = (wxMessageBox("Are you sure?","Confirm",wxYES_NO|wxICON_QUESTION) == wxYES);
+
+ if (!Ok)
+ return;
+
+ if (Ok && Contact->Delete())
+ {
+ // NOTE: Deletions are not finalized until a CommitTrans() is performed.
+ // If the commit were not performed, the program will continue to
+ // show the table contents as if they were deleted until this instance
+ // of Ccontact is deleted. If the Commit wasn't performed, the
+ // database will automatically Rollback the changes when the database
+ // connection is terminated
+ Contact->pDb->CommitTrans();
+
+ // Try to get the row that followed the just deleted row in the orderBy sequence
+ if (!GetNextRec())
+ {
+ // There was now row (in sequence) after the just deleted row, so get the
+ // row which preceded the just deleted row
+ if (!GetPrevRec())
+ {
+ // There are now no rows remaining, so clear the dialog widgets
+ Contact->Initialize();
+ PutData();
+ }
+ }
+ SetMode(mode); // force reset of button enable/disable
+ }
+ else
+ // Delete failed
+ Contact->pDb->RollbackTrans();
+
+ SetMode(mView);
+ return;
+ }
+
+ if (widgetName == pSaveBtn->GetName())
+ {
+ Save();
+ return;
+ }
+
+ if (widgetName == pCancelBtn->GetName())
+ {
+ Bool Ok = (wxMessageBox("Are you sure?","Confirm",wxYES_NO|wxICON_QUESTION) == wxYES);
+
+ if (!Ok)
+ return;
+
+ if (!strcmp(saveName.GetData(),""))
+ {
+ Contact->Initialize();
+ PutData();
+ SetMode(mView);
+ return;
+ }
+ else
+ {
+ // Requery previous record
+ if (Contact->FetchByName(saveName.GetData()))
+ {
+ PutData();
+ SetMode(mView);
+ return;
+ }
+ }
+
+ // Previous record not available, retrieve first record in table
+ Contact->whereStr = "NAME = (SELECT MIN(NAME) FROM ";
+ Contact->whereStr += Contact->tableName;
+ Contact->whereStr += ")";
+
+ Contact->where = Contact->whereStr.GetData();
+ if (!Contact->Query())
+ {
+ wxString tStr;
+ tStr = "ODBC error during Query()\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ SetMode(mView);
+ return;
+ }
+ if (Contact->GetNext()) // Successfully read first record
+ {
+ PutData();
+ SetMode(mView);
+ return;
+ }
+ // No contacts are available, clear dialog
+ Contact->Initialize();
+ PutData();
+ SetMode(mView);
+ return;
+ } // Cancel Button
+
+ if (widgetName == pPrevBtn->GetName())
+ {
+ if (!GetPrevRec())
+ wxBell();
+ return;
+ } // Prev Button
+
+ if (widgetName == pNextBtn->GetName())
+ {
+ if (!GetNextRec())
+ wxBell();
+ return;
+ } // Next Button
+
+ if (widgetName == pQueryBtn->GetName())
+ {
+ // Display the query dialog box
+ char qryWhere[DB_MAX_WHERE_CLAUSE_LEN+1];
+ strcpy(qryWhere, Contact->qryWhereStr.GetData());
+ char *tblName[] = {(char *)CONTACT_TABLE_NAME, 0};
+ new CqueryDlg(GetParent(), Contact->pDb, tblName, qryWhere);
+
+ // Query the first record in the new record set and
+ // display it, if the query string has changed.
+ if (strcmp(qryWhere, Contact->qryWhereStr.GetData()))
+ {
+ Contact->orderBy = "NAME";
+ Contact->whereStr = "NAME = (SELECT MIN(NAME) FROM ";
+ Contact->whereStr += CONTACT_TABLE_NAME;
+ // Append the query where string (if there is one)
+ Contact->qryWhereStr = qryWhere;
+ if (strlen(qryWhere))
+ {
+ Contact->whereStr += " WHERE ";
+ Contact->whereStr += Contact->qryWhereStr;
+ }
+ // Close the expression with a right paren
+ Contact->whereStr += ")";
+ // Requery the table
+ Contact->where = Contact->whereStr.GetData();
+ if (!Contact->Query())
+ {
+ wxString tStr;
+ tStr = "ODBC error during Query()\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ return;
+ }
+ // Display the first record from the query set
+ if (!Contact->GetNext())
+ Contact->Initialize();
+ PutData();
+ }
+
+ // Enable/Disable the reset button
+ pResetBtn->Enable(!Contact->qryWhereStr.Empty());
+
+ return;
+ } // Query button
+
+
+ if (widgetName == pResetBtn->GetName())
+ {
+ // Clear the additional where criteria established by the query feature
+ Contact->qryWhereStr = "";
+
+ // Query the first record in the table
+ Contact->orderBy = "NAME";
+ Contact->whereStr = "NAME = (SELECT MIN(NAME) FROM ";
+ Contact->whereStr += CONTACT_TABLE_NAME;
+ Contact->whereStr += ")";
+ Contact->where = Contact->whereStr.GetData();
+ if (!Contact->Query())
+ {
+ wxString tStr;
+ tStr = "ODBC error during Query()\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ return;
+ }
+ if (!Contact->GetNext())
+ Contact->Initialize();
+ PutData();
+ pResetBtn->Enable(FALSE);
+
+ return;
+ } // Reset button
+
+
+ if (widgetName == pNameListBtn->GetName())
+ {
+ new ClookUpDlg(/* wxWindow *parent */ this,
+ /* char *windowTitle */ "Select contact name",
+ /* char *tableName */ (char *) CONTACT_TABLE_NAME,
+ /* char *dispCol1 */ "NAME",
+ /* char *dispCol2 */ "JOIN_DATE",
+ /* char *where */ "",
+ /* char *orderBy */ "NAME",
+ /* Bool distinctValues */ TRUE);
+
+ if (ListDB_Selection && strlen(ListDB_Selection))
+ {
+ wxString w = "NAME = '";
+ w += ListDB_Selection;
+ w += "'";
+ GetRec(w.GetData());
+ }
+
+ return;
+ }
+
+} // CeditorDlg::OnCommand()
+
+
+void CeditorDlg::FieldsEditable()
+{
+ pNameTxt->Enable((mode == mCreate) || (mode == mEdit));
+ pAddress1Txt->Enable((mode == mCreate) || (mode == mEdit));
+ pAddress2Txt->Enable((mode == mCreate) || (mode == mEdit));
+ pCityTxt->Enable((mode == mCreate) || (mode == mEdit));
+ pStateTxt->Enable((mode == mCreate) || (mode == mEdit));
+ pPostalCodeTxt->Enable((mode == mCreate) || (mode == mEdit));
+ pCountryTxt->Enable((mode == mCreate) || (mode == mEdit));
+
+ pJoinDateTxt->Enable((mode == mCreate) || (mode == mEdit));
+ pContribTxt->Enable((mode == mCreate) || (mode == mEdit));
+ pLinesTxt->Enable((mode == mCreate) || (mode == mEdit));
+ pNativeLangChoice->Enable((mode == mCreate) || (mode == mEdit));
+ pDeveloperRadio->Enable((mode == mCreate) || (mode == mEdit));
+
+} // CeditorDlg::FieldsEditable()
+
+
+void CeditorDlg::SetMode(enum DialogModes m)
+{
+ Bool edit = FALSE;
+
+ mode = m;
+ switch (mode)
+ {
+ case mCreate:
+ case mEdit:
+ edit = TRUE;
+ break;
+ case mView:
+ case mSearch:
+ edit = FALSE;
+ break;
+ default:
+ break;
+ };
+
+ if (widgetPtrsSet)
+ {
+ pCreateBtn->Enable( !edit );
+ pEditBtn->Enable( !edit && (strcmp(Contact->Name,"")!=0) );
+ pDeleteBtn->Enable( !edit && (strcmp(Contact->Name,"")!=0) );
+ pCopyBtn->Enable( !edit && (strcmp(Contact->Name,"")!=0) );
+ pSaveBtn->Enable( edit );
+ pCancelBtn->Enable( edit );
+ pPrevBtn->Enable( !edit );
+ pNextBtn->Enable( !edit );
+ pQueryBtn->Enable( !edit );
+ pResetBtn->Enable( !edit && !Contact->qryWhereStr.Empty() );
+ pNameListBtn->Enable( !edit );
+ }
+
+ FieldsEditable();
+} // CeditorDlg::SetMode()
+
+
+Bool CeditorDlg::PutData()
+{
+ wxString tStr;
+
+ pNameTxt->SetValue(Contact->Name);
+ pAddress1Txt->SetValue(Contact->Addr1);
+ pAddress2Txt->SetValue(Contact->Addr2);
+ pCityTxt->SetValue(Contact->City);
+ pStateTxt->SetValue(Contact->State);
+ pCountryTxt->SetValue(Contact->Country);
+ pPostalCodeTxt->SetValue(Contact->PostalCode);
+
+ tStr.sprintf("%d/%d/%d",Contact->JoinDate.month,Contact->JoinDate.day,Contact->JoinDate.year);
+ pJoinDateTxt->SetValue(tStr.GetData());
+
+ tStr.sprintf("%d",Contact->Contributions);
+ pContribTxt->SetValue(tStr.GetData());
+
+ tStr.sprintf("%lu",Contact->LinesOfCode);
+ pLinesTxt->SetValue(tStr.GetData());
+
+ pNativeLangChoice->SetSelection(Contact->NativeLanguage);
+
+ pDeveloperRadio->SetSelection(Contact->IsDeveloper);
+
+ return TRUE;
+} // Ceditor::PutData()
+
+
+/*
+ * Reads the data out of all the widgets on the dialog. Some data evaluation is done
+ * to ensure that there is a name entered and that the date field is valid.
+ *
+ * A return value of TRUE means that valid data was retrieved from the dialog, otherwise
+ * invalid data was found (and a message was displayed telling the user what to fix), and
+ * the data was not placed into the appropraite fields of Ccontact
+ */
+Bool CeditorDlg::GetData()
+{
+ // Validate that the data currently entered into the widgets is valid data
+
+ wxString tStr;
+ tStr = pNameTxt->GetValue();
+ if (!strcmp(tStr.GetData(),""))
+ {
+ wxMessageBox("A name is required for entry into the contact table","Notice...",wxOK | wxICON_INFORMATION);
+ return FALSE;
+ }
+
+ Bool invalid = FALSE;
+ int mm,dd,yyyy;
+ int first, second;
+
+ tStr = pJoinDateTxt->GetValue();
+ if (tStr.Freq('/') != 2)
+ invalid = TRUE;
+
+ // Find the month, day, and year tokens
+ if (!invalid)
+ {
+ first = tStr.First('/');
+ second = tStr.Last('/');
+
+ mm = atoi(tStr.SubString(0,first));
+ dd = atoi(tStr.SubString(first+1,second));
+ yyyy = atoi(tStr.SubString(second+1,tStr.Length()-1));
+
+ invalid = !(mm && dd && yyyy);
+ }
+
+ // Force Year 2000 compliance
+ if (!invalid && (yyyy < 1000))
+ invalid = TRUE;
+
+ // Check the token ranges for validity
+ if (!invalid)
+ {
+ if (yyyy > 9999)
+ invalid = TRUE;
+ else if ((mm < 1) || (mm > 12))
+ invalid = TRUE;
+ else
+ {
+ if (dd < 1)
+ invalid = TRUE;
+ else
+ {
+ int days[12] = {31,28,31,30,31,30,
+ 31,31,30,31,30,31};
+ if (dd > days[mm-1])
+ {
+ invalid = TRUE;
+ if ((dd == 29) && (mm == 2))
+ {
+ if (((yyyy % 4) == 0) && (((yyyy % 100) != 0) || ((yyyy % 400) == 0)))
+ invalid = FALSE;
+ }
+ }
+ }
+ }
+ }
+
+ if (!invalid)
+ {
+ Contact->JoinDate.month = mm;
+ Contact->JoinDate.day = dd;
+ Contact->JoinDate.year = yyyy;
+ }
+ else
+ {
+ wxMessageBox("Improper date format. Please check the date\nspecified and try again.\n\nNOTE: Dates are in american format (MM/DD/YYYY)","Notice...",wxOK | wxICON_INFORMATION);
+ return FALSE;
+ }
+
+ tStr = pNameTxt->GetValue();
+ strcpy(Contact->Name,tStr.GetData());
+ strcpy(Contact->Addr1,pAddress1Txt->GetValue());
+ strcpy(Contact->Addr2,pAddress2Txt->GetValue());
+ strcpy(Contact->City,pCityTxt->GetValue());
+ strcpy(Contact->State,pStateTxt->GetValue());
+ strcpy(Contact->Country,pCountryTxt->GetValue());
+ strcpy(Contact->PostalCode,pPostalCodeTxt->GetValue());
+
+ Contact->Contributions = atoi(pContribTxt->GetValue());
+ Contact->LinesOfCode = atol(pLinesTxt->GetValue());
+
+ Contact->NativeLanguage = (enum Language) pNativeLangChoice->GetSelection();
+ Contact->IsDeveloper = pDeveloperRadio->GetSelection();
+
+ return TRUE;
+} // CeditorDlg::GetData()
+
+
+/*
+ * Retrieve data from the dialog, verify the validity of the data, and if it is valid,
+ * try to insert/update the data to the table based on the current 'mode' the dialog
+ * is set to.
+ *
+ * A return value of TRUE means the insert/update was completed successfully, a return
+ * value of FALSE means that Save() failed. If returning FALSE, then this function
+ * has displayed a detailed error message for the user.
+ */
+Bool CeditorDlg::Save()
+{
+ Bool failed = FALSE;
+
+ // Read the data in the widgets of the dialog to get the user's data
+ if (!GetData())
+ failed = TRUE;
+
+ // Perform any other required validations necessary before saving
+
+
+ if (!failed)
+ {
+ wxBeginBusyCursor();
+
+ if (mode == mCreate)
+ {
+ RETCODE result = Contact->Insert();
+
+ failed = (result != DB_SUCCESS);
+ if (failed)
+ {
+ // Some errors may be expected, like a duplicate key, so handle those instances with
+ // specific error messages.
+ if (result == DB_ERR_INTEGRITY_CONSTRAINT_VIOL)
+ {
+ wxString tStr;
+ tStr = "A duplicate key value already exists in the table.\nUnable to save record\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ }
+ else
+ {
+ // Some other unexpexted error occurred
+ wxString tStr;
+ tStr = "Database insert failed\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ }
+ }
+ }
+ else // mode == mEdit
+ {
+ if (!Contact->Update())
+ {
+ wxString tStr;
+ tStr = "Database update failed\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ failed = TRUE;
+ }
+ }
+
+ if (!failed)
+ {
+ Contact->pDb->CommitTrans();
+ SetMode(mView); // Sets the dialog mode back to viewing after save is successful
+ }
+ else
+ Contact->pDb->RollbackTrans();
+
+ wxEndBusyCursor();
+ }
+
+ return !failed;
+} // CeditorDlg::Save()
+
+
+/*
+ * Where this program is only showing a single row at a time in the dialog,
+ * a special where clause must be built to find just the single row which,
+ * in sequence, would follow the currently displayed row.
+ */
+Bool CeditorDlg::GetNextRec()
+{
+ wxString w;
+
+ w = "NAME = (SELECT MIN(NAME) FROM ";
+ w += Contact->tableName;
+ w += " WHERE NAME > '";
+ w += Contact->Name;
+ w += "'";
+
+ // If a query where string is currently set, append that criteria
+ if (!Contact->qryWhereStr.Empty())
+ {
+ w += " AND (";
+ w += Contact->qryWhereStr;
+ w += ")";
+ }
+
+ w += ")";
+
+ return(GetRec(w.GetData()));
+
+} // CeditorDlg::GetNextRec()
+
+
+/*
+ * Where this program is only showing a single row at a time in the dialog,
+ * a special where clause must be built to find just the single row which,
+ * in sequence, would precede the currently displayed row.
+ */
+Bool CeditorDlg::GetPrevRec()
+{
+ wxString w;
+
+ w = "NAME = (SELECT MAX(NAME) FROM ";
+ w += Contact->tableName;
+ w += " WHERE NAME < '";
+ w += Contact->Name;
+ w += "'";
+
+ // If a query where string is currently set, append that criteria
+ if (!Contact->qryWhereStr.Empty())
+ {
+ w += " AND (";
+ w += Contact->qryWhereStr;
+ w += ")";
+ }
+
+ w += ")";
+
+ return(GetRec(w.GetData()));
+
+} // CeditorDlg::GetPrevRec()
+
+
+/*
+ * This function is here to avoid duplicating this same code in both the
+ * GetPrevRec() and GetNextRec() functions
+ */
+Bool CeditorDlg::GetRec(char *whereStr)
+{
+ Contact->where = whereStr;
+ Contact->orderBy = "NAME";
+
+ if (!Contact->Query())
+ {
+ wxString tStr;
+ tStr = "ODBC error during Query()\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+
+ return(FALSE);
+ }
+
+ if (Contact->GetNext())
+ {
+ PutData();
+ return(TRUE);
+ }
+ else
+ return(FALSE);
+} // CeditorDlg::GetRec()
+
+
+
+/*
+ * CparameterDlg constructor
+ */
+CparameterDlg::CparameterDlg(wxWindow *parent) : wxDialogBox (parent, "ODBC parameter settings", 1, -1, -1, 400, 275)
+{
+ // Since the ::OnCommand() function is overridden, this prevents the widget
+ // detection in ::OnCommand() until all widgets have been initialized to prevent
+ // uninitialized pointers from crashing the program
+ widgetPtrsSet = FALSE;
+
+ // Build the dialog
+ SetLabelPosition(wxVERTICAL);
+
+ wxFont *ButtonFont = new wxFont(12,wxSWISS,wxNORMAL,wxBOLD);
+ wxFont *TextFont = new wxFont(12,wxSWISS,wxNORMAL,wxNORMAL);
+
+ SetButtonFont(ButtonFont);
+ SetLabelFont(TextFont);
+ SetLabelPosition(wxVERTICAL);
+
+ pParamODBCSourceMsg = new wxMessage(this, "ODBC data sources:", 10, 10, -1, -1, 0, "ParamODBCSourceMsg");
+ pParamODBCSourceList = new wxListBox(this, NULL, "", wxSINGLE|wxALWAYS_SB, 10, 29, 285, 150, 0, 0, 0, "ParamODBCSourceList");
+
+ pParamUserNameMsg = new wxMessage(this, "Database user name:", 10, 193, -1, -1, 0, "ParamUserNameMsg");
+ pParamUserNameTxt = new wxText(this, NULL, "", "", 10, 209, 140, 25, 0, "ParamUserNameTxt");
+
+ pParamPasswordMsg = new wxMessage(this, "Password:", 156, 193, -1, -1, 0, "ParamPasswordMsg");
+ pParamPasswordTxt = new wxText(this, NULL, "", "", 156, 209, 140, 25, 0, "ParamPasswordTxt");
+
+ pParamSaveBtn = new wxButton(this, NULL, "&Save", 310, 21, 70, 35, 0, "ParamSaveBtn");
+ pParamCancelBtn = new wxButton(this, NULL, "C&ancel", 310, 66, 70, 35, 0, "ParamCancelBtn");
+
+ // Now that all the widgets on the panel are created, its safe to allow ::OnCommand() to
+ // handle all widget processing
+ widgetPtrsSet = TRUE;
+
+ saved = FALSE;
+ savedParamSettings = DatabaseDemoApp.params;
+
+ Centre(wxBOTH);
+ PutData();
+ Show(TRUE);
+} // CparameterDlg constructor
+
+
+Bool CparameterDlg::OnClose()
+{
+ // Put any additional checking necessary to make certain it is alright
+ // to close the program here that is not done elsewhere
+ if (!saved)
+ {
+ Bool Ok = (wxMessageBox("No changes have been saved.\n\nAre you sure you wish exit the parameter screen?","Confirm",wxYES_NO|wxICON_QUESTION) == wxYES);
+
+ if (!Ok)
+ return FALSE;
+
+ DatabaseDemoApp.params = savedParamSettings;
+ }
+
+ if (GetParent() != NULL)
+ GetParent()->SetFocus();
+ return TRUE;
+} // Cparameter::OnClose()
+
+
+void CparameterDlg::OnCommand(wxWindow& win, wxCommandEvent& event)
+{
+ wxString widgetName;
+
+ widgetName = win.GetName();
+
+ if (!widgetPtrsSet)
+ return;
+
+ if (widgetName == pParamSaveBtn->GetName())
+ {
+ if (Save())
+ {
+ wxString tStr;
+ tStr = "Database parameters have been saved.";
+ if (GetParent() != NULL) // The parameter dialog was not called during startup due to a missing cfg file
+ tStr += "\nNew parameters will take effect the next time the program is started.";
+ wxMessageBox(tStr.GetData(),"Notice...",wxOK | wxICON_INFORMATION);
+ saved = TRUE;
+ Close();
+ }
+ return;
+ }
+
+ if (widgetName == pParamCancelBtn->GetName())
+ {
+ Close();
+ return;
+ }
+} // CparameterDlg::OnCommand()
+
+
+Bool CparameterDlg::PutData()
+{
+ // Fill the data source list box
+ FillDataSourceList();
+
+ // Fill in the fields from the params object
+ pParamODBCSourceList->SetStringSelection(DatabaseDemoApp.params.ODBCSource);
+ pParamUserNameTxt->SetValue(DatabaseDemoApp.params.UserName);
+ pParamPasswordTxt->SetValue(DatabaseDemoApp.params.Password);
+ return TRUE;
+} // CparameterDlg::PutData()
+
+
+Bool CparameterDlg::GetData()
+{
+ wxString tStr;
+ if (pParamODBCSourceList->GetStringSelection())
+ {
+ tStr = pParamODBCSourceList->GetStringSelection();
+ if (tStr.Length() > (sizeof(DatabaseDemoApp.params.ODBCSource)-1))
+ {
+ wxString errmsg;
+ errmsg.sprintf("ODBC Data source name is longer than the data structure to hold it.\n'Cparameter.ODBCSource' must have a larger character array\nto handle a data source with this long of a name\n\nThe data source currently selected is %d characters long.",tStr.Length());
+ wxMessageBox(errmsg.GetData(),"Internal program error...",wxOK | wxICON_EXCLAMATION);
+ return FALSE;
+ }
+ strcpy(DatabaseDemoApp.params.ODBCSource, tStr.GetData());
+ }
+ else
+ return FALSE;
+
+ tStr = pParamUserNameTxt->GetValue();
+ if (tStr.Length() > (sizeof(DatabaseDemoApp.params.UserName)-1))
+ {
+ wxString errmsg;
+ errmsg.sprintf("User name is longer than the data structure to hold it.\n'Cparameter.UserName' must have a larger character array\nto handle a data source with this long of a name\n\nThe user name currently specified is %d characters long.",tStr.Length());
+ wxMessageBox(errmsg.GetData(),"Internal program error...",wxOK | wxICON_EXCLAMATION);
+ return FALSE;
+ }
+ strcpy(DatabaseDemoApp.params.UserName, tStr.GetData());
+
+ tStr = pParamPasswordTxt->GetValue();
+ if (tStr.Length() > (sizeof(DatabaseDemoApp.params.Password)-1))
+ {
+ wxString errmsg;
+ errmsg.sprintf("Password is longer than the data structure to hold it.\n'Cparameter.Password' must have a larger character array\nto handle a data source with this long of a name\n\nThe password currently specified is %d characters long.",tStr.Length());
+ wxMessageBox(errmsg.GetData(),"Internal program error...",wxOK | wxICON_EXCLAMATION);
+ return FALSE;
+ }
+ strcpy(DatabaseDemoApp.params.Password,tStr.GetData());
+ return TRUE;
+} // CparameterDlg::GetData()
+
+
+Bool CparameterDlg::Save()
+{
+ Cparameters saveParams = DatabaseDemoApp.params;
+ if (!GetData())
+ {
+ DatabaseDemoApp.params = saveParams;
+ return FALSE;
+ }
+
+ FILE *paramFile;
+ if ((paramFile = fopen(paramFilename, "wt")) == NULL)
+ {
+ wxString tStr;
+ tStr.sprintf("Unable to write/overwrite '%s'.",paramFilename);
+ wxMessageBox(tStr.GetData(),"File I/O Error...",wxOK | wxICON_EXCLAMATION);
+ return FALSE;
+ }
+
+ fputs(DatabaseDemoApp.params.ODBCSource, paramFile);
+ fputc('\n', paramFile);
+ fputs(DatabaseDemoApp.params.UserName, paramFile);
+ fputc('\n', paramFile);
+ fputs(DatabaseDemoApp.params.Password, paramFile);
+ fputc('\n', paramFile);
+ fclose(paramFile);
+
+ return TRUE;
+} // CparameterDlg::Save()
+
+
+void CparameterDlg::FillDataSourceList()
+{
+ char Dsn[SQL_MAX_DSN_LENGTH + 1];
+ char DsDesc[255];
+ wxStringList strList;
+
+ while(GetDataSource(DbConnectInf.Henv, Dsn, SQL_MAX_DSN_LENGTH+1, DsDesc, 255))
+ strList.Add(Dsn);
+
+ strList.Sort();
+ strList.Add("");
+ char **p = strList.ListToArray();
+
+ for (int i = 0; strlen(p[i]); i++)
+ pParamODBCSourceList->Append(p[i]);
+} // CparameterDlg::CparameterDlg::FillDataSourceList()
+
+
+// CqueryDlg() constructor
+CqueryDlg::CqueryDlg(wxWindow *parent, wxDB *pDb, char *tblName[], char *pWhereArg) : wxDialogBox (parent, "Query", 1, -1, -1, 480, 360)
+{
+ wxBeginBusyCursor();
+
+ colInf = 0;
+ dbTable = 0;
+ masterTableName = tblName[0];
+ widgetPtrsSet = FALSE;
+ pDB = pDb;
+
+ // Initialize the WHERE clause from the string passed in
+ pWhere = pWhereArg; // Save a pointer to the output buffer
+ if (strlen(pWhere) > DB_MAX_WHERE_CLAUSE_LEN) // Check the length of the buffer passed in
+ {
+ wxString s;
+ s.sprintf("Maximum where clause length exceeded.\nLength must be less than %d", DB_MAX_WHERE_CLAUSE_LEN+1);
+ wxMessageBox(s.GetData(),"Error...",wxOK | wxICON_EXCLAMATION);
+ Close();
+ return;
+ }
+
+ // Build the dialog
+ SetLabelPosition(wxVERTICAL);
+
+ wxFont *ButtonFont = new wxFont(12,wxSWISS,wxNORMAL,wxBOLD);
+ wxFont *TextFont = new wxFont(12,wxSWISS,wxNORMAL,wxNORMAL);
+
+ SetButtonFont(ButtonFont);
+ SetLabelFont(TextFont);
+ SetLabelPosition(wxVERTICAL);
+
+ pQueryCol1Msg = new wxMessage(this, "Column 1:", 10, 10, 69, 16, 0, "QueryCol1Msg");
+ pQueryCol1Choice = new wxChoice(this, NULL, "", 10, 27, 250, 27, 0, 0, 0, "QueryCol1Choice");
+
+ pQueryNotMsg = new wxMessage(this, "NOT", 268, 10, -1, -1, 0, "QueryNotMsg");
+ pQueryNotCheck = new wxCheckBox(this, NULL, "", 275, 37, 20, 20, 0, "QueryNotCheck");
+
+ char *choice_strings[9];
+ choice_strings[0] = "=";
+ choice_strings[1] = "<";
+ choice_strings[2] = ">";
+ choice_strings[3] = "<=";
+ choice_strings[4] = ">=";
+ choice_strings[5] = "Begins";
+ choice_strings[6] = "Contains";
+ choice_strings[7] = "Like";
+ choice_strings[8] = "Between";
+ pQueryOperatorMsg = new wxMessage(this, "Operator:", 305, 10, -1, -1, 0, "QueryOperatorMsg");
+ pQueryOperatorChoice = new wxChoice(this, NULL, "", 305, 27, 80, 27, 9, choice_strings, 0, "QueryOperatorChoice");
+
+ pQueryCol2Msg = new wxMessage(this, "Column 2:", 10, 65, 69, 16, 0, "QueryCol2Msg");
+ pQueryCol2Choice = new wxChoice(this, NULL, "", 10, 82, 250, 27, 0, 0, 0, "QueryCol2Choice");
+
+ pQuerySqlWhereMsg = new wxMessage(this, "SQL where clause:", 10, 141, -1, -1, 0, "QuerySqlWhereMsg");
+ pQuerySqlWhereMtxt = new wxMultiText(this, NULL, "", "", 10, 159, 377, 134, 0, "QuerySqlWhereMtxt");
+
+ pQueryAddBtn = new wxButton(this, NULL, "&Add", 406, 24, 56, 26, 0, "QueryAddBtn");
+ pQueryAndBtn = new wxButton(this, NULL, "A&nd", 406, 58, 56, 26, 0, "QueryAndBtn");
+ pQueryOrBtn = new wxButton(this, NULL, "&Or", 406, 92, 56, 26, 0, "QueryOrBtn");
+
+ pQueryLParenBtn = new wxButton(this, NULL, "(", 406, 126, 26, 26, 0, "QueryLParenBtn");
+ pQueryRParenBtn = new wxButton(this, NULL, ")", 436, 126, 26, 26, 0, "QueryRParenBtn");
+
+ pQueryDoneBtn = new wxButton(this, NULL, "&Done", 406, 185, 56, 26, 0, "QueryDoneBtn");
+ pQueryClearBtn = new wxButton(this, NULL, "C&lear", 406, 218, 56, 26, 0, "QueryClearBtn");
+ pQueryCountBtn = new wxButton(this, NULL, "&Count", 406, 252, 56, 26, 0, "QueryCountBtn");
+
+ pQueryValue1Msg = new wxMessage(this, "Value:", 277, 66, -1, -1, 0, "QueryValue1Msg");
+ pQueryValue1Txt = new wxText(this, NULL, "", "", 277, 83, 108, 25, 0, "QueryValue1Txt");
+
+ pQueryValue2Msg = new wxMessage(this, "AND", 238, 126, -1, -1, 0, "QueryValue2Msg");
+ pQueryValue2Txt = new wxText(this, NULL, "", "", 277, 120, 108, 25, 0, "QueryValue2Txt");
+
+ pQueryHintGrp = new wxGroupBox(this, "", 10, 291, 377, 40, 0, "QueryHintGrp");
+ pQueryHintMsg = new wxMessage(this, "", 16, 306, -1, -1, 0, "QueryHintMsg");
+
+ widgetPtrsSet = TRUE;
+ // Initialize the dialog
+ wxString qualName;
+ pQueryCol2Choice->Append("VALUE -->");
+ colInf = pDB->GetColumns(tblName);
+ for (int i = 0; colInf[i].colName && strlen(colInf[i].colName); i++)
+ {
+ // If there is more than one table being queried, qualify
+ // the column names with the table name prefix.
+ if (tblName[1] && strlen(tblName[1]))
+ {
+ qualName.sprintf("%s.%s", colInf[i].tableName, colInf[i].colName);
+ pQueryCol1Choice->Append(qualName.GetData());
+ pQueryCol2Choice->Append(qualName.GetData());
+ }
+ else // Single table query, append just the column names
+ {
+ pQueryCol1Choice->Append(colInf[i].colName);
+ pQueryCol2Choice->Append(colInf[i].colName);
+ }
+ }
+
+ pQueryCol1Choice->SetSelection(0);
+ pQueryCol2Choice->SetSelection(0);
+ pQueryOperatorChoice->SetSelection(0);
+
+ pQueryValue2Msg->Show(FALSE);
+ pQueryValue2Txt->Show(FALSE);
+
+ pQueryHintMsg->SetLabel(langQRY_EQ);
+
+ pQuerySqlWhereMtxt->SetValue(pWhere);
+
+ wxEndBusyCursor();
+
+ // Display the dialog window
+ SetModal(TRUE);
+ Centre(wxBOTH);
+ Show(TRUE);
+
+} // CqueryDlg() constructor
+
+
+void CqueryDlg::OnCommand(wxWindow& win, wxCommandEvent& event)
+{
+ // Widget pointers won't be set when the dialog is constructed.
+ // Control is passed through this function once for each widget on
+ // a dialog as the dialog is constructed.
+ if (!widgetPtrsSet)
+ return;
+
+ wxString widgetName = win.GetName();
+
+ // Operator choice box
+ if (widgetName == pQueryOperatorChoice->GetName())
+ {
+ // Set the help text
+ switch((qryOp) pQueryOperatorChoice->GetSelection())
+ {
+ case qryOpEQ:
+ pQueryHintMsg->SetLabel(langQRY_EQ);
+ break;
+ case qryOpLT:
+ pQueryHintMsg->SetLabel(langQRY_LT);
+ break;
+ case qryOpGT:
+ pQueryHintMsg->SetLabel(langQRY_GT);
+ break;
+ case qryOpLE:
+ pQueryHintMsg->SetLabel(langQRY_LE);
+ break;
+ case qryOpGE:
+ pQueryHintMsg->SetLabel(langQRY_GE);
+ break;
+ case qryOpBEGINS:
+ pQueryHintMsg->SetLabel(langQRY_BEGINS);
+ break;
+ case qryOpCONTAINS:
+ pQueryHintMsg->SetLabel(langQRY_CONTAINS);
+ break;
+ case qryOpLIKE:
+ pQueryHintMsg->SetLabel(langQRY_LIKE);
+ break;
+ case qryOpBETWEEN:
+ pQueryHintMsg->SetLabel(langQRY_BETWEEN);
+ break;
+ }
+
+ // Hide the value2 widget
+ pQueryValue2Msg->Show(FALSE); // BETWEEN will show this widget
+ pQueryValue2Txt->Show(FALSE); // BETWEEN will show this widget
+
+ // Disable the NOT operator for <, <=, >, >=
+ switch((qryOp) pQueryOperatorChoice->GetSelection())
+ {
+ case qryOpLT:
+ case qryOpGT:
+ case qryOpLE:
+ case qryOpGE:
+ pQueryNotCheck->SetValue(0);
+ pQueryNotCheck->Enable(FALSE);
+ break;
+ default:
+ pQueryNotCheck->Enable(TRUE);
+ break;
+ }
+
+ // Manipulate the dialog to handle the selected operator
+ switch((qryOp) pQueryOperatorChoice->GetSelection())
+ {
+ case qryOpEQ:
+ case qryOpLT:
+ case qryOpGT:
+ case qryOpLE:
+ case qryOpGE:
+ pQueryCol2Choice->Enable(TRUE);
+ if (pQueryCol2Choice->GetSelection()) // Column name is highlighted
+ {
+ pQueryValue1Msg->Show(FALSE);
+ pQueryValue1Txt->Show(FALSE);
+ }
+ else // "Value" is highlighted
+ {
+ pQueryValue1Msg->Show(TRUE);
+ pQueryValue1Txt->Show(TRUE);
+ pQueryValue1Txt->SetFocus();
+ }
+ break;
+ case qryOpBEGINS:
+ case qryOpCONTAINS:
+ case qryOpLIKE:
+ pQueryCol2Choice->SetSelection(0);
+ pQueryCol2Choice->Enable(FALSE);
+ pQueryValue1Msg->Show(TRUE);
+ pQueryValue1Txt->Show(TRUE);
+ pQueryValue1Txt->SetFocus();
+ break;
+ case qryOpBETWEEN:
+ pQueryCol2Choice->SetSelection(0);
+ pQueryCol2Choice->Enable(FALSE);
+ pQueryValue2Msg->Show(TRUE);
+ pQueryValue2Txt->Show(TRUE);
+ pQueryValue1Msg->Show(TRUE);
+ pQueryValue1Txt->Show(TRUE);
+ pQueryValue1Txt->SetFocus();
+ break;
+ }
+
+ return;
+
+ } // Operator choice box
+
+ // Column 2 choice
+ if (widgetName == pQueryCol2Choice->GetName())
+ {
+ if (pQueryCol2Choice->GetSelection()) // Column name is highlighted
+ {
+ pQueryValue1Msg->Show(FALSE);
+ pQueryValue1Txt->Show(FALSE);
+ }
+ else // "Value" is highlighted
+ {
+ pQueryValue1Msg->Show(TRUE);
+ pQueryValue1Txt->Show(TRUE);
+ pQueryValue1Txt->SetFocus();
+ }
+ return;
+
+ } // Column 2 choice
+
+ // Add button
+ if (widgetName == pQueryAddBtn->GetName())
+ {
+ ProcessAddBtn();
+ return;
+
+ } // Add button
+
+ // And button
+ if (widgetName == pQueryAndBtn->GetName())
+ {
+ AppendToWhere(" AND\n");
+ return;
+
+ } // And button
+
+ // Or button
+ if (widgetName == pQueryOrBtn->GetName())
+ {
+ AppendToWhere(" OR\n");
+ return;
+
+ } // Or button
+
+ // Left Paren button
+ if (widgetName == pQueryLParenBtn->GetName())
+ {
+ AppendToWhere("(");
+ return;
+
+ } // Left Paren button
+
+ // Right paren button
+ if (widgetName == pQueryRParenBtn->GetName())
+ {
+ AppendToWhere(")");
+ return;
+
+ } // Right Paren button
+
+ // Done button
+ if (widgetName == pQueryDoneBtn->GetName())
+ {
+ // Be sure the where clause will not overflow the output buffer
+ if (strlen(pQuerySqlWhereMtxt->GetValue()) > DB_MAX_WHERE_CLAUSE_LEN)
+ {
+ wxString s;
+ s.sprintf("Maximum where clause length exceeded.\nLength must be less than %d", DB_MAX_WHERE_CLAUSE_LEN+1);
+ wxMessageBox(s.GetData(),"Error...",wxOK | wxICON_EXCLAMATION);
+ return;
+ }
+ // Validate the where clause for things such as matching parens
+ if (!ValidateWhereClause())
+ return;
+ // Copy the where clause to the output buffer and exit
+ strcpy(pWhere, pQuerySqlWhereMtxt->GetValue());
+ Close();
+ return;
+
+ } // Done button
+
+ // Clear button
+ if (widgetName == pQueryClearBtn->GetName())
+ {
+ Bool Ok = (wxMessageBox("Are you sure you wish to clear the Query?","Confirm",wxYES_NO|wxICON_QUESTION) == wxYES);
+
+ if (Ok)
+ pQuerySqlWhereMtxt->SetValue("");
+ return;
+
+ } // Clear button
+
+ // Count button
+ if (widgetName == pQueryCountBtn->GetName())
+ {
+ wxBeginBusyCursor();
+ ProcessCountBtn();
+ wxEndBusyCursor();
+ return;
+
+ } // Count button
+
+} // CqueryDlg::OnCommand
+
+
+Bool CqueryDlg::OnClose()
+{
+ // Clean up
+ if (colInf)
+ {
+ delete [] colInf;
+ colInf = 0;
+ }
+
+ if (dbTable)
+ {
+ delete dbTable;
+ dbTable = 0;
+ }
+
+ GetParent()->SetFocus();
+ wxEndBusyCursor();
+ return TRUE;
+
+} // CqueryDlg::OnClose()
+
+/*
+Bool CqueryDlg::SetWidgetPtrs()
+{
+ Bool abort = FALSE;
+
+ abort = abort || !(pQueryCol1Choice = (wxChoice *)GetWidgetPtr("QueryCol1Choice",this));
+ abort = abort || !(pQueryNotCheck = (wxCheckBox *)GetWidgetPtr("QueryNotCheck",this));
+ abort = abort || !(pQueryOperatorChoice = (wxChoice *)GetWidgetPtr("QueryOperatorChoice",this));
+ abort = abort || !(pQueryCol2Choice = (wxChoice *)GetWidgetPtr("QueryCol2Choice",this));
+ abort = abort || !(pQueryValue1Txt = (wxText *)GetWidgetPtr("QueryValue1Txt",this));
+ abort = abort || !(pQueryValue2Txt = (wxText *)GetWidgetPtr("QueryValue2Txt",this));
+ abort = abort || !(pQuerySqlWhereMtxt = (wxMultiText *)GetWidgetPtr("QuerySqlWhereMtxt",this));
+ abort = abort || !(pQueryAddBtn = (wxButton *)GetWidgetPtr("QueryAddBtn",this));
+ abort = abort || !(pQueryAndBtn = (wxButton *)GetWidgetPtr("QueryAndBtn",this));
+ abort = abort || !(pQueryOrBtn = (wxButton *)GetWidgetPtr("QueryOrBtn",this));
+ abort = abort || !(pQueryLParenBtn = (wxButton *)GetWidgetPtr("QueryLParenBtn",this));
+ abort = abort || !(pQueryRParenBtn = (wxButton *)GetWidgetPtr("QueryRParenBtn",this));
+ abort = abort || !(pQueryDoneBtn = (wxButton *)GetWidgetPtr("QueryDoneBtn",this));
+ abort = abort || !(pQueryClearBtn = (wxButton *)GetWidgetPtr("QueryClearBtn",this));
+ abort = abort || !(pQueryCountBtn = (wxButton *)GetWidgetPtr("QueryCountBtn",this));
+ abort = abort || !(pQueryHelpBtn = (wxButton *)GetWidgetPtr("QueryHelpBtn",this));
+ abort = abort || !(pQueryHintMsg = (wxMessage *)GetWidgetPtr("QueryHintMsg",this));
+
+ pFocusTxt = NULL;
+
+ return(widgetPtrsSet = !abort);
+
+} // CqueryDlg::SetWidgetPtrs
+*/
+
+void CqueryDlg::AppendToWhere(char *s)
+{
+ wxString whereStr = pQuerySqlWhereMtxt->GetValue();
+ whereStr += s;
+ pQuerySqlWhereMtxt->SetValue(whereStr.GetData());
+
+} // CqueryDlg::AppendToWhere()
+
+
+void CqueryDlg::ProcessAddBtn()
+{
+ qryOp oper = (qryOp) pQueryOperatorChoice->GetSelection();
+
+ // Verify that eveything is filled in correctly
+ if (pQueryCol2Choice->GetSelection() == 0) // "Value" is selected
+ {
+ // Verify that value 1 is filled in
+ if (strlen(pQueryValue1Txt->GetValue()) == 0)
+ {
+ wxBell();
+ pQueryValue1Txt->SetFocus();
+ return;
+ }
+ // For the BETWEEN operator, value 2 must be filled in as well
+ if (oper == qryOpBETWEEN &&
+ strlen(pQueryValue2Txt->GetValue()) == 0)
+ {
+ wxBell();
+ pQueryValue2Txt->SetFocus();
+ return;
+ }
+ }
+
+ // Build the expression and append it to the where clause window
+ wxString s = pQueryCol1Choice->GetStringSelection();
+
+ if (pQueryNotCheck->GetValue() && (oper != qryOpEQ))
+ s += " NOT";
+
+ switch(oper)
+ {
+ case qryOpEQ:
+ if (pQueryNotCheck->GetValue()) // NOT box is checked
+ s += " <>";
+ else
+ s += " =";
+ break;
+ case qryOpLT:
+ s += " <";
+ break;
+ case qryOpGT:
+ s += " >";
+ break;
+ case qryOpLE:
+ s += " <=";
+ break;
+ case qryOpGE:
+ s += " >=";
+ break;
+ case qryOpBEGINS:
+ case qryOpCONTAINS:
+ case qryOpLIKE:
+ s += " LIKE";
+ break;
+ case qryOpBETWEEN:
+ s += " BETWEEN";
+ break;
+ }
+
+ s += " ";
+
+ int col1Idx = pQueryCol1Choice->GetSelection();
+
+ Bool quote = FALSE;
+ if (colInf[col1Idx].sqlDataType == SQL_VARCHAR ||
+ oper == qryOpBEGINS ||
+ oper == qryOpCONTAINS ||
+ oper == qryOpLIKE)
+ quote = TRUE;
+
+ if (pQueryCol2Choice->GetSelection()) // Column name
+ s += pQueryCol2Choice->GetStringSelection();
+ else // Column 2 is a "value"
+ {
+ if (quote)
+ s += "'";
+ if (oper == qryOpCONTAINS)
+ s += "%";
+ s += pQueryValue1Txt->GetValue();
+ if (oper == qryOpCONTAINS || oper == qryOpBEGINS)
+ s += "%";
+ if (quote)
+ s += "'";
+ }
+
+ if (oper == qryOpBETWEEN)
+ {
+ s += " AND ";
+ if (quote)
+ s += "'";
+ s += pQueryValue2Txt->GetValue();
+ if (quote)
+ s += "'";
+ }
+
+ AppendToWhere(s.GetData());
+
+} // CqueryDlg::ProcessAddBtn()
+
+
+void CqueryDlg::ProcessCountBtn()
+{
+ if (!ValidateWhereClause())
+ return;
+
+ if (dbTable == 0) // wxTable object needs to be created and opened
+ {
+ if (!(dbTable = new wxTable(pDB, masterTableName, 0)))
+ {
+ wxMessageBox("Memory allocation failed creating a wxTable object.","Error...",wxOK | wxICON_EXCLAMATION);
+ return;
+ }
+ if (!dbTable->Open())
+ {
+ wxString tStr;
+ tStr = "ODBC error during Open()\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ return;
+ }
+ }
+
+ // Count() with WHERE clause
+ dbTable->where = pQuerySqlWhereMtxt->GetValue();
+ ULONG whereCnt = dbTable->Count();
+
+ // Count() of all records in the table
+ dbTable->where = 0;
+ ULONG totalCnt = dbTable->Count();
+
+ if (whereCnt > 0 || totalCnt == 0)
+ {
+ wxString tStr;
+ tStr.sprintf("%lu of %lu records match the query criteria.",whereCnt,totalCnt);
+ wxMessageBox(tStr.GetData(),"Notice...",wxOK | wxICON_INFORMATION);
+ }
+ else
+ {
+ wxString tStr;
+ tStr.sprintf("%lu of %lu records match the query criteria.\n\nEither the criteria entered produced a result set\nwith no records, or there was a syntactical error\nin the clause you entered.\n\nPress the details button to see if any database errors were reported.",whereCnt,totalCnt);
+ wxMessageBox(tStr.GetData(),"Notice...",wxOK | wxICON_INFORMATION);
+ }
+
+ // After a wxMessageBox, the focus does not necessarily return to the
+ // window which was the focus when the message box popped up, so return
+ // focus to the Query dialog for certain
+ SetFocus();
+
+} // CqueryDlg::ProcessCountBtn()
+
+
+Bool CqueryDlg::ValidateWhereClause()
+{
+ wxString where = pQuerySqlWhereMtxt->GetValue();
+
+ if (where.Freq('(') != where.Freq(')'))
+ {
+ wxMessageBox("There are mismatched parenthesis in the constructed where clause","Error...",wxOK | wxICON_EXCLAMATION);
+ return(FALSE);
+ }
+ // After a wxMessageBox, the focus does not necessarily return to the
+ // window which was the focus when the message box popped up, so return
+ // focus to the Query dialog for certain
+ SetFocus();
+
+ return(TRUE);
+
+} // CqueryDlg::ValidateWhereClause()
--- /dev/null
+NAME DBTEST
+DESCRIPTION 'Database wxWindows application'
+EXETYPE WINDOWS
+STUB 'WINSTUB.EXE'
+CODE PRELOAD MOVEABLE DISCARDABLE
+DATA PRELOAD MOVEABLE MULTIPLE
+HEAPSIZE 1024
+STACKSIZE 8192
--- /dev/null
+///////////////////////////////////////////////////////////////////////////////
+// Name: dbtest.h
+// Purpose: wxWindows database demo app
+// Author: George Tasker
+// Modified by:
+// Created: 1998
+// RCS-ID: $Id$
+// Copyright: (c) 1998 Remstar International, Inc.
+// Licence: wxWindows licence
+///////////////////////////////////////////////////////////////////////////////
+
+#pragma interface "dbtest.h"
+
+#include <wx/string.h>
+#include <wx/dbtable.h>
+
+enum DialogModes {mView,mCreate,mEdit,mSearch};
+
+// ID for the menu quit command
+#define FILE_CREATE 100
+#define FILE_EXIT 199
+#define EDIT_PARAMETERS 200
+#define ABOUT_DEMO 300
+
+
+// Name of the table to be created/opened
+const char CONTACT_TABLE_NAME[] = "CONTACTS";
+
+// Nuber of columns in the above table
+const int CONTACT_NO_COLS = 12; // 0-11
+
+// Global structure for holding ODBC connection information
+struct DbStuff DbConnectInf;
+
+enum Language {langENGLISH, langFRENCH, langGERMAN, langSPANISH, langOTHER};
+
+// Forward class declarations
+class CeditorDlg;
+class CparameterDlg;
+
+const char paramFilename[] = "database.cfg";
+
+
+/*
+ * This class contains the actual data members that are used for transferring
+ * data back and forth from the database to the program.
+ *
+ * NOTE: The object described in this class is just for example purposes, and has no
+ * real meaning other than to show each type of field being used by the database
+ */
+class CstructContact : public wxObject
+{
+ public:
+ char Name[ 50+1 ]; // Contact's name
+ char Addr1[ 50+1 ];
+ char Addr2[ 50+1 ];
+ char City[ 25+1 ];
+ char State[ 25+1 ];
+ char PostalCode[ 15+1 ];
+ char Country[ 20+1 ];
+ TIMESTAMP_STRUCT JoinDate; // Date on which this person joined the wxWindows project
+ Language NativeLanguage; // Enumerated type indicating person's native language
+ bool IsDeveloper; // Is this person a developer for wxWindows, or just a subscriber
+ int Contributions; // Something to show off an integer field
+ ULONG LinesOfCode; // Something to show off a 'long' field
+}; // CstructContact
+
+
+//
+// NOTE: Ccontact inherits wxTable, which gives access to all the database functionality
+//
+class Ccontact : public wxTable, public CstructContact
+{
+ private:
+ bool freeDbConn;
+ void SetupColumns();
+
+ public:
+ wxString whereStr;
+ wxString qryWhereStr; // Where string returned from the query dialog
+
+ Ccontact(wxDB *pwxDB=NULL);
+ ~Ccontact();
+
+ void Initialize();
+ bool CreateIndexes(void);
+ bool FetchByName(char *name);
+
+}; // Ccontact class definition
+
+
+typedef struct Cparameters
+{
+ // The length of these strings were arbitrarily picked, and are
+ // dependent on the OS and database engine you will be using.
+ char ODBCSource[100+1];
+ char UserName[25+1];
+ char Password[25+1];
+} Cparameters;
+
+
+// Define a new application type
+class DatabaseDemoApp: public wxApp
+{
+ public:
+ Cparameters params;
+ wxFrame *OnInit(void);
+}; // DatabaseDemoApp
+
+DECLARE_APP(DatabaseDemoApp)
+
+// Define a new frame type
+class DatabaseDemoFrame: public wxFrame
+{
+ private:
+ CeditorDlg *pEditorDlg;
+ CparameterDlg *pParamDlg;
+
+ public:
+ DatabaseDemoFrame(wxFrame *frame, char *title, int x, int y, int w, int h);
+
+ void OnMenuCommand(int id);
+ bool OnClose(void);
+
+ void CreateDataTable();
+ void BuildEditorDialog();
+ void BuildParameterDialog(wxWindow *parent);
+}; // DatabaseDemoFrame
+
+
+
+// *************************** CeditorDlg ***************************
+
+class CeditorDlg : public wxPanel
+{
+ private:
+ bool widgetPtrsSet;
+ wxString saveName;
+
+ // Pointers to all widgets on the dialog
+ wxButton *pCreateBtn, *pEditBtn, *pDeleteBtn, *pCopyBtn, *pSaveBtn, *pCancelBtn;
+ wxButton *pPrevBtn, *pNextBtn, *pQueryBtn, *pResetBtn, *pDoneBtn, *pHelpBtn;
+ wxButton *pNameListBtn;
+ wxText *pNameTxt, *pAddress1Txt, *pAddress2Txt,*pCityTxt, *pStateTxt, *pCountryTxt,*pPostalCodeTxt;
+ wxMessage *pNameMsg, *pAddress1Msg, *pAddress2Msg,*pCityMsg, *pStateMsg, *pCountryMsg,*pPostalCodeMsg;
+ wxText *pJoinDateTxt,*pContribTxt, *pLinesTxt;
+ wxMessage *pJoinDateMsg,*pContribMsg, *pLinesMsg;
+ wxRadioBox *pDeveloperRadio;
+ wxChoice *pNativeLangChoice;
+ wxMessage *pNativeLangMsg;
+
+ public:
+ enum DialogModes mode;
+ Ccontact *Contact; // this is the table object that will be being manipulated
+
+ CeditorDlg(wxWindow *parent);
+ bool OnClose(void);
+ void OnCommand(wxWindow& win, wxCommandEvent& event);
+ void OnActivate(bool) {}; // necessary for hot keys
+
+ void FieldsEditable();
+ void SetMode(enum DialogModes m);
+ bool PutData();
+ bool GetData();
+ bool Save();
+ bool GetNextRec();
+ bool GetPrevRec();
+ bool GetRec(char *whereStr);
+}; // CeditorDlg
+
+
+// *************************** CparameterDlg ***************************
+
+class CparameterDlg : public wxDialogBox
+{
+ private:
+ bool widgetPtrsSet;
+ enum DialogModes mode;
+ bool saved;
+ Cparameters savedParamSettings;
+
+ // Pointers to all widgets on the dialog
+ wxMessage *pParamODBCSourceMsg;
+ wxListBox *pParamODBCSourceList;
+ wxMessage *pParamUserNameMsg, *pParamPasswordMsg;
+ wxText *pParamUserNameTxt, *pParamPasswordTxt;
+ wxButton *pParamSaveBtn, *pParamCancelBtn;
+
+ public:
+ CparameterDlg(wxWindow *parent);
+ bool OnClose(void);
+ void OnCommand(wxWindow& win, wxCommandEvent& event);
+ void OnActivate(bool) {}; // necessary for hot keys
+
+ bool PutData();
+ bool GetData();
+ bool Save();
+ void FillDataSourceList();
+
+}; // CparameterDlg
+
+
+// *************************** CqueryDlg ***************************
+
+
+// QUERY DIALOG
+enum qryOp
+{
+ qryOpEQ,
+ qryOpLT,
+ qryOpGT,
+ qryOpLE,
+ qryOpGE,
+ qryOpBEGINS,
+ qryOpCONTAINS,
+ qryOpLIKE,
+ qryOpBETWEEN
+};
+
+
+// Query strings
+char * const langQRY_EQ = "column = column | value";
+char * const langQRY_LT = "column < column | value";
+char * const langQRY_GT = "column > column | value";
+char * const langQRY_LE = "column <= column | value";
+char * const langQRY_GE = "column >= column | value";
+char * const langQRY_BEGINS = "columns that BEGIN with the string entered";
+char * const langQRY_CONTAINS = "columns that CONTAIN the string entered";
+char * const langQRY_LIKE = "% matches 0 or more of any char; _ matches 1 char";
+char * const langQRY_BETWEEN = "column BETWEEN value AND value";
+
+
+class CqueryDlg : public wxDialogBox
+{
+ private:
+ CcolInf *colInf; // Column inf. returned by db->GetColumns()
+ wxTable *dbTable;
+ char *masterTableName;
+ char *pWhere; // A pointer to the storage for the resulting where clause
+ wxDB *pDB;
+
+ public:
+ bool widgetPtrsSet;
+
+ // Widget pointers
+ wxMessage *pQueryCol1Msg;
+ wxChoice *pQueryCol1Choice;
+ wxMessage *pQueryNotMsg;
+ wxCheckBox *pQueryNotCheck;
+ wxMessage *pQueryOperatorMsg;
+ wxChoice *pQueryOperatorChoice;
+ wxMessage *pQueryCol2Msg;
+ wxChoice *pQueryCol2Choice;
+ wxMessage *pQueryValue1Msg;
+ wxText *pQueryValue1Txt;
+ wxMessage *pQueryValue2Msg;
+ wxText *pQueryValue2Txt;
+ wxMessage *pQuerySqlWhereMsg;
+ wxMultiText *pQuerySqlWhereMtxt;
+ wxButton *pQueryAddBtn;
+ wxButton *pQueryAndBtn;
+ wxButton *pQueryOrBtn;
+ wxButton *pQueryLParenBtn;
+ wxButton *pQueryRParenBtn;
+ wxButton *pQueryDoneBtn;
+ wxButton *pQueryClearBtn;
+ wxButton *pQueryCountBtn;
+ wxButton *pQueryHelpBtn;
+ wxGroupBox *pQueryHintGrp;
+ wxMessage *pQueryHintMsg;
+
+ wxText *pFocusTxt;
+
+ CqueryDlg(wxWindow *parent, wxDB *pDb, char *tblName[], char *pWhereArg);
+
+ void OnCommand(wxWindow& win, wxCommandEvent& event);
+ bool OnClose();
+ void OnActivate(bool) {}; // necessary for hot keys
+
+// bool SetWidgetPtrs();
+ void AppendToWhere(char *s);
+ void ProcessAddBtn();
+ void ProcessCountBtn();
+ bool ValidateWhereClause();
+
+}; // CqueryDlg
--- /dev/null
+db_icon ICON "dbtest.ico"
+#include "wx.rc"
+
--- /dev/null
+///////////////////////////////////////////////////////////////////////////////
+// Name: listdb.cpp
+// Purpose: Data table lookup listbox code
+// Author: George Tasker/Doug Card
+// Modified by:
+// Created: 1996
+// RCS-ID: $Id$
+// Copyright: (c) 1996 Remstar International, Inc.
+// Licence: wxWindows licence
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+// SYNOPSIS START
+
+ Member functions for the classes defined in LISTDB.H
+
+ This class is used to present a generic ListBox lookup window for
+ use with any of the object creation/selection choice widgets. This
+ dialog window will present a (possibly) scrolling list of values
+ that come from a data table source. Based on the object type passed
+ in the constructor, a ListBox is built to present the user with a
+ single selection listbox.
+
+ The string selected from the list box is stored in the Global variable
+ "ListDB_Seclection", and will remain set until another interation of this
+ routine is called.
+
+ For each object (database) type that is to be used, an overridden
+ constructor should be written to appropriately link to the proper
+ data table/object for building the list.
+
+ The data table record access is all handled through the routines
+ in this module, interfacing with the methods defined in wxTable.
+
+ All objects which use data table access must be initialized and
+ have opened the table prior to passing them in the dialog
+ constructor, and the 'where' query should already have been set
+ and performed before creating this dialog instance.
+
+// SYNOPSIS STOP
+*/
+
+#ifdef __GNUG__
+#pragma implementation "listdb.h"
+#endif
+
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif //__BORLANDC__
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif //WX_PRECOMP
+
+#include <wx/dbtable.h>
+
+#include "listdb.h"
+
+// Global structure for holding ODBC connection information
+extern DbStuff DbConnectInf;
+
+// Global database connection
+extern wxDB *READONLY_DB;
+
+
+// Used for passing the selected listbox selection back to the calling
+// routine. This variable must be declared as 'extern' in the calling
+// source module
+char ListDB_Selection[LOOKUP_COL_LEN+1];
+
+// If the listbox contains two columns of data, the second column is
+// returned in this variable.
+char ListDB_Selection2[LOOKUP_COL_LEN+1];
+
+// Constants
+const LISTDB_NO_SPACES_BETWEEN_COLS = 3;
+
+
+// Clookup constructor
+Clookup::Clookup(char *tblName, char *colName) : wxTable(READONLY_DB, tblName, 1)
+{
+
+ SetColDefs (0, colName, DB_DATA_TYPE_VARCHAR, lookupCol, SQL_C_CHAR, LOOKUP_COL_LEN+1, FALSE, FALSE);
+
+} // Clookup()
+
+
+// Clookup2 constructor
+Clookup2::Clookup2(char *tblName, char *colName1, char *colName2, wxDB *pDb)
+ : wxTable(pDb, tblName, (1 + (strlen(colName2) > 0)))
+{
+ int i = 0;
+
+ SetColDefs (i, colName1, DB_DATA_TYPE_VARCHAR, lookupCol1, SQL_C_CHAR, LOOKUP_COL_LEN+1, FALSE, FALSE);
+
+ if (strlen(colName2) > 0)
+ SetColDefs (++i, colName2, DB_DATA_TYPE_VARCHAR, lookupCol2, SQL_C_CHAR, LOOKUP_COL_LEN+1, FALSE, FALSE);
+
+} // Clookup2()
+
+
+// This is a generic lookup constructor that will work with any table and any column
+ClookUpDlg::ClookUpDlg(wxWindow *parent, char *windowTitle, char *tableName, char *colName,
+ char *where, char *orderBy) : wxDialogBox (parent, "Select...", 1, -1, -1, 400, 290)
+{
+ wxBeginBusyCursor();
+
+ strcpy(ListDB_Selection,"");
+ widgetPtrsSet = FALSE;
+ lookup = 0;
+ lookup2 = 0;
+ noDisplayCols = 1;
+ col1Len = 0;
+
+ // Build the dialog
+ SetLabelPosition(wxVERTICAL);
+
+ wxFont *ButtonFont = new wxFont(12,wxSWISS,wxNORMAL,wxBOLD);
+ wxFont *TextFont = new wxFont(12,wxSWISS,wxNORMAL,wxNORMAL);
+
+ SetButtonFont(ButtonFont);
+ SetLabelFont(TextFont);
+ SetLabelPosition(wxVERTICAL);
+
+ pLookUpSelectList = new wxListBox(this, NULL, "", wxSINGLE|wxALWAYS_SB, 5, 15, 384, 195, 0, 0, 0, "LookUpSelectList");
+ pLookUpOkBtn = new wxButton(this, NULL, "&Ok", 113, 222, 70, 35, 0, "LookUpOkBtn");
+ pLookUpCancelBtn = new wxButton(this, NULL, "C&ancel", 212, 222, 70, 35, 0, "LookUpCancelBtn");
+
+ widgetPtrsSet = TRUE;
+
+ // Query the lookup table and display the result set
+ if (!(lookup = new Clookup(tableName, colName)))
+ {
+ wxMessageBox("Error allocating memory for 'Clookup'object.","Error...");
+ Close();
+ return;
+ }
+
+ if (!lookup->Open())
+ {
+ wxString tStr;
+ tStr.sprintf("Unable to open the table '%s'.",tableName);
+ wxMessageBox(tStr.GetData(),"ODBC Error...");
+ Close();
+ return;
+ }
+
+ lookup->orderBy = orderBy;
+ lookup->where = where;
+ if (!lookup->Query())
+ {
+ wxMessageBox("ODBC error during Query()","ODBC Error...");
+ Close();
+ return;
+ }
+
+ // Fill in the list box from the query result set
+ while (lookup->GetNext())
+ pLookUpSelectList->Append(lookup->lookupCol);
+
+ // Highlight the first list item
+ pLookUpSelectList->SetSelection(0);
+
+ // Make the OK activate by pressing Enter
+ if (pLookUpSelectList->Number())
+ pLookUpOkBtn->SetDefault();
+ else
+ {
+ pLookUpCancelBtn->SetDefault();
+ pLookUpOkBtn->Enable(FALSE);
+ }
+
+ // Display the dialog window
+ SetTitle(windowTitle);
+ Centre(wxBOTH);
+ wxEndBusyCursor();
+ Show(TRUE);
+
+} // Generic lookup constructor
+
+
+//
+// This is a generic lookup constructor that will work with any table and any column.
+// It extends the capabilites of the lookup dialog in the following ways:
+//
+// 1) 2 columns rather than one
+// 2) The ability to select DISTINCT column values
+//
+// Only set distinctValues equal to true if necessary. In many cases, the constraints
+// of the index(es) will enforce this uniqueness. Selecting DISTINCT does require
+// overhead by the database to ensure that all values returned are distinct. Therefore,
+// use this ONLY when you need it.
+//
+// For complicated queries, you can pass in the sql select statement. This would be
+// necessary if joins are involved since by default both columns must come from the
+// same table.
+//
+// If you do query by sql statement, you must pass in the maximum lenght of column1,
+// since it cannot be derived when you query using your own sql statement.
+//
+// The optional database connection can be used if you'd like the lookup class
+// to use a database pointer other than the global READONLY_DB. This is necessary if
+// records are being saved, but not committed to the db, yet should be included
+// in the lookup window.
+//
+ClookUpDlg::ClookUpDlg(wxWindow *parent, char *windowTitle, char *tableName,
+ char *dispCol1, char *dispCol2, char *where, char *orderBy, bool distinctValues,
+ char *selectStmt, int maxLenCol1, wxDB *pDb, bool allowOk) : wxDialogBox (parent, "Select...", 1, -1, -1, 400, 290)
+{
+ wxBeginBusyCursor();
+
+ strcpy(ListDB_Selection,"");
+ strcpy(ListDB_Selection2,"");
+ widgetPtrsSet = FALSE;
+ lookup = 0;
+ lookup2 = 0;
+ noDisplayCols = (strlen(dispCol2) ? 2 : 1);
+ col1Len = 0;
+
+ // Build the dialog
+ SetLabelPosition(wxVERTICAL);
+
+ wxFont *ButtonFont = new wxFont(12,wxSWISS,wxNORMAL,wxBOLD);
+ wxFont *TextFont = new wxFont(12,wxSWISS,wxNORMAL,wxNORMAL);
+ wxFont *FixedFont = new wxFont(12,wxMODERN,wxNORMAL,wxNORMAL);
+
+ SetButtonFont(ButtonFont);
+ SetLabelFont(TextFont);
+ SetLabelPosition(wxVERTICAL);
+
+ // this is done with fixed font so that the second column (if any) will be left
+ // justified in the second column
+ SetButtonFont(FixedFont);
+ pLookUpSelectList = new wxListBox(this, NULL, "", wxSINGLE|wxALWAYS_SB, 5, 15, 384, 195, 0, 0, 0, "LookUpSelectList");
+ SetButtonFont(ButtonFont);
+ pLookUpOkBtn = new wxButton(this, NULL, "&Ok", 113, 222, 70, 35, 0, "LookUpOkBtn");
+ pLookUpCancelBtn = new wxButton(this, NULL, "C&ancel", 212, 222, 70, 35, 0, "LookUpCancelBtn");
+
+ widgetPtrsSet = TRUE;
+
+ // Query the lookup table and display the result set
+ if (!(lookup2 = new Clookup2(tableName, dispCol1, dispCol2, pDb)))
+ {
+ wxMessageBox("Error allocating memory for 'Clookup2'object.","Error...");
+ Close();
+ return;
+ }
+
+ if (!lookup2->Open())
+ {
+ wxString tStr;
+ tStr.sprintf("Unable to open the table '%s'.",tableName);
+ wxMessageBox(tStr.GetData(),"ODBC Error...");
+ Close();
+ return;
+ }
+
+ // If displaying 2 columns, determine the maximum length of column1
+ int maxColLen;
+ if (maxLenCol1)
+ maxColLen = col1Len = maxLenCol1; // user passed in max col length for column 1
+ else
+ {
+ maxColLen = LOOKUP_COL_LEN;
+ if (strlen(dispCol2))
+ {
+ wxString q = "SELECT MAX({fn LENGTH(";
+ q += dispCol1;
+ q += ")}), NULL";
+ q += " FROM ";
+ q += tableName;
+ if (strlen(where))
+ {
+ q += " WHERE ";
+ q += where;
+ }
+ if (!lookup2->QueryBySqlStmt(q.GetData()))
+ {
+ wxMessageBox("ODBC error during QueryBySqlStmt()","ODBC Error...");
+ Close();
+ return;
+ }
+ if (lookup2->GetNext())
+ maxColLen = col1Len = atoi(lookup2->lookupCol1);
+ else
+ wxMessageBox("ODBC error during GetNext()","ODBC Error...");
+ }
+ }
+
+ // Query the actual record set
+ if (selectStmt && strlen(selectStmt)) // Query by sql stmt passed in
+ {
+ if (!lookup2->QueryBySqlStmt(selectStmt))
+ {
+ wxMessageBox("ODBC error during QueryBySqlStmt()","ODBC Error...");
+ Close();
+ return;
+ }
+ }
+ else // Query using where and order by clauses
+ {
+ lookup2->orderBy = orderBy;
+ lookup2->where = where;
+ if (!lookup2->Query(FALSE, distinctValues))
+ {
+ wxMessageBox("ODBC error during Query()","ODBC Error...");
+ Close();
+ return;
+ }
+ }
+
+ // Fill in the list box from the query result set
+ wxString s;
+ while (lookup2->GetNext())
+ {
+ s = lookup2->lookupCol1;
+ if (strlen(dispCol2)) // Append the optional column 2
+ {
+ s.Append(' ', (maxColLen + LISTDB_NO_SPACES_BETWEEN_COLS - strlen(lookup2->lookupCol1)));
+ s.Append(lookup2->lookupCol2);
+ }
+ pLookUpSelectList->Append(s.GetData());
+ }
+
+ // Highlight the first list item
+ pLookUpSelectList->SetSelection(0);
+
+ // Make the OK activate by pressing Enter
+ if (pLookUpSelectList->Number())
+ pLookUpOkBtn->SetDefault();
+ else
+ {
+ pLookUpCancelBtn->SetDefault();
+ pLookUpOkBtn->Enable(FALSE);
+ }
+
+ pLookUpOkBtn->Enable(allowOk);
+
+ // Display the dialog window
+ SetTitle(windowTitle);
+ Centre(wxBOTH);
+ wxEndBusyCursor();
+ Show(TRUE);
+
+} // Generic lookup constructor 2
+
+
+bool ClookUpDlg::OnClose(void)
+{
+ widgetPtrsSet = FALSE;
+ GetParent()->Enable(TRUE);
+
+ if (lookup)
+ delete lookup;
+ if (lookup2)
+ delete lookup2;
+
+ wxEndBusyCursor();
+ return TRUE;
+
+} // ClookUpDlg::OnClose
+
+
+void ClookUpDlg::OnCommand(wxWindow& win, wxCommandEvent& event)
+{
+ wxString widgetName = win.GetName();
+
+ if (widgetPtrsSet)
+ {
+ // OK Button
+ if (widgetName == pLookUpOkBtn->GetName())
+ {
+ if (pLookUpSelectList->GetSelection() != -1)
+ {
+ if (noDisplayCols == 1)
+ strcpy (ListDB_Selection, pLookUpSelectList->GetStringSelection());
+ else // 2 display columns
+ {
+ wxString s = pLookUpSelectList->GetStringSelection();
+ // Column 1
+ s = s.SubString(0, col1Len-1);
+ s = s.Strip();
+ strcpy(ListDB_Selection, s.GetData());
+ // Column 2
+ s = pLookUpSelectList->GetStringSelection();
+ s = s.From(col1Len + LISTDB_NO_SPACES_BETWEEN_COLS);
+ s = s.Strip();
+ strcpy(ListDB_Selection2, s.GetData());
+ }
+ }
+ else
+ {
+ strcpy(ListDB_Selection,"");
+ strcpy(ListDB_Selection2,"");
+ }
+ Close();
+ } // OK Button
+
+ // Cancel Button
+ if (widgetName == pLookUpCancelBtn->GetName())
+ {
+ strcpy (ListDB_Selection,"");
+ strcpy (ListDB_Selection2,"");
+ Close();
+ } // Cancel Button
+ }
+
+}; // ClookUpDlg::OnCommand
+
+// *********************************** listdb.cpp **********************************
--- /dev/null
+///////////////////////////////////////////////////////////////////////////////
+// Name: listdb.h
+// Purpose: wxWindows database demo app
+// Author: George Tasker
+// Modified by:
+// Created: 1996
+// RCS-ID: $Id$
+// Copyright: (c) 1996 Remstar International, Inc.
+// Licence: wxWindows licence
+///////////////////////////////////////////////////////////////////////////////
+
+#pragma interface "listdb.h"
+
+/*
+/*
+// SYNOPSIS START
+
+ Contains dialog class for creating a data table lookup listbox
+
+// SYNOPSIS STOP
+*/
+
+#ifndef LISTDB_DOT_H
+#define LISTDB_DOT_H
+
+
+#include <wx/dbtable.h>
+
+const LOOKUP_COL_LEN = 250;
+
+// Global database connection
+extern wxDB *READONLY_DB;
+
+// Clookup class
+class Clookup : public wxTable
+{
+ public:
+
+ char lookupCol[LOOKUP_COL_LEN+1];
+
+ Clookup(char *tblName, char *colName);
+
+}; // Clookup
+
+// Clookup2 class
+class Clookup2 : public wxTable
+{
+ public:
+
+ char lookupCol1[LOOKUP_COL_LEN+1];
+ char lookupCol2[LOOKUP_COL_LEN+1];
+
+ Clookup2(char *tblName, char *colName1, char *colName2, wxDB *pDb);
+
+}; // Clookup2
+
+class ClookUpDlg : public wxDialogBox
+{
+ private:
+ bool widgetPtrsSet;
+ int currentCursor;
+ Clookup *lookup;
+ Clookup2 *lookup2;
+ int noDisplayCols;
+ int col1Len;
+
+ wxListBox *pLookUpSelectList;
+ wxButton *pLookUpOkBtn;
+ wxButton *pLookUpCancelBtn;
+
+ public:
+
+ // This is a generic lookup constructor that will work with any table and any column
+ ClookUpDlg(wxWindow *parent,
+ char *windowTitle,
+ char *tableName,
+ char *colName,
+ char *where,
+ char *orderBy);
+
+ //
+ // This is a generic lookup constructor that will work with any table and any column.
+ // It extends the capabilites of the lookup dialog in the following ways:
+ //
+ // 1) 2 columns rather than one
+ // 2) The ability to select DISTINCT column values
+ //
+ // Only set distinctValues equal to true if necessary. In many cases, the constraints
+ // of the index(es) will enforce this uniqueness. Selecting DISTINCT does require
+ // overhead by the database to ensure that all values returned are distinct. Therefore,
+ // use this ONLY when you need it.
+ //
+ // For complicated queries, you can pass in the sql select statement. This would be
+ // necessary if joins are involved since by default both columns must come from the
+ // same table.
+ //
+ // If you do query by sql statement, you must pass in the maximum length of column1,
+ // since it cannot be derived when you query using your own sql statement.
+ //
+ // The optional database connection can be used if you'd like the lookup class
+ // to use a database pointer other than the global READONLY_DB. This is necessary if
+ // records are being saved, but not committed to the db, yet should be included
+ // in the lookup window.
+ //
+ ClookUpDlg(wxWindow *parent,
+ char *windowTitle,
+ char *tableName,
+ char *dispCol1, // Must have at least 1 display column
+ char *dispCol2, // Optional
+ char *where,
+ char *orderBy,
+ bool distinctValues, // e.g. SELECT DISTINCT ...
+ char *selectStmt = 0, // If you wish to query by SQLstmt (complicated lookups)
+ int maxLenCol1 = 0, // Mandatory if querying by SQLstmt
+ wxDB *pDb = READONLY_DB, // Database connection pointer
+ bool allowOk = TRUE); // is the OK button enabled
+
+ void OnCommand(wxWindow& win, wxCommandEvent& event);
+ bool OnClose();
+ void OnActivate(bool) {}; // necessary for hot keys
+};
+
+#endif // LISTDB_DOT_H
+
+// ************************************ listdb.h *********************************
--- /dev/null
+#
+# File: makefile.nt
+# Author: George Tasker
+# Created: 1998
+# Updated:
+#
+# "%W% %G%"
+#
+# Makefile : Builds database example (MS VC++).
+
+!if "$(FINAL)" == ""
+FINAL=0
+!endif
+
+
+!if "$(MSVCDIR)" == ""
+MSVCDIR=c:\devstudio\vc
+!endif
+
+# Set WXDIR for your system
+WXDIR = $(WXWIN)
+THISDIR = $(WXDIR)\samples\database
+WXODBCDIR = $(WXDIR)\utils\wxodbc
+
+!if "$(MSVCDIR)" == ""
+DBLIBS=$(MSDEVDIR)\lib\odbc32.lib
+!else
+DBLIBS=$(MSVCDIR)\lib\odbc32.lib
+!endif
+
+EXTRAINC = -I$(WXODBCDIR)\src
+EXTRALIBS = $(DBLIBS) $(WXODBCDIR)\lib\wxodbc.lib
+
+!include $(WXDIR)\src\ntwxwin.mak
+
+PROGRAM=database
+
+OBJECTS = $(PROGRAM).$(OBJSUFF) listdb.$(OBJSUFF)
+
+all: wxodbc $(PROGRAM).exe
+
+$(PROGRAM): $(PROGRAM).exe
+
+gt:
+ cd $(CPPFLAGS)
+
+wxodbc:
+ cd $(WXODBCDIR)\src
+ nmake -f makefile.nt FINAL=$(FINAL)
+ cd $(THISDIR)
+
+wx:
+ cd $(WXDIR)\src\msw
+ nmake -f makefile.nt FINAL=$(FINAL)
+ cd $(THISDIR)
+
+wxclean:
+ cd $(WXDIR)\src\msw
+ nmake -f makefile.nt clean
+ cd $(THISDIR)
+ cd $(WXODBCDIR)\src
+ nmake -f makefile.nt clean
+ cd $(THISDIR)
+
+
+$(PROGRAM).exe: $(DUMMYOBJ) $(OBJECTS) $(PROGRAM).res
+ $(link) @<<
+-out:$(PROGRAM).exe
+$(LINKFLAGS)
+$(DUMMYOBJ) $(OBJECTS) $(PROGRAM).res
+$(LIBS)
+<<
+
+
+listdb.$(OBJSUFF): $(*B).$(SRCSUFF) $(*B).h
+ $(cc) @<<
+$(CPPFLAGS) /c /Fo$(*B).$(OBJSUFF) /Tp $(*B).$(SRCSUFF)
+<<
+
+
+$(PROGRAM).$(OBJSUFF): $(PROGRAM).$(SRCSUFF) $(PROGRAM).h listdb.h
+ $(cc) @<<
+$(CPPFLAGS) /c /Fo$(*B).$(OBJSUFF) /Tp $(*B).$(SRCSUFF)
+<<
+
+
+$(PROGRAM).res: $(PROGRAM).rc $(WXDIR)\include\wx\msw\wx.rc
+ $(rc) -r /i$(WXDIR)\include -fo$@ $(PROGRAM).rc
+
+
+clean:
+ -erase *.obj
+ -erase *.exe
+ -erase *.res
+ -erase *.map
+ -erase *.sbr
+ -erase *.pdb
--- /dev/null
+#
+# File: makefile.unx
+# Author: Terry Tompkins
+# Created: 1998
+# Updated:
+# Copyright: (c) 1998, Remstar International
+#
+# Makefile for wxDB (UNIX).
+
+OBJDIR=database
+OBJSUFF=.o
+SRCSUFF=.cpp
+WXDIR = $(WXWIN)
+
+# All common UNIX compiler flags and options are now in
+# this central makefile.
+include $(WXDIR)/src/make.env
+
+PROGRAM=database
+
+OBJECTS = $(OBJDIR)/$(PROGRAM).$(OBJSUFF) $(OBJDIR)/table.$(OBJSUFF) $(OBJDIR)/db.$(OBJSUFF) $(OBJDIR)/listdb.$(OBJSUFF)
+
+.SUFFIXES:
+
+all: $(OBJDIR) $(PROGRAM)$(GUISUFFIX)
+
+wx:
+
+
+motif:
+ $(MAKE) -f makefile.unx GUISUFFIX=_motif GUI=-Dwx_motif GUISUFFIX=_motif OPT='$(OPT)' LDLIBS='$(MOTIFLDLIBS)' WXLIB=$(WXDIR)/lib/libwx_motif.a OPTIONS='$(OPTIONS)' DEBUG='$(DEBUG)' WARN='$(WARN)' XLIB='$(XLIB)' XINCLUDE='$(XINCLUDE)' XVIEW_LINK=
+
+xview:
+ cd $(WXDIR)/src/x; $(MAKE) -f makefile.unx xview
+ $(MAKE) -f makefile.unx GUI=-Dwx_xview GUISUFFIX=_ol CC=$(CC) OPTIONS='$(OPTIONS)' DEBUG='$(DEBUG)' WARN='$(WARN)' XLIB='$(XLIB)' XINCLUDE='$(XINCLUDE)'
+
+hp:
+ cd $(WXDIR)/src/x; $(MAKE) -f makefile.unx hp
+ $(MAKE) -f makefile.unx GUI=-Dwx_motif GUISUFFIX=_hp CC=CC DEBUG='$(DEBUG)' WARN='-w' \
+ XINCLUDE='$(HPXINCLUDE)' XLIB='$(HPXLIB)' XVIEW_LINK='' LDLIBS='$(HPLDLIBS)'
+
+$(OBJDIR):
+ mkdir $(OBJDIR)
+
+$(PROGRAM)$(GUISUFFIX): $(DUMMYOBJ) $(DBLIBS) $(OBJECTS) $(WXLIB)
+ $(CC) $(LDFLAGS) -o $(PROGRAM)$(GUISUFFIX) $(OBJECTS) $(XVIEW_LINK) $(LDLIBS)
+
+$(OBJDIR)/$(PROGRAM).$(OBJSUFF): $(PROGRAM).$(SRCSUFF)
+ $(CC) -c $(CPPFLAGS) -o $@ $(PROGRAM).$(SRCSUFF)
+
+$(OBJDIR)/table.$(OBJSUFF): table.$(SRCSUFF)
+ $(CC) -c $(CPPFLAGS) -o $@ table.$(SRCSUFF)
+
+$(OBJDIR)/db.$(OBJSUFF): db.$(SRCSUFF)
+ $(CC) -c $(CPPFLAGS) -o $@ db.$(SRCSUFF)
+
+$(OBJDIR)/listdb.$(OBJSUFF): listdb.$(SRCSUFF)
+ $(CC) -c $(CPPFLAGS) -o $@ listdb.$(SRCSUFF)
+
+clean_motif:
+ $(MAKE) -f makefile.unx GUISUFFIX=_motif cleanany
+
+clean_ol:
+ $(MAKE) -f makefile.unx GUISUFFIX=_ol cleanany
+
+clean_hp:
+ $(MAKE) -f makefile.unx GUISUFFIX=_hp cleanany
+
+cleanany:
+ rm -f $(OBJECTS) $(PROGRAM)$(GUISUFFIX) core
+
0, 0,
wxLB_SINGLE
);
-/*
- Robert Roebling
+#if 1
+// Robert Roebling
int numPlayers = 0;
wxString* players = 0;
list->Append(players[i]);
}
delete players;
-*/
+#endif
m_textField = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxDefaultSize, 0);
--- /dev/null
+///////////////////////////////////////////////////////////////////////////////
+// Name: db.cpp
+// Purpose: Implementation of the wxDB class. The wxDB class represents a connection
+// to an ODBC data source. The wxDB class allows operations on the data
+// source such as opening and closing the data source.
+// Author: Doug Card
+// Modified by:
+// Created: 9.96
+// RCS-ID: $Id$
+// Copyright: (c) 1996 Remstar International, Inc.
+// Licence: wxWindows licence, plus:
+// Notice: This class library and its intellectual design are free of charge for use,
+// modification, enhancement, debugging under the following conditions:
+// 1) These classes may only be used as part of the implementation of a
+// wxWindows-based application
+// 2) All enhancements and bug fixes are to be submitted back to the wxWindows
+// user groups free of all charges for use with the wxWindows library.
+// 3) These classes may not be distributed as part of any other class library,
+// DLL, text (written or electronic), other than a complete distribution of
+// the wxWindows GUI development toolkit.
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+// SYNOPSIS START
+// SYNOPSIS STOP
+*/
+
+#pragma implementation "db.h"
+
+/*
+#ifdef _CONSOLE
+ #include <iostream.h>
+#endif
+*/
+
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+ #pragma hdrstop
+#endif //__BORLANDC__
+
+#ifndef WX_PRECOMP
+ #include <wx/wx.h>
+#endif //WX_PRECOMP
+
+#if USE_ODBC
+
+#include <wx/db.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+DbList *PtrBegDbList = 0;
+
+/********** wxDB Constructor **********/
+wxDB::wxDB(HENV &aHenv)
+{
+ int i;
+
+ strcpy(sqlState,"");
+ strcpy(errorMsg,"");
+ nativeError = cbErrorMsg = 0;
+ for (i = 0; i < DB_MAX_ERROR_HISTORY; i++)
+ strcpy(errorList[i], "");
+
+ // Init typeInf structures
+ strcpy(typeInfVarchar.TypeName,"");
+ typeInfVarchar.FsqlType = 0;
+ typeInfVarchar.Precision = 0;
+ typeInfVarchar.CaseSensitive = 0;
+ typeInfVarchar.MaximumScale = 0;
+
+ strcpy(typeInfInteger.TypeName,"");
+ typeInfInteger.FsqlType = 0;
+ typeInfInteger.Precision = 0;
+ typeInfInteger.CaseSensitive = 0;
+ typeInfInteger.MaximumScale = 0;
+
+ strcpy(typeInfFloat.TypeName,"");
+ typeInfFloat.FsqlType = 0;
+ typeInfFloat.Precision = 0;
+ typeInfFloat.CaseSensitive = 0;
+ typeInfFloat.MaximumScale = 0;
+
+ strcpy(typeInfDate.TypeName,"");
+ typeInfDate.FsqlType = 0;
+ typeInfDate.Precision = 0;
+ typeInfDate.CaseSensitive = 0;
+ typeInfDate.MaximumScale = 0;
+
+ // Error reporting is turned OFF by default
+ silent = TRUE;
+
+ // Copy the HENV into the db class
+ henv = aHenv;
+
+ // Allocate a data source connection handle
+ if (SQLAllocConnect(henv, &hdbc) != SQL_SUCCESS)
+ DispAllErrors(henv);
+
+ // Initialize the db status flag
+ DB_STATUS = 0;
+
+ // Mark database as not open as of yet
+ dbIsOpen = FALSE;
+
+} // wxDB::wxDB()
+
+/********** wxDB::Open() **********/
+bool wxDB::Open(char *Dsn, char *Uid, char *AuthStr)
+{
+ assert(Dsn);
+ dsn = Dsn;
+ uid = Uid;
+ authStr = AuthStr;
+
+#ifndef FWD_ONLY_CURSORS
+
+ RETCODE retcode;
+
+ // Specify that the ODBC cursor library be used, if needed. This must be
+ // specified before the connection is made.
+ retcode = SQLSetConnectOption(hdbc, SQL_ODBC_CURSORS, SQL_CUR_USE_IF_NEEDED);
+
+ #ifdef _CONSOLE
+ if (retcode == SQL_SUCCESS)
+ cout << "SQLSetConnectOption(CURSOR_LIB) successful" << endl;
+ else
+ cout << "SQLSetConnectOption(CURSOR_LIB) failed" << endl;
+ #endif
+
+#endif
+
+ // Connect to the data source
+ if (SQLConnect(hdbc, (UCHAR FAR *) Dsn, SQL_NTS,
+ (UCHAR FAR *) Uid, SQL_NTS,
+ (UCHAR FAR *) AuthStr, SQL_NTS) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ // Mark database as open
+ dbIsOpen = TRUE;
+
+ // Allocate a statement handle for the database connection
+ if (SQLAllocStmt(hdbc, &hstmt) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ // Set Connection Options
+ if (! setConnectionOptions())
+ return(FALSE);
+
+ // Query the data source for inf. about itself
+ if (! getDbInfo())
+ return(FALSE);
+
+ // Query the data source regarding data type information
+
+ //
+ // The way I determined which SQL data types to use was by calling SQLGetInfo
+ // for all of the possible SQL data types to see which ones were supported. If
+ // a type is not supported, the SQLFetch() that's called from getDataTypeInfo()
+ // fails with SQL_NO_DATA_FOUND. This is ugly because I'm sure the three SQL data
+ // types I've selected below will not alway's be what we want. These are just
+ // what happened to work against an Oracle 7/Intersolv combination. The following is
+ // a complete list of the results I got back against the Oracle 7 database:
+ //
+ // SQL_BIGINT SQL_NO_DATA_FOUND
+ // SQL_BINARY SQL_NO_DATA_FOUND
+ // SQL_BIT SQL_NO_DATA_FOUND
+ // SQL_CHAR type name = 'CHAR', Precision = 255
+ // SQL_DATE SQL_NO_DATA_FOUND
+ // SQL_DECIMAL type name = 'NUMBER', Precision = 38
+ // SQL_DOUBLE type name = 'NUMBER', Precision = 15
+ // SQL_FLOAT SQL_NO_DATA_FOUND
+ // SQL_INTEGER SQL_NO_DATA_FOUND
+ // SQL_LONGVARBINARY type name = 'LONG RAW', Precision = 2 billion
+ // SQL_LONGVARCHAR type name = 'LONG', Precision = 2 billion
+ // SQL_NUMERIC SQL_NO_DATA_FOUND
+ // SQL_REAL SQL_NO_DATA_FOUND
+ // SQL_SMALLINT SQL_NO_DATA_FOUND
+ // SQL_TIME SQL_NO_DATA_FOUND
+ // SQL_TIMESTAMP type name = 'DATE', Precision = 19
+ // SQL_VARBINARY type name = 'RAW', Precision = 255
+ // SQL_VARCHAR type name = 'VARCHAR2', Precision = 2000
+ // =====================================================================
+ // Results from a Microsoft Access 7.0 db, using a driver from Microsoft
+ //
+ // SQL_VARCHAR type name = 'TEXT', Precision = 255
+ // SQL_TIMESTAMP type name = 'DATETIME'
+ // SQL_DECIMAL SQL_NO_DATA_FOUND
+ // SQL_NUMERIC type name = 'CURRENCY', Precision = 19
+ // SQL_FLOAT SQL_NO_DATA_FOUND
+ // SQL_REAL type name = 'SINGLE', Precision = 7
+ // SQL_DOUBLE type name = 'DOUBLE', Precision = 15
+ // SQL_INTEGER type name = 'LONG', Precision = 10
+
+ // VARCHAR = Variable length character string
+ if (! getDataTypeInfo(SQL_VARCHAR, typeInfVarchar))
+ if (! getDataTypeInfo(SQL_CHAR, typeInfVarchar))
+ return(FALSE);
+ else
+ typeInfVarchar.FsqlType = SQL_CHAR;
+ else
+ typeInfVarchar.FsqlType = SQL_VARCHAR;
+
+ // Float
+ if (! getDataTypeInfo(SQL_DOUBLE, typeInfFloat))
+ if (! getDataTypeInfo(SQL_REAL, typeInfFloat))
+ if (! getDataTypeInfo(SQL_FLOAT, typeInfFloat))
+ if (! getDataTypeInfo(SQL_DECIMAL, typeInfFloat))
+ if (! getDataTypeInfo(SQL_NUMERIC, typeInfFloat))
+ return(FALSE);
+ else
+ typeInfFloat.FsqlType = SQL_NUMERIC;
+ else
+ typeInfFloat.FsqlType = SQL_DECIMAL;
+ else
+ typeInfFloat.FsqlType = SQL_FLOAT;
+ else
+ typeInfFloat.FsqlType = SQL_REAL;
+ else
+ typeInfFloat.FsqlType = SQL_DOUBLE;
+
+ // Integer
+ if (! getDataTypeInfo(SQL_INTEGER, typeInfInteger))
+ // If SQL_INTEGER is not supported, use the floating point
+ // data type to store integers as well as floats
+ if (! getDataTypeInfo(typeInfFloat.FsqlType, typeInfInteger))
+ return(FALSE);
+ else
+ typeInfInteger.FsqlType = typeInfFloat.FsqlType;
+ else
+ typeInfInteger.FsqlType = SQL_INTEGER;
+
+ // Date/Time
+ if (! getDataTypeInfo(SQL_TIMESTAMP, typeInfDate))
+ return(FALSE);
+ else
+ typeInfDate.FsqlType = SQL_TIMESTAMP;
+
+#ifdef _CONSOLE
+ cout << "VARCHAR DATA TYPE: " << typeInfVarchar.TypeName << endl;
+ cout << "INTEGER DATA TYPE: " << typeInfInteger.TypeName << endl;
+ cout << "FLOAT DATA TYPE: " << typeInfFloat.TypeName << endl;
+ cout << "DATE DATA TYPE: " << typeInfDate.TypeName << endl;
+ cout << endl;
+#endif
+
+ // Completed Successfully
+ return(TRUE);
+
+} // wxDB::Open()
+
+// The Intersolv/Oracle 7 driver was "Not Capable" of setting the login timeout.
+
+/********** wxDB::setConnectionOptions() **********/
+bool wxDB::setConnectionOptions(void)
+{
+ SQLSetConnectOption(hdbc, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF);
+ SQLSetConnectOption(hdbc, SQL_OPT_TRACE, SQL_OPT_TRACE_OFF);
+
+ // Display the connection options to verify them
+#ifdef _CONSOLE
+ long l;
+ cout << ">>>>> CONNECTION OPTIONS <<<<<<" << endl;
+
+ if (SQLGetConnectOption(hdbc, SQL_AUTOCOMMIT, &l) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+ cout << "AUTOCOMMIT: " << (l == SQL_AUTOCOMMIT_OFF ? "OFF" : "ON") << endl;
+
+ if (SQLGetConnectOption(hdbc, SQL_ODBC_CURSORS, &l) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+ cout << "ODBC CURSORS: ";
+ switch(l)
+ {
+ case(SQL_CUR_USE_IF_NEEDED):
+ cout << "SQL_CUR_USE_IF_NEEDED";
+ break;
+ case(SQL_CUR_USE_ODBC):
+ cout << "SQL_CUR_USE_ODBC";
+ break;
+ case(SQL_CUR_USE_DRIVER):
+ cout << "SQL_CUR_USE_DRIVER";
+ break;
+ }
+ cout << endl;
+
+ if (SQLGetConnectOption(hdbc, SQL_OPT_TRACE, &l) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+ cout << "TRACING: " << (l == SQL_OPT_TRACE_OFF ? "OFF" : "ON") << endl;
+
+ cout << endl;
+#endif
+
+ // Completed Successfully
+ return(TRUE);
+
+} // wxDB::setConnectionOptions()
+
+/********** wxDB::getDbInfo() **********/
+bool wxDB::getDbInfo(void)
+{
+ SWORD cb;
+
+ if (SQLGetInfo(hdbc, SQL_SERVER_NAME, dbInf.serverName, 40, &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_DATABASE_NAME, dbInf.databaseName, 128, &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_DBMS_NAME, dbInf.dbmsName, 40, &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_DBMS_VER, dbInf.dbmsVer, 20, &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_ACTIVE_CONNECTIONS, &dbInf.maxConnections, sizeof(dbInf.maxConnections), &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_ACTIVE_STATEMENTS, &dbInf.maxStmts, sizeof(dbInf.maxStmts), &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_DRIVER_NAME, dbInf.driverName, 40, &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_DRIVER_ODBC_VER, dbInf.odbcVer, 20, &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_ODBC_VER, dbInf.drvMgrOdbcVer, 20, &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_DRIVER_VER, dbInf.driverVer, 40, &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_ODBC_API_CONFORMANCE, &dbInf.apiConfLvl, sizeof(dbInf.apiConfLvl), &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_ODBC_SAG_CLI_CONFORMANCE, &dbInf.cliConfLvl, sizeof(dbInf.cliConfLvl), &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_ODBC_SQL_CONFORMANCE, &dbInf.sqlConfLvl, sizeof(dbInf.sqlConfLvl), &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_OUTER_JOINS, dbInf.outerJoins, 2, &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_PROCEDURES, dbInf.procedureSupport, 2, &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_CURSOR_COMMIT_BEHAVIOR, &dbInf.cursorCommitBehavior, sizeof(dbInf.cursorCommitBehavior), &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_CURSOR_ROLLBACK_BEHAVIOR, &dbInf.cursorRollbackBehavior, sizeof(dbInf.cursorRollbackBehavior), &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_NON_NULLABLE_COLUMNS, &dbInf.supportNotNullClause, sizeof(dbInf.supportNotNullClause), &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_ODBC_SQL_OPT_IEF, dbInf.supportIEF, 2, &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_DEFAULT_TXN_ISOLATION, &dbInf.txnIsolation, sizeof(dbInf.txnIsolation), &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_TXN_ISOLATION_OPTION, &dbInf.txnIsolationOptions, sizeof(dbInf.txnIsolationOptions), &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_FETCH_DIRECTION, &dbInf.fetchDirections, sizeof(dbInf.fetchDirections), &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_LOCK_TYPES, &dbInf.lockTypes, sizeof(dbInf.lockTypes), &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_POS_OPERATIONS, &dbInf.posOperations, sizeof(dbInf.posOperations), &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_POSITIONED_STATEMENTS, &dbInf.posStmts, sizeof(dbInf.posStmts), &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_SCROLL_CONCURRENCY, &dbInf.scrollConcurrency, sizeof(dbInf.scrollConcurrency), &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_SCROLL_OPTIONS, &dbInf.scrollOptions, sizeof(dbInf.scrollOptions), &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_STATIC_SENSITIVITY, &dbInf.staticSensitivity, sizeof(dbInf.staticSensitivity), &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_TXN_CAPABLE, &dbInf.txnCapable, sizeof(dbInf.txnCapable), &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ if (SQLGetInfo(hdbc, SQL_LOGIN_TIMEOUT, &dbInf.loginTimeout, sizeof(dbInf.loginTimeout), &cb) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+#ifdef _CONSOLE
+ cout << ">>>>> DATA SOURCE INFORMATION <<<<<" << endl;
+ cout << "SERVER Name: " << dbInf.serverName << endl;
+ cout << "DBMS Name: " << dbInf.dbmsName << "; DBMS Version: " << dbInf.dbmsVer << endl;
+ cout << "ODBC Version: " << dbInf.odbcVer << "; Driver Version: " << dbInf.driverVer << endl;
+
+ cout << "API Conf. Level: ";
+ switch(dbInf.apiConfLvl)
+ {
+ case SQL_OAC_NONE: cout << "None"; break;
+ case SQL_OAC_LEVEL1: cout << "Level 1"; break;
+ case SQL_OAC_LEVEL2: cout << "Level 2"; break;
+ }
+ cout << endl;
+
+ cout << "SAG CLI Conf. Level: ";
+ switch(dbInf.cliConfLvl)
+ {
+ case SQL_OSCC_NOT_COMPLIANT: cout << "Not Compliant"; break;
+ case SQL_OSCC_COMPLIANT: cout << "Compliant"; break;
+ }
+ cout << endl;
+
+ cout << "SQL Conf. Level: ";
+ switch(dbInf.sqlConfLvl)
+ {
+ case SQL_OSC_MINIMUM: cout << "Minimum Grammer"; break;
+ case SQL_OSC_CORE: cout << "Core Grammer"; break;
+ case SQL_OSC_EXTENDED: cout << "Extended Grammer"; break;
+ }
+ cout << endl;
+
+ cout << "Max. Connections: " << dbInf.maxConnections << endl;
+ cout << "Outer Joins: " << dbInf.outerJoins << endl;
+ cout << "Support for Procedures: " << dbInf.procedureSupport << endl;
+
+ cout << "Cursor COMMIT Behavior: ";
+ switch(dbInf.cursorCommitBehavior)
+ {
+ case SQL_CB_DELETE: cout << "Delete cursors"; break;
+ case SQL_CB_CLOSE: cout << "Close cursors"; break;
+ case SQL_CB_PRESERVE: cout << "Preserve cursors"; break;
+ }
+ cout << endl;
+
+ cout << "Cursor ROLLBACK Behavior: ";
+ switch(dbInf.cursorRollbackBehavior)
+ {
+ case SQL_CB_DELETE: cout << "Delete cursors"; break;
+ case SQL_CB_CLOSE: cout << "Close cursors"; break;
+ case SQL_CB_PRESERVE: cout << "Preserve cursors"; break;
+ }
+ cout << endl;
+
+ cout << "Support NOT NULL clause: ";
+ switch(dbInf.supportNotNullClause)
+ {
+ case SQL_NNC_NULL: cout << "No"; break;
+ case SQL_NNC_NON_NULL: cout << "Yes"; break;
+ }
+ cout << endl;
+
+ cout << "Support IEF (Ref. Integrity): " << dbInf.supportIEF << endl;
+ cout << "Login Timeout: " << dbInf.loginTimeout << endl;
+
+ cout << endl << endl << "more ..." << endl;
+ getchar();
+
+ cout << "Default Transaction Isolation: ";
+ switch(dbInf.txnIsolation)
+ {
+ case SQL_TXN_READ_UNCOMMITTED: cout << "Read Uncommitted"; break;
+ case SQL_TXN_READ_COMMITTED: cout << "Read Committed"; break;
+ case SQL_TXN_REPEATABLE_READ: cout << "Repeatable Read"; break;
+ case SQL_TXN_SERIALIZABLE: cout << "Serializable"; break;
+#ifdef ODBC_V20
+ case SQL_TXN_VERSIONING: cout << "Versioning"; break;
+#endif
+ }
+ cout << endl;
+
+ cout << "Transaction Isolation Options: ";
+ if (dbInf.txnIsolationOptions & SQL_TXN_READ_UNCOMMITTED)
+ cout << "Read Uncommitted, ";
+ if (dbInf.txnIsolationOptions & SQL_TXN_READ_COMMITTED)
+ cout << "Read Committed, ";
+ if (dbInf.txnIsolationOptions & SQL_TXN_REPEATABLE_READ)
+ cout << "Repeatable Read, ";
+ if (dbInf.txnIsolationOptions & SQL_TXN_SERIALIZABLE)
+ cout << "Serializable, ";
+#ifdef ODBC_V20
+ if (dbInf.txnIsolationOptions & SQL_TXN_VERSIONING)
+ cout << "Versioning";
+#endif
+ cout << endl;
+
+ cout << "Fetch Directions Supported:" << endl << " ";
+ if (dbInf.fetchDirections & SQL_FD_FETCH_NEXT)
+ cout << "Next, ";
+ if (dbInf.fetchDirections & SQL_FD_FETCH_PRIOR)
+ cout << "Prev, ";
+ if (dbInf.fetchDirections & SQL_FD_FETCH_FIRST)
+ cout << "First, ";
+ if (dbInf.fetchDirections & SQL_FD_FETCH_LAST)
+ cout << "Last, ";
+ if (dbInf.fetchDirections & SQL_FD_FETCH_ABSOLUTE)
+ cout << "Absolute, ";
+ if (dbInf.fetchDirections & SQL_FD_FETCH_RELATIVE)
+ cout << "Relative, ";
+#ifdef ODBC_V20
+ if (dbInf.fetchDirections & SQL_FD_FETCH_RESUME)
+ cout << "Resume, ";
+#endif
+ if (dbInf.fetchDirections & SQL_FD_FETCH_BOOKMARK)
+ cout << "Bookmark";
+ cout << endl;
+
+ cout << "Lock Types Supported (SQLSetPos): ";
+ if (dbInf.lockTypes & SQL_LCK_NO_CHANGE)
+ cout << "No Change, ";
+ if (dbInf.lockTypes & SQL_LCK_EXCLUSIVE)
+ cout << "Exclusive, ";
+ if (dbInf.lockTypes & SQL_LCK_UNLOCK)
+ cout << "UnLock";
+ cout << endl;
+
+ cout << "Position Operations Supported (SQLSetPos): ";
+ if (dbInf.posOperations & SQL_POS_POSITION)
+ cout << "Position, ";
+ if (dbInf.posOperations & SQL_POS_REFRESH)
+ cout << "Refresh, ";
+ if (dbInf.posOperations & SQL_POS_UPDATE)
+ cout << "Upd, ";
+ if (dbInf.posOperations & SQL_POS_DELETE)
+ cout << "Del, ";
+ if (dbInf.posOperations & SQL_POS_ADD)
+ cout << "Add";
+ cout << endl;
+
+ cout << "Positioned Statements Supported: ";
+ if (dbInf.posStmts & SQL_PS_POSITIONED_DELETE)
+ cout << "Pos delete, ";
+ if (dbInf.posStmts & SQL_PS_POSITIONED_UPDATE)
+ cout << "Pos update, ";
+ if (dbInf.posStmts & SQL_PS_SELECT_FOR_UPDATE)
+ cout << "Select for update";
+ cout << endl;
+
+ cout << "Scroll Concurrency: ";
+ if (dbInf.scrollConcurrency & SQL_SCCO_READ_ONLY)
+ cout << "Read Only, ";
+ if (dbInf.scrollConcurrency & SQL_SCCO_LOCK)
+ cout << "Lock, ";
+ if (dbInf.scrollConcurrency & SQL_SCCO_OPT_ROWVER)
+ cout << "Opt. Rowver, ";
+ if (dbInf.scrollConcurrency & SQL_SCCO_OPT_VALUES)
+ cout << "Opt. Values";
+ cout << endl;
+
+ cout << "Scroll Options: ";
+ if (dbInf.scrollOptions & SQL_SO_FORWARD_ONLY)
+ cout << "Fwd Only, ";
+ if (dbInf.scrollOptions & SQL_SO_STATIC)
+ cout << "Static, ";
+ if (dbInf.scrollOptions & SQL_SO_KEYSET_DRIVEN)
+ cout << "Keyset Driven, ";
+ if (dbInf.scrollOptions & SQL_SO_DYNAMIC)
+ cout << "Dynamic, ";
+ if (dbInf.scrollOptions & SQL_SO_MIXED)
+ cout << "Mixed";
+ cout << endl;
+
+ cout << "Static Sensitivity: ";
+ if (dbInf.staticSensitivity & SQL_SS_ADDITIONS)
+ cout << "Additions, ";
+ if (dbInf.staticSensitivity & SQL_SS_DELETIONS)
+ cout << "Deletions, ";
+ if (dbInf.staticSensitivity & SQL_SS_UPDATES)
+ cout << "Updates";
+ cout << endl;
+
+ cout << "Transaction Capable?: ";
+ switch(dbInf.txnCapable)
+ {
+ case SQL_TC_NONE: cout << "No"; break;
+ case SQL_TC_DML: cout << "DML Only"; break;
+ case SQL_TC_DDL_COMMIT: cout << "DDL Commit"; break;
+ case SQL_TC_DDL_IGNORE: cout << "DDL Ignore"; break;
+ case SQL_TC_ALL: cout << "DDL & DML"; break;
+ }
+ cout << endl;
+
+ cout << endl;
+
+#endif
+
+ // Completed Successfully
+ return(TRUE);
+
+} // wxDB::getDbInfo()
+
+/********** wxDB::getDataTypeInfo() **********/
+bool wxDB::getDataTypeInfo(SWORD fSqlType, SqlTypeInfo &structSQLTypeInfo)
+{
+ // fSqlType will be something like SQL_VARCHAR. This parameter determines
+ // the data type inf. is gathered for.
+ //
+ // SqlTypeInfo is a structure that is filled in with data type information,
+
+ RETCODE retcode;
+ SDWORD cbRet;
+
+ // Get information about the data type specified
+ if (SQLGetTypeInfo(hstmt, fSqlType) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc, hstmt));
+ // Fetch the record
+ if ((retcode = SQLFetch(hstmt)) != SQL_SUCCESS)
+ {
+#ifdef _CONSOLE
+ if (retcode == SQL_NO_DATA_FOUND)
+ cout << "SQL_NO_DATA_FOUND fetching inf. about data type." << endl;
+#endif
+ DispAllErrors(henv, hdbc, hstmt);
+ SQLFreeStmt(hstmt, SQL_CLOSE);
+ return(FALSE);
+ }
+ // Obtain columns from the record
+ if (SQLGetData(hstmt, 1, SQL_C_CHAR, structSQLTypeInfo.TypeName, DB_TYPE_NAME_LEN, &cbRet) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc, hstmt));
+ if (SQLGetData(hstmt, 3, SQL_C_LONG, &structSQLTypeInfo.Precision, 0, &cbRet) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc, hstmt));
+ if (SQLGetData(hstmt, 8, SQL_C_SHORT, &structSQLTypeInfo.CaseSensitive, 0, &cbRet) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc, hstmt));
+// if (SQLGetData(hstmt, 14, SQL_C_SHORT, &structSQLTypeInfo.MinimumScale, 0, &cbRet) != SQL_SUCCESS)
+// return(DispAllErrors(henv, hdbc, hstmt));
+ if (SQLGetData(hstmt, 15, SQL_C_SHORT, &structSQLTypeInfo.MaximumScale, 0, &cbRet) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc, hstmt));
+
+ if (structSQLTypeInfo.MaximumScale < 0)
+ structSQLTypeInfo.MaximumScale = 0;
+
+ // Close the statement handle which closes open cursors
+ if (SQLFreeStmt(hstmt, SQL_CLOSE) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc, hstmt));
+
+ // Completed Successfully
+ return(TRUE);
+
+} // wxDB::getDataTypeInfo()
+
+/********** wxDB::Close() **********/
+void wxDB::Close(void)
+{
+ // Free statement handle
+ if (dbIsOpen)
+ {
+ if (SQLFreeStmt(hstmt, SQL_DROP) != SQL_SUCCESS)
+ DispAllErrors(henv, hdbc);
+ }
+
+ // Disconnect from the datasource
+ if (SQLDisconnect(hdbc) != SQL_SUCCESS)
+ DispAllErrors(henv, hdbc);
+
+ // Free the connection to the datasource
+ if (SQLFreeConnect(hdbc) != SQL_SUCCESS)
+ DispAllErrors(henv, hdbc);
+
+} // wxDB::Close()
+
+/********** wxDB::CommitTrans() **********/
+bool wxDB::CommitTrans(void)
+{
+ // Commit the transaction
+ if (SQLTransact(henv, hdbc, SQL_COMMIT) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ // Completed successfully
+ return(TRUE);
+
+} // wxDB::CommitTrans()
+
+/********** wxDB::RollbackTrans() **********/
+bool wxDB::RollbackTrans(void)
+{
+ // Rollback the transaction
+ if (SQLTransact(henv, hdbc, SQL_ROLLBACK) != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc));
+
+ // Completed successfully
+ return(TRUE);
+
+} // wxDB::RollbackTrans()
+
+/********** wxDB::DispAllErrors() **********/
+bool wxDB::DispAllErrors(HENV aHenv, HDBC aHdbc, HSTMT aHstmt)
+{
+ char odbcErrMsg[DB_MAX_ERROR_MSG_LEN];
+
+ while (SQLError(aHenv, aHdbc, aHstmt, (UCHAR FAR *) sqlState, &nativeError, (UCHAR FAR *) errorMsg, SQL_MAX_MESSAGE_LENGTH - 1, &cbErrorMsg) == SQL_SUCCESS)
+ {
+ sprintf(odbcErrMsg, "SQL State = %s\nNative Error Code = %li\nError Message = %s\n", sqlState, nativeError, errorMsg);
+ logError(odbcErrMsg, sqlState);
+ if (!silent)
+ {
+#ifdef _CONSOLE
+ // When run in console mode, use standard out to display errors.
+ cout << odbcErrMsg << endl;
+ cout << "Press any key to continue..." << endl;
+ getchar();
+#endif
+ }
+ }
+
+ return(FALSE); // This function alway's returns false.
+
+} // wxDB::DispAllErrors()
+
+/********** wxDB::GetNextError() **********/
+bool wxDB::GetNextError(HENV aHenv, HDBC aHdbc, HSTMT aHstmt)
+{
+ if (SQLError(aHenv, aHdbc, aHstmt, (UCHAR FAR *) sqlState, &nativeError, (UCHAR FAR *) errorMsg, SQL_MAX_MESSAGE_LENGTH - 1, &cbErrorMsg) == SQL_SUCCESS)
+ return(TRUE);
+ else
+ return(FALSE);
+
+} // wxDB::GetNextError()
+
+/********** wxDB::DispNextError() **********/
+void wxDB::DispNextError(void)
+{
+ char odbcErrMsg[DB_MAX_ERROR_MSG_LEN];
+
+ sprintf(odbcErrMsg, "SQL State = %s\nNative Error Code = %li\nError Message = %s\n", sqlState, nativeError, errorMsg);
+ logError(odbcErrMsg, sqlState);
+
+ if (silent)
+ return;
+
+#ifdef _CONSOLE
+ // When run in console mode, use standard out to display errors.
+ cout << odbcErrMsg << endl;
+ cout << "Press any key to continue..." << endl;
+ getchar();
+#endif
+
+} // wxDB::DispNextError()
+
+/********** wxDB::logError() **********/
+void wxDB::logError(char *errMsg, char *SQLState)
+{
+ assert(errMsg && strlen(errMsg));
+
+ static int pLast = -1;
+ int dbStatus;
+
+ if (++pLast == DB_MAX_ERROR_HISTORY)
+ {
+ for (int i = 0; i < DB_MAX_ERROR_HISTORY; i++)
+ strcpy(errorList[i], errorList[i+1]);
+ pLast--;
+ }
+
+ strcpy(errorList[pLast], errMsg);
+
+ if (SQLState && strlen(SQLState))
+ if ((dbStatus = TranslateSqlState(SQLState)) != DB_ERR_FUNCTION_SEQUENCE_ERROR)
+ DB_STATUS = dbStatus;
+
+} // wxDB::logError()
+
+/**********wxDB::TranslateSqlState() **********/
+int wxDB::TranslateSqlState(char *SQLState)
+{
+ if (!strcmp(SQLState, "01000"))
+ return(DB_ERR_GENERAL_WARNING);
+ if (!strcmp(SQLState, "01002"))
+ return(DB_ERR_DISCONNECT_ERROR);
+ if (!strcmp(SQLState, "01004"))
+ return(DB_ERR_DATA_TRUNCATED);
+ if (!strcmp(SQLState, "01006"))
+ return(DB_ERR_PRIV_NOT_REVOKED);
+ if (!strcmp(SQLState, "01S00"))
+ return(DB_ERR_INVALID_CONN_STR_ATTR);
+ if (!strcmp(SQLState, "01S01"))
+ return(DB_ERR_ERROR_IN_ROW);
+ if (!strcmp(SQLState, "01S02"))
+ return(DB_ERR_OPTION_VALUE_CHANGED);
+ if (!strcmp(SQLState, "01S03"))
+ return(DB_ERR_NO_ROWS_UPD_OR_DEL);
+ if (!strcmp(SQLState, "01S04"))
+ return(DB_ERR_MULTI_ROWS_UPD_OR_DEL);
+ if (!strcmp(SQLState, "07001"))
+ return(DB_ERR_WRONG_NO_OF_PARAMS);
+ if (!strcmp(SQLState, "07006"))
+ return(DB_ERR_DATA_TYPE_ATTR_VIOL);
+ if (!strcmp(SQLState, "08001"))
+ return(DB_ERR_UNABLE_TO_CONNECT);
+ if (!strcmp(SQLState, "08002"))
+ return(DB_ERR_CONNECTION_IN_USE);
+ if (!strcmp(SQLState, "08003"))
+ return(DB_ERR_CONNECTION_NOT_OPEN);
+ if (!strcmp(SQLState, "08004"))
+ return(DB_ERR_REJECTED_CONNECTION);
+ if (!strcmp(SQLState, "08007"))
+ return(DB_ERR_CONN_FAIL_IN_TRANS);
+ if (!strcmp(SQLState, "08S01"))
+ return(DB_ERR_COMM_LINK_FAILURE);
+ if (!strcmp(SQLState, "21S01"))
+ return(DB_ERR_INSERT_VALUE_LIST_MISMATCH);
+ if (!strcmp(SQLState, "21S02"))
+ return(DB_ERR_DERIVED_TABLE_MISMATCH);
+ if (!strcmp(SQLState, "22001"))
+ return(DB_ERR_STRING_RIGHT_TRUNC);
+ if (!strcmp(SQLState, "22003"))
+ return(DB_ERR_NUMERIC_VALUE_OUT_OF_RNG);
+ if (!strcmp(SQLState, "22005"))
+ return(DB_ERR_ERROR_IN_ASSIGNMENT);
+ if (!strcmp(SQLState, "22008"))
+ return(DB_ERR_DATETIME_FLD_OVERFLOW);
+ if (!strcmp(SQLState, "22012"))
+ return(DB_ERR_DIVIDE_BY_ZERO);
+ if (!strcmp(SQLState, "22026"))
+ return(DB_ERR_STR_DATA_LENGTH_MISMATCH);
+ if (!strcmp(SQLState, "23000"))
+ return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL);
+ if (!strcmp(SQLState, "24000"))
+ return(DB_ERR_INVALID_CURSOR_STATE);
+ if (!strcmp(SQLState, "25000"))
+ return(DB_ERR_INVALID_TRANS_STATE);
+ if (!strcmp(SQLState, "28000"))
+ return(DB_ERR_INVALID_AUTH_SPEC);
+ if (!strcmp(SQLState, "34000"))
+ return(DB_ERR_INVALID_CURSOR_NAME);
+ if (!strcmp(SQLState, "37000"))
+ return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL);
+ if (!strcmp(SQLState, "3C000"))
+ return(DB_ERR_DUPLICATE_CURSOR_NAME);
+ if (!strcmp(SQLState, "40001"))
+ return(DB_ERR_SERIALIZATION_FAILURE);
+ if (!strcmp(SQLState, "42000"))
+ return(DB_ERR_SYNTAX_ERROR_OR_ACCESS_VIOL2);
+ if (!strcmp(SQLState, "70100"))
+ return(DB_ERR_OPERATION_ABORTED);
+ if (!strcmp(SQLState, "IM001"))
+ return(DB_ERR_UNSUPPORTED_FUNCTION);
+ if (!strcmp(SQLState, "IM002"))
+ return(DB_ERR_NO_DATA_SOURCE);
+ if (!strcmp(SQLState, "IM003"))
+ return(DB_ERR_DRIVER_LOAD_ERROR);
+ if (!strcmp(SQLState, "IM004"))
+ return(DB_ERR_SQLALLOCENV_FAILED);
+ if (!strcmp(SQLState, "IM005"))
+ return(DB_ERR_SQLALLOCCONNECT_FAILED);
+ if (!strcmp(SQLState, "IM006"))
+ return(DB_ERR_SQLSETCONNECTOPTION_FAILED);
+ if (!strcmp(SQLState, "IM007"))
+ return(DB_ERR_NO_DATA_SOURCE_DLG_PROHIB);
+ if (!strcmp(SQLState, "IM008"))
+ return(DB_ERR_DIALOG_FAILED);
+ if (!strcmp(SQLState, "IM009"))
+ return(DB_ERR_UNABLE_TO_LOAD_TRANSLATION_DLL);
+ if (!strcmp(SQLState, "IM010"))
+ return(DB_ERR_DATA_SOURCE_NAME_TOO_LONG);
+ if (!strcmp(SQLState, "IM011"))
+ return(DB_ERR_DRIVER_NAME_TOO_LONG);
+ if (!strcmp(SQLState, "IM012"))
+ return(DB_ERR_DRIVER_KEYWORD_SYNTAX_ERROR);
+ if (!strcmp(SQLState, "IM013"))
+ return(DB_ERR_TRACE_FILE_ERROR);
+ if (!strcmp(SQLState, "S0001"))
+ return(DB_ERR_TABLE_OR_VIEW_ALREADY_EXISTS);
+ if (!strcmp(SQLState, "S0002"))
+ return(DB_ERR_TABLE_NOT_FOUND);
+ if (!strcmp(SQLState, "S0011"))
+ return(DB_ERR_INDEX_ALREADY_EXISTS);
+ if (!strcmp(SQLState, "S0012"))
+ return(DB_ERR_INDEX_NOT_FOUND);
+ if (!strcmp(SQLState, "S0021"))
+ return(DB_ERR_COLUMN_ALREADY_EXISTS);
+ if (!strcmp(SQLState, "S0022"))
+ return(DB_ERR_COLUMN_NOT_FOUND);
+ if (!strcmp(SQLState, "S0023"))
+ return(DB_ERR_NO_DEFAULT_FOR_COLUMN);
+ if (!strcmp(SQLState, "S1000"))
+ return(DB_ERR_GENERAL_ERROR);
+ if (!strcmp(SQLState, "S1001"))
+ return(DB_ERR_MEMORY_ALLOCATION_FAILURE);
+ if (!strcmp(SQLState, "S1002"))
+ return(DB_ERR_INVALID_COLUMN_NUMBER);
+ if (!strcmp(SQLState, "S1003"))
+ return(DB_ERR_PROGRAM_TYPE_OUT_OF_RANGE);
+ if (!strcmp(SQLState, "S1004"))
+ return(DB_ERR_SQL_DATA_TYPE_OUT_OF_RANGE);
+ if (!strcmp(SQLState, "S1008"))
+ return(DB_ERR_OPERATION_CANCELLED);
+ if (!strcmp(SQLState, "S1009"))
+ return(DB_ERR_INVALID_ARGUMENT_VALUE);
+ if (!strcmp(SQLState, "S1010"))
+ return(DB_ERR_FUNCTION_SEQUENCE_ERROR);
+ if (!strcmp(SQLState, "S1011"))
+ return(DB_ERR_OPERATION_INVALID_AT_THIS_TIME);
+ if (!strcmp(SQLState, "S1012"))
+ return(DB_ERR_INVALID_TRANS_OPERATION_CODE);
+ if (!strcmp(SQLState, "S1015"))
+ return(DB_ERR_NO_CURSOR_NAME_AVAIL);
+ if (!strcmp(SQLState, "S1090"))
+ return(DB_ERR_INVALID_STR_OR_BUF_LEN);
+ if (!strcmp(SQLState, "S1091"))
+ return(DB_ERR_DESCRIPTOR_TYPE_OUT_OF_RANGE);
+ if (!strcmp(SQLState, "S1092"))
+ return(DB_ERR_OPTION_TYPE_OUT_OF_RANGE);
+ if (!strcmp(SQLState, "S1093"))
+ return(DB_ERR_INVALID_PARAM_NO);
+ if (!strcmp(SQLState, "S1094"))
+ return(DB_ERR_INVALID_SCALE_VALUE);
+ if (!strcmp(SQLState, "S1095"))
+ return(DB_ERR_FUNCTION_TYPE_OUT_OF_RANGE);
+ if (!strcmp(SQLState, "S1096"))
+ return(DB_ERR_INF_TYPE_OUT_OF_RANGE);
+ if (!strcmp(SQLState, "S1097"))
+ return(DB_ERR_COLUMN_TYPE_OUT_OF_RANGE);
+ if (!strcmp(SQLState, "S1098"))
+ return(DB_ERR_SCOPE_TYPE_OUT_OF_RANGE);
+ if (!strcmp(SQLState, "S1099"))
+ return(DB_ERR_NULLABLE_TYPE_OUT_OF_RANGE);
+ if (!strcmp(SQLState, "S1100"))
+ return(DB_ERR_UNIQUENESS_OPTION_TYPE_OUT_OF_RANGE);
+ if (!strcmp(SQLState, "S1101"))
+ return(DB_ERR_ACCURACY_OPTION_TYPE_OUT_OF_RANGE);
+ if (!strcmp(SQLState, "S1103"))
+ return(DB_ERR_DIRECTION_OPTION_OUT_OF_RANGE);
+ if (!strcmp(SQLState, "S1104"))
+ return(DB_ERR_INVALID_PRECISION_VALUE);
+ if (!strcmp(SQLState, "S1105"))
+ return(DB_ERR_INVALID_PARAM_TYPE);
+ if (!strcmp(SQLState, "S1106"))
+ return(DB_ERR_FETCH_TYPE_OUT_OF_RANGE);
+ if (!strcmp(SQLState, "S1107"))
+ return(DB_ERR_ROW_VALUE_OUT_OF_RANGE);
+ if (!strcmp(SQLState, "S1108"))
+ return(DB_ERR_CONCURRENCY_OPTION_OUT_OF_RANGE);
+ if (!strcmp(SQLState, "S1109"))
+ return(DB_ERR_INVALID_CURSOR_POSITION);
+ if (!strcmp(SQLState, "S1110"))
+ return(DB_ERR_INVALID_DRIVER_COMPLETION);
+ if (!strcmp(SQLState, "S1111"))
+ return(DB_ERR_INVALID_BOOKMARK_VALUE);
+ if (!strcmp(SQLState, "S1C00"))
+ return(DB_ERR_DRIVER_NOT_CAPABLE);
+ if (!strcmp(SQLState, "S1T00"))
+ return(DB_ERR_TIMEOUT_EXPIRED);
+
+ // No match
+ return(0);
+
+} // wxDB::TranslateSqlState()
+
+/********** wxDB::Grant() **********/
+bool wxDB::Grant(int privileges, char *tableName, char *userList)
+{
+ char sqlStmt[DB_MAX_STATEMENT_LEN];
+
+ // Build the grant statement
+ strcpy(sqlStmt, "GRANT ");
+ if (privileges == DB_GRANT_ALL)
+ strcat(sqlStmt, "ALL");
+ else
+ {
+ int c = 0;
+ if (privileges & DB_GRANT_SELECT)
+ {
+ strcat(sqlStmt, "SELECT");
+ c++;
+ }
+ if (privileges & DB_GRANT_INSERT)
+ {
+ if (c++)
+ strcat(sqlStmt, ", ");
+ strcat(sqlStmt, "INSERT");
+ }
+ if (privileges & DB_GRANT_UPDATE)
+ {
+ if (c++)
+ strcat(sqlStmt, ", ");
+ strcat(sqlStmt, "UPDATE");
+ }
+ if (privileges & DB_GRANT_DELETE)
+ {
+ if (c++)
+ strcat(sqlStmt, ", ");
+ strcat(sqlStmt, "DELETE");
+ }
+ }
+
+ strcat(sqlStmt, " ON ");
+ strcat(sqlStmt, tableName);
+ strcat(sqlStmt, " TO ");
+ strcat(sqlStmt, userList);
+
+#ifdef _CONSOLE
+ cout << endl << sqlStmt << endl;
+#endif
+
+ return(ExecSql(sqlStmt));
+
+} // wxDB::Grant()
+
+/********** wxDB::CreateView() **********/
+bool wxDB::CreateView(char *viewName, char *colList, char *pSqlStmt)
+{
+ char sqlStmt[DB_MAX_STATEMENT_LEN];
+
+ // Drop the view first
+ sprintf(sqlStmt, "DROP VIEW %s", viewName);
+ if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt, SQL_NTS) != SQL_SUCCESS)
+ {
+ // Check for sqlState = S0002, "Table or view not found".
+ // Ignore this error, bomb out on any other error.
+ // SQL Sybase Anwhere v5.5 returns an access violation error here
+ // (sqlstate = 42000) rather than an S0002.
+ GetNextError(henv, hdbc, hstmt);
+ if (strcmp(sqlState, "S0002") && strcmp(sqlState, "42000"))
+ {
+ DispNextError();
+ DispAllErrors(henv, hdbc, hstmt);
+ RollbackTrans();
+ return(FALSE);
+ }
+ }
+
+#ifdef _CONSOLE
+ cout << endl << sqlStmt << endl;
+#endif
+
+ // Build the create view statement
+ strcpy(sqlStmt, "CREATE VIEW ");
+ strcat(sqlStmt, viewName);
+
+ if (strlen(colList))
+ {
+ strcat(sqlStmt, " (");
+ strcat(sqlStmt, colList);
+ strcat(sqlStmt, ")");
+ }
+
+ strcat(sqlStmt, " AS ");
+ strcat(sqlStmt, pSqlStmt);
+
+#ifdef _CONSOLE
+ cout << sqlStmt << endl;
+#endif
+
+ return(ExecSql(sqlStmt));
+
+} // wxDB::CreateView()
+
+/********** wxDB::ExecSql() **********/
+bool wxDB::ExecSql(char *pSqlStmt)
+{
+ if (SQLExecDirect(hstmt, (UCHAR FAR *) pSqlStmt, SQL_NTS) == SQL_SUCCESS)
+ return(TRUE);
+ else
+ {
+ DispAllErrors(henv, hdbc, hstmt);
+ return(FALSE);
+ }
+
+} // wxDB::ExecSql()
+
+/********** wxDB::GetColumns() **********/
+/*
+ * 1) The last array element of the tableName[] argument must be zero (null).
+ * This is how the end of the array is detected.
+ * 2) This function returns an array of CcolInf structures. If no columns
+ * were found, or an error occured, this pointer will be zero (null). THE
+ * CALLING FUNCTION IS RESPONSIBLE FOR DELETING THE MEMORY RETURNED WHEN IT
+ * IS FINISHED WITH IT. i.e.
+ *
+ * CcolInf *colInf = pDb->GetColumns(tableList);
+ * if (colInf)
+ * {
+ * // Use the column inf
+ * .......
+ * // Destroy the memory
+ * delete [] colInf;
+ * }
+ */
+CcolInf *wxDB::GetColumns(char *tableName[])
+{
+ UINT noCols = 0;
+ UINT colNo = 0;
+ CcolInf *colInf = 0;
+ RETCODE retcode;
+ SDWORD cb;
+ char tblName[DB_MAX_TABLE_NAME_LEN+1];
+ char colName[DB_MAX_COLUMN_NAME_LEN+1];
+ SWORD sqlDataType;
+
+ // Pass 1 - Determine how many columns there are.
+ // Pass 2 - Allocate the CcolInf array and fill in
+ // the array with the column information.
+ for (int pass = 1; pass <= 2; pass++)
+ {
+ if (pass == 2)
+ {
+ if (noCols == 0) // Probably a bogus table name(s)
+ break;
+ // Allocate n CcolInf objects to hold the column information
+ colInf = new CcolInf[noCols+1];
+ if (!colInf)
+ break;
+ // Mark the end of the array
+ strcpy(colInf[noCols].tableName, "");
+ strcpy(colInf[noCols].colName, "");
+ colInf[noCols].sqlDataType = 0;
+ }
+ // Loop through each table name
+ for (int tbl = 0; tableName[tbl]; tbl++)
+ {
+ SQLFreeStmt(hstmt, SQL_CLOSE);
+ retcode = SQLColumns(hstmt,
+ NULL, 0, // All qualifiers
+ NULL, 0, // All owners
+ (UCHAR *) tableName[tbl], SQL_NTS,
+ NULL, 0); // All columns
+ if (retcode != SQL_SUCCESS)
+ { // Error occured, abort
+ DispAllErrors(henv, hdbc, hstmt);
+ if (colInf)
+ delete [] colInf;
+ return(0);
+ }
+ SQLBindCol(hstmt, 3, SQL_C_CHAR, tblName, DB_MAX_TABLE_NAME_LEN+1, &cb);
+ SQLBindCol(hstmt, 4, SQL_C_CHAR, colName, DB_MAX_COLUMN_NAME_LEN+1, &cb);
+ SQLBindCol(hstmt, 5, SQL_C_SSHORT, &sqlDataType, 0, &cb);
+ while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
+ {
+ if (pass == 1) // First pass, just add up the number of columns
+ noCols++;
+ else // Pass 2; Fill in the array of structures
+ {
+ if (colNo < noCols) // Some extra error checking to prevent memory overwrites
+ {
+ strcpy(colInf[colNo].tableName, tblName);
+ strcpy(colInf[colNo].colName, colName);
+ colInf[colNo].sqlDataType = sqlDataType;
+ colNo++;
+ }
+ }
+ }
+ if (retcode != SQL_NO_DATA_FOUND)
+ { // Error occured, abort
+ DispAllErrors(henv, hdbc, hstmt);
+ if (colInf)
+ delete [] colInf;
+ return(0);
+ }
+ }
+ }
+
+ SQLFreeStmt(hstmt, SQL_CLOSE);
+ return colInf;
+
+} // wxDB::GetColumns()
+
+
+// Table name can refer to a table, view, alias or synonym. Returns true
+// if the object exists in the database. This function does not indicate
+// whether or not the user has privleges to query or perform other functions
+// on the table.
+bool wxDB::TableExists(char *tableName)
+{
+ assert(tableName && strlen(tableName));
+
+ SQLFreeStmt(hstmt, SQL_CLOSE);
+ RETCODE retcode = SQLTables(hstmt,
+ NULL, 0, // All qualifiers
+ NULL, 0, // All owners
+ (UCHAR FAR *)tableName, SQL_NTS,
+ NULL, 0); // All table types
+ if (retcode != SQL_SUCCESS)
+ return(DispAllErrors(henv, hdbc, hstmt));
+
+ if (SQLFetch(hstmt) != SQL_SUCCESS)
+ {
+ SQLFreeStmt(hstmt, SQL_CLOSE);
+ return(DispAllErrors(henv, hdbc, hstmt));
+ }
+
+ SQLFreeStmt(hstmt, SQL_CLOSE);
+ return(TRUE);
+
+} // wxDB::TableExists()
+
+
+/********** GetDbConnection() **********/
+wxDB *GetDbConnection(DbStuff *pDbStuff)
+{
+ DbList *pList;
+
+ // Scan the linked list searching for an available database connection
+ // that's already been opened but is currently not in use.
+ for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
+ {
+ // The database connection must be for the same datasource
+ // name and must currently not be in use.
+ if (pList->Free && (! strcmp(pDbStuff->Dsn, pList->Dsn))) // Found a free connection
+ {
+ pList->Free = FALSE;
+ return(pList->PtrDb);
+ }
+ }
+
+ // No available connections. A new connection must be made and
+ // appended to the end of the linked list.
+ if (PtrBegDbList)
+ {
+ // Find the end of the list
+ for (pList = PtrBegDbList; pList->PtrNext; pList = pList->PtrNext);
+ // Append a new list item
+ pList->PtrNext = new DbList;
+ pList->PtrNext->PtrPrev = pList;
+ pList = pList->PtrNext;
+ }
+ else // Empty list
+ {
+ // Create the first node on the list
+ pList = PtrBegDbList = new DbList;
+ pList->PtrPrev = 0;
+ }
+
+ // Initialize new node in the linked list
+ pList->PtrNext = 0;
+ pList->Free = FALSE;
+ strcpy(pList->Dsn, pDbStuff->Dsn);
+ pList->PtrDb = new wxDB(pDbStuff->Henv);
+
+ // Connect to the datasource
+ if (pList->PtrDb->Open(pDbStuff->Dsn, pDbStuff->Uid, pDbStuff->AuthStr))
+ return(pList->PtrDb);
+ else // Unable to connect, destroy list item
+ {
+ if (pList->PtrPrev)
+ pList->PtrPrev->PtrNext = 0;
+ else
+ PtrBegDbList = 0; // Empty list again
+ pList->PtrDb->CommitTrans(); // Commit any open transactions on wxDB object
+ pList->PtrDb->Close(); // Close the wxDB object
+ delete pList->PtrDb; // Deletes the wxDB object
+ delete pList; // Deletes the linked list object
+ return(0);
+ }
+
+} // GetDbConnection()
+
+/********** FreeDbConnection() **********/
+bool FreeDbConnection(wxDB *pDb)
+{
+ DbList *pList;
+
+ // Scan the linked list searching for the database connection
+ for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
+ {
+ if (pList->PtrDb == pDb) // Found it!!!
+ return(pList->Free = TRUE);
+ }
+
+ // Never found the database object, return failure
+ return(FALSE);
+
+} // FreeDbConnection()
+
+/********** CloseDbConnections() **********/
+void CloseDbConnections(void)
+{
+ DbList *pList, *pNext;
+
+ // Traverse the linked list closing database connections and freeing memory as I go.
+ for (pList = PtrBegDbList; pList; pList = pNext)
+ {
+ pNext = pList->PtrNext; // Save the pointer to next
+ pList->PtrDb->CommitTrans(); // Commit any open transactions on wxDB object
+ pList->PtrDb->Close(); // Close the wxDB object
+ delete pList->PtrDb; // Deletes the wxDB object
+ delete pList; // Deletes the linked list object
+ }
+
+ // Mark the list as empty
+ PtrBegDbList = 0;
+
+} // CloseDbConnections()
+
+/********** NumberDbConnectionsInUse() **********/
+int NumberDbConnectionsInUse(void)
+{
+ DbList *pList;
+ int cnt = 0;
+
+ // Scan the linked list counting db connections that are currently in use
+ for (pList = PtrBegDbList; pList; pList = pList->PtrNext)
+ {
+ if (pList->Free == FALSE)
+ cnt++;
+ }
+
+ return(cnt);
+
+} // NumberDbConnectionsInUse()
+
+/********** GetDataSource() **********/
+bool GetDataSource(HENV henv, char *Dsn, SWORD DsnMax, char *DsDesc, SWORD DsDescMax,
+ UWORD direction)
+{
+ SWORD cb;
+
+ if (SQLDataSources(henv, direction, (UCHAR FAR *) Dsn, DsnMax, &cb,
+ (UCHAR FAR *) DsDesc, DsDescMax, &cb) == SQL_SUCCESS)
+ return(TRUE);
+ else
+ return(FALSE);
+
+} // GetDataSource()
+
+#endif
+ // USE_ODBC
--- /dev/null
+///////////////////////////////////////////////////////////////////////////////
+// Name: table.cpp
+// Purpose: Implementation of the wxTable class.
+// Author: Doug Card
+// Modified by:
+// Created: 9.96
+// RCS-ID: $Id$
+// Copyright: (c) 1996 Remstar International, Inc.
+// Licence: wxWindows licence, plus:
+// Notice: This class library and its intellectual design are free of charge for use,
+// modification, enhancement, debugging under the following conditions:
+// 1) These classes may only be used as part of the implementation of a
+// wxWindows-based application
+// 2) All enhancements and bug fixes are to be submitted back to the wxWindows
+// user groups free of all charges for use with the wxWindows library.
+// 3) These classes may not be distributed as part of any other class library,
+// DLL, text (written or electronic), other than a complete distribution of
+// the wxWindows GUI development toolkit.
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+// SYNOPSIS START
+// SYNOPSIS STOP
+*/
+
+/*
+#ifdef _CONSOLE
+ #include <iostream.h>
+#endif
+*/
+
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+ #pragma hdrstop
+#endif //__BORLANDC__
+
+#ifndef WX_PRECOMP
+ #include <wx/wx.h>
+#endif //WX_PRECOMP
+
+#if USE_ODBC
+
+#include <wx/dbtable.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __WXUNIX__
+// The HPUX preprocessor lines below were commented out on 8/20/97
+// because macros.h currently redefines DEBUG and is unneeded.
+// # ifdef HPUX
+// # include <macros.h>
+// # endif
+# ifdef __WXLINUX__
+# include <sys/minmax.h>
+# endif
+#endif
+
+/********** wxTable::wxTable() **********/
+wxTable::wxTable(wxDB *pwxDB, const char *tblName, const int nCols, const char *qryTblName)
+{
+ // Assign member variables
+ pDb = pwxDB; // Pointer to the wxDB object
+ strcpy(tableName, tblName); // Table Name
+ if (qryTblName) // Name of the table/view to query
+ strcpy(queryTableName, qryTblName);
+ else
+ strcpy(queryTableName, tblName);
+
+ noCols = nCols; // No. of cols in the table
+ where = 0; // Where clause
+ orderBy = 0; // Order By clause
+ selectForUpdate = FALSE; // SELECT ... FOR UPDATE; Indicates whether to include the FOR UPDATE phrase
+
+ // Grab the HENV and HDBC from the wxDB object
+ henv = pDb->henv;
+ hdbc = pDb->hdbc;
+
+ // Allocate space for column definitions
+ if (noCols)
+ colDefs = new CcolDef[noCols]; // Points to the first column defintion
+ else
+ colDefs = 0;
+
+ // Allocate statement handles for the table
+ if (SQLAllocStmt(hdbc, &c0) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc);
+ if (SQLAllocStmt(hdbc, &c1) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc);
+ if (SQLAllocStmt(hdbc, &c2) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc);
+// if (SQLAllocStmt(hdbc, &c3) != SQL_SUCCESS)
+// pDb->DispAllErrors(henv, hdbc);
+// if (SQLAllocStmt(hdbc, &c4) != SQL_SUCCESS)
+// pDb->DispAllErrors(henv, hdbc);
+// if (SQLAllocStmt(hdbc, &c5) != SQL_SUCCESS)
+// pDb->DispAllErrors(henv, hdbc);
+ // Allocate a separate statement handle for performing inserts
+ if (SQLAllocStmt(hdbc, &hstmtInsert) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc);
+ // Allocate a separate statement handle for performing deletes
+ if (SQLAllocStmt(hdbc, &hstmtDelete) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc);
+ // Allocate a separate statement handle for performing updates
+ if (SQLAllocStmt(hdbc, &hstmtUpdate) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc);
+ // Allocate a separate statement handle for performing count(*) function
+ if (SQLAllocStmt(hdbc, &hstmtCount) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc);
+
+ // Set the cursor type for the statement handles
+ UDWORD cursorType = SQL_CURSOR_STATIC;
+ if (SQLSetStmtOption(c1, SQL_CURSOR_TYPE, cursorType) != SQL_SUCCESS)
+ {
+ // Check to see if cursor type is supported
+ pDb->GetNextError(henv, hdbc, c1);
+ if (! strcmp(pDb->sqlState, "01S02")) // Option Value Changed
+ {
+ // Datasource does not support static cursors. Driver
+ // will substitute a cursor type. Call SQLGetStmtOption()
+ // to determine which cursor type was selected.
+ if (SQLGetStmtOption(c1, SQL_CURSOR_TYPE, &cursorType) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc, c1);
+#ifdef _CONSOLE
+ cout << "Static cursor changed to: ";
+ switch(cursorType)
+ {
+ case SQL_CURSOR_FORWARD_ONLY:
+ cout << "Forward Only"; break;
+ case SQL_CURSOR_STATIC:
+ cout << "Static"; break;
+ case SQL_CURSOR_KEYSET_DRIVEN:
+ cout << "Keyset Driven"; break;
+ case SQL_CURSOR_DYNAMIC:
+ cout << "Dynamic"; break;
+ }
+ cout << endl << endl;
+#endif
+ }
+ else
+ {
+ pDb->DispNextError();
+ pDb->DispAllErrors(henv, hdbc, c1);
+ }
+ }
+#ifdef _CONSOLE
+ else
+ cout << "Cursor Type set to STATIC" << endl << endl;
+#endif
+
+ if (SQLSetStmtOption(c0, SQL_CURSOR_TYPE, cursorType) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc, c0);
+ if (SQLSetStmtOption(c2, SQL_CURSOR_TYPE, cursorType) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc, c2);
+// if (SQLSetStmtOption(c3, SQL_CURSOR_TYPE, cursorType) != SQL_SUCCESS)
+// pDb->DispAllErrors(henv, hdbc, c3);
+// if (SQLSetStmtOption(c4, SQL_CURSOR_TYPE, cursorType) != SQL_SUCCESS)
+// pDb->DispAllErrors(henv, hdbc, c4);
+// if (SQLSetStmtOption(c5, SQL_CURSOR_TYPE, cursorType) != SQL_SUCCESS)
+// pDb->DispAllErrors(henv, hdbc, c5);
+
+ // Set the cursor type for the INSERT statement handle
+ if (SQLSetStmtOption(hstmtInsert, SQL_CURSOR_TYPE, SQL_CURSOR_FORWARD_ONLY) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc, hstmtInsert);
+ // Set the cursor type for the DELETE statement handle
+ if (SQLSetStmtOption(hstmtDelete, SQL_CURSOR_TYPE, SQL_CURSOR_FORWARD_ONLY) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc, hstmtDelete);
+ // Set the cursor type for the UPDATE statement handle
+ if (SQLSetStmtOption(hstmtUpdate, SQL_CURSOR_TYPE, SQL_CURSOR_FORWARD_ONLY) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc, hstmtUpdate);
+ // Set the cursor type for the COUNT(*) statement handle
+ if (SQLSetStmtOption(hstmtCount, SQL_CURSOR_TYPE, SQL_CURSOR_FORWARD_ONLY) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc, hstmtCount);
+
+ // Copy cursor 1 to the default cursor
+ hstmt = c1;
+ currCursorNo = DB_CURSOR1;
+
+} // wxTable::wxTable()
+
+/********** wxTable::~wxTable() **********/
+wxTable::~wxTable()
+{
+ // Delete memory allocated for column definitions
+ if (colDefs)
+ delete [] colDefs;
+
+ // Free statement handles
+ if (SQLFreeStmt(c0, SQL_DROP) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc);
+ if (SQLFreeStmt(c1, SQL_DROP) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc);
+ if (SQLFreeStmt(c2, SQL_DROP) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc);
+// if (SQLFreeStmt(c3, SQL_DROP) != SQL_SUCCESS)
+// pDb->DispAllErrors(henv, hdbc);
+// if (SQLFreeStmt(c4, SQL_DROP) != SQL_SUCCESS)
+// pDb->DispAllErrors(henv, hdbc);
+// if (SQLFreeStmt(c5, SQL_DROP) != SQL_SUCCESS)
+// pDb->DispAllErrors(henv, hdbc);
+ if (SQLFreeStmt(hstmtInsert, SQL_DROP) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc);
+ if (SQLFreeStmt(hstmtDelete, SQL_DROP) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc);
+ if (SQLFreeStmt(hstmtUpdate, SQL_DROP) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc);
+ if (SQLFreeStmt(hstmtCount, SQL_DROP) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc);
+
+} // wxTable::~wxTable()
+
+/********** wxTable::Open() **********/
+bool wxTable::Open(void)
+{
+ int i;
+ char sqlStmt[DB_MAX_STATEMENT_LEN];
+
+ // Verify that the table exists in the database
+ if (!pDb->TableExists(tableName))
+ {
+ char s[128];
+ sprintf(s, "Error opening '%s', table/view does not exist in the database.", tableName);
+ pDb->LogError(s);
+ return(FALSE);
+ }
+
+ // Bind the member variables for field exchange between
+ // the wxTable object and the ODBC record.
+ if(! bindInsertParams()) // Inserts
+ return(FALSE);
+ if(! bindUpdateParams()) // Updates
+ return(FALSE);
+ if(! bindCols(c0)) // Selects
+ return(FALSE);
+ if(! bindCols(c1))
+ return(FALSE);
+ if(! bindCols(c2))
+ return(FALSE);
+// if(! bindCols(c3))
+// return(FALSE);
+// if(! bindCols(c4))
+// return(FALSE);
+// if(! bindCols(c5))
+// return(FALSE);
+
+ // Build an insert statement using parameter markers
+ if (noCols > 0)
+ {
+ bool needComma = FALSE;
+ sprintf(sqlStmt, "INSERT INTO %s (", tableName);
+ for (i = 0; i < noCols; i++)
+ {
+ if (! colDefs[i].InsertAllowed)
+ continue;
+ if (needComma)
+ strcat(sqlStmt, ",");
+ strcat(sqlStmt, colDefs[i].ColName);
+ needComma = TRUE;
+ }
+ needComma = FALSE;
+ strcat(sqlStmt, ") VALUES (");
+ for (i = 0; i < noCols; i++)
+ {
+ if (! colDefs[i].InsertAllowed)
+ continue;
+ if (needComma)
+ strcat(sqlStmt, ",");
+ strcat(sqlStmt, "?");
+ needComma = TRUE;
+ }
+ strcat(sqlStmt, ")");
+
+ // Prepare the insert statement for execution
+ if (SQLPrepare(hstmtInsert, (UCHAR FAR *) sqlStmt, SQL_NTS) != SQL_SUCCESS)
+ return(pDb->DispAllErrors(henv, hdbc, hstmtInsert));
+ }
+
+ // Completed successfully
+ return(TRUE);
+
+} // wxTable::Open()
+
+/********** wxTable::Query() **********/
+bool wxTable::Query(bool forUpdate, bool distinct)
+{
+
+ return(query(DB_SELECT_WHERE, forUpdate, distinct));
+
+} // wxTable::Query()
+
+/********** wxTable::QueryBySqlStmt() **********/
+bool wxTable::QueryBySqlStmt(char *pSqlStmt)
+{
+
+ return(query(DB_SELECT_STATEMENT, FALSE, FALSE, pSqlStmt));
+
+} // wxTable::QueryBySqlStmt()
+
+/********** wxTable::QueryMatching() **********/
+bool wxTable::QueryMatching(bool forUpdate, bool distinct)
+{
+
+ return(query(DB_SELECT_MATCHING, forUpdate, distinct));
+
+} // wxTable::QueryMatching()
+
+/********** wxTable::QueryOnKeyFields() **********/
+bool wxTable::QueryOnKeyFields(bool forUpdate, bool distinct)
+{
+
+ return(query(DB_SELECT_KEYFIELDS, forUpdate, distinct));
+
+} // wxTable::QueryOnKeyFields()
+
+/********** wxTable::query() **********/
+bool wxTable::query(int queryType, bool forUpdate, bool distinct, char *pSqlStmt)
+{
+ char sqlStmt[DB_MAX_STATEMENT_LEN];
+
+ // Set the selectForUpdate member variable
+ if (forUpdate)
+ // The user may wish to select for update, but the DBMS may not be capable
+ selectForUpdate = CanSelectForUpdate();
+ else
+ selectForUpdate = FALSE;
+
+ // Set the SQL SELECT string
+ if (queryType != DB_SELECT_STATEMENT) // A select statement was not passed in,
+ GetSelectStmt(sqlStmt, queryType, distinct); // so generate a select statement.
+
+ // Make sure the cursor is closed first
+ if (! CloseCursor(hstmt))
+ return(FALSE);
+
+ // Execute the SQL SELECT statement
+ if (SQLExecDirect(hstmt, (UCHAR FAR *) (queryType == DB_SELECT_STATEMENT ? pSqlStmt : sqlStmt),
+ SQL_NTS) != SQL_SUCCESS)
+ return(pDb->DispAllErrors(henv, hdbc, hstmt));
+
+ // Completed successfully
+ return(TRUE);
+
+} // wxTable::query()
+
+/********** wxTable::GetSelectStmt() **********/
+void wxTable::GetSelectStmt(char *pSqlStmt, int typeOfSelect, bool distinct)
+{
+ char whereClause[DB_MAX_WHERE_CLAUSE_LEN];
+
+ whereClause[0] = 0;
+
+ // Build a select statement to query the database
+ strcpy(pSqlStmt, "SELECT ");
+
+ // SELECT DISTINCT values only?
+ if (distinct)
+ strcat(pSqlStmt, "DISTINCT ");
+
+ // Add the column list
+ for (int i = 0; i < noCols; i++)
+ {
+ strcat(pSqlStmt, colDefs[i].ColName);
+ if (i + 1 < noCols)
+ strcat(pSqlStmt, ",");
+ }
+
+ // If the datasource supports ROWID, get this column as well. Exception: Don't retrieve
+ // the ROWID if querying distinct records. The rowid will always be unique.
+ if (!distinct && CanUpdByROWID())
+ strcat(pSqlStmt, ",ROWID");
+
+ // Append the FROM tablename portion
+ strcat(pSqlStmt, " FROM ");
+ strcat(pSqlStmt, queryTableName);
+
+ // Append the WHERE clause. Either append the where clause for the class
+ // or build a where clause. The typeOfSelect determines this.
+ switch(typeOfSelect)
+ {
+ case DB_SELECT_WHERE:
+ if (where && strlen(where)) // May not want a where clause!!!
+ {
+ strcat(pSqlStmt, " WHERE ");
+ strcat(pSqlStmt, where);
+ }
+ break;
+ case DB_SELECT_KEYFIELDS:
+ GetWhereClause(whereClause, DB_WHERE_KEYFIELDS);
+ if (strlen(whereClause))
+ {
+ strcat(pSqlStmt, " WHERE ");
+ strcat(pSqlStmt, whereClause);
+ }
+ break;
+ case DB_SELECT_MATCHING:
+ GetWhereClause(whereClause, DB_WHERE_MATCHING);
+ if (strlen(whereClause))
+ {
+ strcat(pSqlStmt, " WHERE ");
+ strcat(pSqlStmt, whereClause);
+ }
+ break;
+ }
+
+ // Append the ORDER BY clause
+ if (orderBy && strlen(orderBy))
+ {
+ strcat(pSqlStmt, " ORDER BY ");
+ strcat(pSqlStmt, orderBy);
+ }
+
+ // SELECT FOR UPDATE if told to do so and the datasource is capable
+ if (selectForUpdate && CanSelectForUpdate())
+ strcat(pSqlStmt, " FOR UPDATE");
+
+} // wxTable::GetSelectStmt()
+
+/********** wxTable::getRec() **********/
+bool wxTable::getRec(UWORD fetchType)
+{
+ RETCODE retcode;
+
+#ifndef FWD_ONLY_CURSORS
+ // Fetch the NEXT, PREV, FIRST or LAST record, depending on fetchType
+ UDWORD cRowsFetched;
+ UWORD rowStatus;
+ if ((retcode = SQLExtendedFetch(hstmt, fetchType, 0, &cRowsFetched, &rowStatus)) != SQL_SUCCESS)
+ if (retcode == SQL_NO_DATA_FOUND)
+ return(FALSE);
+ else
+ return(pDb->DispAllErrors(henv, hdbc, hstmt));
+#else
+ // Fetch the next record from the record set
+ if ((retcode = SQLFetch(hstmt)) != SQL_SUCCESS)
+ if (retcode == SQL_NO_DATA_FOUND)
+ return(FALSE);
+ else
+ return(pDb->DispAllErrors(henv, hdbc, hstmt));
+#endif
+
+ // Completed successfully
+ return(TRUE);
+
+} // wxTable::getRec()
+
+/********** wxTable::GetRowNum() **********/
+UWORD wxTable::GetRowNum(void)
+{
+ UDWORD rowNum;
+
+ if (SQLGetStmtOption(hstmt, SQL_ROW_NUMBER, &rowNum) != SQL_SUCCESS)
+ {
+ pDb->DispAllErrors(henv, hdbc, hstmt);
+ return(0);
+ }
+
+ // Completed successfully
+ return((UWORD) rowNum);
+
+} // wxTable::GetRowNum()
+
+/********** wxTable::bindInsertParams() **********/
+bool wxTable::bindInsertParams(void)
+{
+ SWORD fSqlType;
+ UDWORD precision;
+ SWORD scale;
+
+//glt CcolDef *tColDef;
+
+ // Bind each column (that can be inserted) of the table to a parameter marker
+ for (int i = 0; i < noCols; i++)
+ {
+//glt tColDef = &colDefs[i];
+ if (! colDefs[i].InsertAllowed)
+ continue;
+ switch(colDefs[i].DbDataType)
+ {
+ case DB_DATA_TYPE_VARCHAR:
+ fSqlType = pDb->typeInfVarchar.FsqlType;
+ precision = colDefs[i].SzDataObj;
+ scale = 0;
+ colDefs[i].CbValue = SQL_NTS;
+ break;
+ case DB_DATA_TYPE_INTEGER:
+ fSqlType = pDb->typeInfInteger.FsqlType;
+ precision = pDb->typeInfInteger.Precision;
+ scale = 0;
+ colDefs[i].CbValue = 0;
+ break;
+ case DB_DATA_TYPE_FLOAT:
+ fSqlType = pDb->typeInfFloat.FsqlType;
+ precision = pDb->typeInfFloat.Precision;
+ scale = pDb->typeInfFloat.MaximumScale;
+ // SQL Sybase Anywhere v5.5 returned a negative number for the
+ // MaxScale. This caused ODBC to kick out an error on ibscale.
+ // I check for this here and set the scale = precision.
+ //if (scale < 0)
+ // scale = (short) precision;
+ colDefs[i].CbValue = 0;
+ break;
+ case DB_DATA_TYPE_DATE:
+ fSqlType = pDb->typeInfDate.FsqlType;
+ precision = pDb->typeInfDate.Precision;
+ scale = 0;
+ colDefs[i].CbValue = 0;
+ break;
+ }
+ if (SQLBindParameter(hstmtInsert, i+1, SQL_PARAM_INPUT, colDefs[i].SqlCtype,
+ fSqlType, precision, scale, colDefs[i].PtrDataObj,
+ precision+1,&colDefs[i].CbValue) != SQL_SUCCESS)
+ return(pDb->DispAllErrors(henv, hdbc, hstmtInsert));
+ }
+
+ // Completed successfully
+ return(TRUE);
+
+} // wxTable::bindInsertParams()
+
+/********** wxTable::bindUpdateParams() **********/
+bool wxTable::bindUpdateParams(void)
+{
+ SWORD fSqlType;
+ UDWORD precision;
+ SWORD scale;
+
+ // Bind each UPDATEABLE column of the table to a parameter marker
+ for (int i = 0, colNo = 1; i < noCols; i++)
+ {
+ if (! colDefs[i].Updateable)
+ continue;
+ switch(colDefs[i].DbDataType)
+ {
+ case DB_DATA_TYPE_VARCHAR:
+ fSqlType = pDb->typeInfVarchar.FsqlType;
+ precision = colDefs[i].SzDataObj;
+ scale = 0;
+ colDefs[i].CbValue = SQL_NTS;
+ break;
+ case DB_DATA_TYPE_INTEGER:
+ fSqlType = pDb->typeInfInteger.FsqlType;
+ precision = pDb->typeInfInteger.Precision;
+ scale = 0;
+ colDefs[i].CbValue = 0;
+ break;
+ case DB_DATA_TYPE_FLOAT:
+ fSqlType = pDb->typeInfFloat.FsqlType;
+ precision = pDb->typeInfFloat.Precision;
+ scale = pDb->typeInfFloat.MaximumScale;
+ // SQL Sybase Anywhere v5.5 returned a negative number for the
+ // MaxScale. This caused ODBC to kick out an error on ibscale.
+ // I check for this here and set the scale = precision.
+ //if (scale < 0)
+ // scale = (short) precision;
+ colDefs[i].CbValue = 0;
+ break;
+ case DB_DATA_TYPE_DATE:
+ fSqlType = pDb->typeInfDate.FsqlType;
+ precision = pDb->typeInfDate.Precision;
+ scale = 0;
+ colDefs[i].CbValue = 0;
+ break;
+ }
+ if (SQLBindParameter(hstmtUpdate, colNo++, SQL_PARAM_INPUT, colDefs[i].SqlCtype,
+ fSqlType, precision, scale, colDefs[i].PtrDataObj,
+ precision+1, &colDefs[i].CbValue) != SQL_SUCCESS)
+ return(pDb->DispAllErrors(henv, hdbc, hstmtUpdate));
+ }
+
+ // Completed successfully
+ return(TRUE);
+
+} // wxTable::bindUpdateParams()
+
+/********** wxTable::bindCols() **********/
+bool wxTable::bindCols(HSTMT cursor)
+{
+ static SDWORD cb;
+
+ // Bind each column of the table to a memory address for fetching data
+ for (int i = 0; i < noCols; i++)
+ {
+ if (SQLBindCol(cursor, i+1, colDefs[i].SqlCtype, colDefs[i].PtrDataObj,
+ colDefs[i].SzDataObj, &cb) != SQL_SUCCESS)
+ return(pDb->DispAllErrors(henv, hdbc, cursor));
+ }
+
+ // Completed successfully
+ return(TRUE);
+
+} // wxTable::bindCols()
+
+/********** wxTable::CloseCursor() **********/
+bool wxTable::CloseCursor(HSTMT cursor)
+{
+ if (SQLFreeStmt(cursor, SQL_CLOSE) != SQL_SUCCESS)
+ return(pDb->DispAllErrors(henv, hdbc, cursor));
+
+ // Completed successfully
+ return(TRUE);
+
+} // wxTable::CloseCursor()
+
+/********** wxTable::CreateTable() **********/
+bool wxTable::CreateTable(void)
+{
+ int i, j;
+ char sqlStmt[DB_MAX_STATEMENT_LEN];
+
+#ifdef _CONSOLE
+ cout << "Creating Table " << tableName << "..." << endl;
+#endif
+
+ // Drop the table first
+ sprintf(sqlStmt, "DROP TABLE %s", tableName);
+ if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt, SQL_NTS) != SQL_SUCCESS)
+ {
+ // Check for sqlState = S0002, "Table or view not found".
+ // Ignore this error, bomb out on any other error.
+ // SQL Sybase Anwhere v5.5 returns an access violation error here
+ // (sqlstate = 42000) rather than an S0002.
+ pDb->GetNextError(henv, hdbc, hstmt);
+ if (strcmp(pDb->sqlState, "S0002") && strcmp(pDb->sqlState, "42000"))
+ {
+ pDb->DispNextError();
+ pDb->DispAllErrors(henv, hdbc, hstmt);
+ pDb->RollbackTrans();
+ CloseCursor(hstmt);
+ return(FALSE);
+ }
+ }
+
+ // Commit the transaction and close the cursor
+ if (! pDb->CommitTrans())
+ return(FALSE);
+ if (! CloseCursor(hstmt))
+ return(FALSE);
+
+ // Create the table
+#ifdef _CONSOLE
+ for (i = 0; i < noCols; i++)
+ {
+ // Exclude derived columns since they are NOT part of the base table
+ if (colDefs[i].DerivedCol)
+ continue;
+ cout << i + 1 << ": " << colDefs[i].ColName << "; ";
+ switch(colDefs[i].DbDataType)
+ {
+ case DB_DATA_TYPE_VARCHAR:
+ cout << pDb->typeInfVarchar.TypeName << "(" << colDefs[i].SzDataObj << ")";
+ break;
+ case DB_DATA_TYPE_INTEGER:
+ cout << pDb->typeInfInteger.TypeName;
+ break;
+ case DB_DATA_TYPE_FLOAT:
+ cout << pDb->typeInfFloat.TypeName;
+ break;
+ case DB_DATA_TYPE_DATE:
+ cout << pDb->typeInfDate.TypeName;
+ break;
+ }
+ cout << endl;
+ }
+#endif
+
+ // Build a CREATE TABLE string from the colDefs structure.
+ bool needComma = FALSE;
+ sprintf(sqlStmt, "CREATE TABLE %s (", tableName);
+ for (i = 0; i < noCols; i++)
+ {
+ // Exclude derived columns since they are NOT part of the base table
+ if (colDefs[i].DerivedCol)
+ continue;
+ // Comma Delimiter
+ if (needComma)
+ strcat(sqlStmt, ",");
+ // Column Name
+ strcat(sqlStmt, colDefs[i].ColName);
+ strcat(sqlStmt, " ");
+ // Column Type
+ switch(colDefs[i].DbDataType)
+ {
+ case DB_DATA_TYPE_VARCHAR:
+ strcat(sqlStmt, pDb->typeInfVarchar.TypeName); break;
+ case DB_DATA_TYPE_INTEGER:
+ strcat(sqlStmt, pDb->typeInfInteger.TypeName); break;
+ case DB_DATA_TYPE_FLOAT:
+ strcat(sqlStmt, pDb->typeInfFloat.TypeName); break;
+ case DB_DATA_TYPE_DATE:
+ strcat(sqlStmt, pDb->typeInfDate.TypeName); break;
+ }
+ // For varchars, append the size of the string
+ if (colDefs[i].DbDataType == DB_DATA_TYPE_VARCHAR)
+ {
+ char s[10];
+ // strcat(sqlStmt, "(");
+ // strcat(sqlStmt, itoa(colDefs[i].SzDataObj, s, 10));
+ // strcat(sqlStmt, ")");
+ sprintf(s, "(%d)", colDefs[i].SzDataObj);
+ strcat(sqlStmt, s);
+ }
+ needComma = TRUE;
+ }
+ // If there is a primary key defined, include it in the create statement
+ for (i = j = 0; i < noCols; i++)
+ {
+ if (colDefs[i].KeyField)
+ {
+ j++;
+ break;
+ }
+ }
+ if (j) // Found a keyfield
+ {
+ strcat(sqlStmt, ",CONSTRAINT ");
+ strcat(sqlStmt, tableName);
+ strcat(sqlStmt, "_PIDX PRIMARY KEY (");
+ // List column name(s) of column(s) comprising the primary key
+ for (i = j = 0; i < noCols; i++)
+ {
+ if (colDefs[i].KeyField)
+ {
+ if (j++) // Multi part key, comma separate names
+ strcat(sqlStmt, ",");
+ strcat(sqlStmt, colDefs[i].ColName);
+ }
+ }
+ strcat(sqlStmt, ")");
+ }
+ // Append the closing parentheses for the create table statement
+ strcat(sqlStmt, ")");
+
+#ifdef _CONSOLE
+ cout << endl << sqlStmt << endl;
+#endif
+
+ // Execute the CREATE TABLE statement
+ if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt, SQL_NTS) != SQL_SUCCESS)
+ {
+ pDb->DispAllErrors(henv, hdbc, hstmt);
+ pDb->RollbackTrans();
+ CloseCursor(hstmt);
+ return(FALSE);
+ }
+
+ // Commit the transaction and close the cursor
+ if (! pDb->CommitTrans())
+ return(FALSE);
+ if (! CloseCursor(hstmt))
+ return(FALSE);
+
+ // Database table created successfully
+ return(TRUE);
+
+} // wxTable::CreateTable()
+
+/********** wxTable::CreateIndex() **********/
+bool wxTable::CreateIndex(char * idxName, bool unique, int noIdxCols, CidxDef *pIdxDefs)
+{
+ char sqlStmt[DB_MAX_STATEMENT_LEN];
+
+ // Build a CREATE INDEX statement
+ strcpy(sqlStmt, "CREATE ");
+ if (unique)
+ strcat(sqlStmt, "UNIQUE ");
+
+ strcat(sqlStmt, "INDEX ");
+ strcat(sqlStmt, idxName);
+ strcat(sqlStmt, " ON ");
+ strcat(sqlStmt, tableName);
+ strcat(sqlStmt, " (");
+
+ // Append list of columns making up index
+ for (int i = 0; i < noIdxCols; i++)
+ {
+ strcat(sqlStmt, pIdxDefs[i].ColName);
+ if (pIdxDefs[i].Ascending)
+ strcat(sqlStmt, " ASC");
+ else
+ strcat(sqlStmt, " DESC");
+ if ((i + 1) < noIdxCols)
+ strcat(sqlStmt, ",");
+ }
+
+ // Append closing parentheses
+ strcat(sqlStmt, ")");
+
+#ifdef _CONSOLE
+ cout << endl << sqlStmt << endl << endl;
+#endif
+
+ // Execute the CREATE INDEX statement
+ if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt, SQL_NTS) != SQL_SUCCESS)
+ {
+ pDb->DispAllErrors(henv, hdbc, hstmt);
+ pDb->RollbackTrans();
+ CloseCursor(hstmt);
+ return(FALSE);
+ }
+
+ // Commit the transaction and close the cursor
+ if (! pDb->CommitTrans())
+ return(FALSE);
+ if (! CloseCursor(hstmt))
+ return(FALSE);
+
+ // Index Created Successfully
+ return(TRUE);
+
+} // wxTable::CreateIndex()
+
+/********** wxTable::Insert() **********/
+int wxTable::Insert(void)
+{
+ // Insert the record by executing the already prepared insert statement
+ if (SQLExecute(hstmtInsert) != SQL_SUCCESS)
+ {
+ // Check to see if integrity constraint was violated
+ pDb->GetNextError(henv, hdbc, hstmtInsert);
+ if (! strcmp(pDb->sqlState, "23000")) // Integrity constraint violated
+ return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL);
+ else
+ {
+ pDb->DispNextError();
+ pDb->DispAllErrors(henv, hdbc, hstmtInsert);
+ return(DB_FAILURE);
+ }
+ }
+
+ // Record inserted into the datasource successfully
+ return(DB_SUCCESS);
+
+} // wxTable::Insert()
+
+/********** wxTable::Update(pSqlStmt) **********/
+bool wxTable::Update(char *pSqlStmt)
+{
+
+ return(execUpdate(pSqlStmt));
+
+} // wxTable::Update(pSqlStmt)
+
+/********** wxTable::Update() **********/
+bool wxTable::Update(void)
+{
+ char sqlStmt[DB_MAX_STATEMENT_LEN];
+
+ // Build the SQL UPDATE statement
+ GetUpdateStmt(sqlStmt, DB_UPD_KEYFIELDS);
+
+#ifdef _CONSOLE
+ cout << endl << sqlStmt << endl << endl;
+#endif
+
+ // Execute the SQL UPDATE statement
+ return(execUpdate(sqlStmt));
+
+} // wxTable::Update()
+
+/********** wxTable::UpdateWhere() **********/
+bool wxTable::UpdateWhere(char *pWhereClause)
+{
+ char sqlStmt[DB_MAX_STATEMENT_LEN];
+
+ // Build the SQL UPDATE statement
+ GetUpdateStmt(sqlStmt, DB_UPD_WHERE, pWhereClause);
+
+#ifdef _CONSOLE
+ cout << endl << sqlStmt << endl << endl;
+#endif
+
+ // Execute the SQL UPDATE statement
+ return(execUpdate(sqlStmt));
+
+} // wxTable::UpdateWhere()
+
+/********** wxTable::Delete() **********/
+bool wxTable::Delete(void)
+{
+ char sqlStmt[DB_MAX_STATEMENT_LEN];
+
+ // Build the SQL DELETE statement
+ GetDeleteStmt(sqlStmt, DB_DEL_KEYFIELDS);
+
+ // Execute the SQL DELETE statement
+ return(execDelete(sqlStmt));
+
+} // wxTable::Delete()
+
+/********** wxTable::DeleteWhere() **********/
+bool wxTable::DeleteWhere(char *pWhereClause)
+{
+ char sqlStmt[DB_MAX_STATEMENT_LEN];
+
+ // Build the SQL DELETE statement
+ GetDeleteStmt(sqlStmt, DB_DEL_WHERE, pWhereClause);
+
+ // Execute the SQL DELETE statement
+ return(execDelete(sqlStmt));
+
+} // wxTable::DeleteWhere()
+
+/********** wxTable::DeleteMatching() **********/
+bool wxTable::DeleteMatching(void)
+{
+ char sqlStmt[DB_MAX_STATEMENT_LEN];
+
+ // Build the SQL DELETE statement
+ GetDeleteStmt(sqlStmt, DB_DEL_MATCHING);
+
+ // Execute the SQL DELETE statement
+ return(execDelete(sqlStmt));
+
+} // wxTable::DeleteMatching()
+
+/********** wxTable::execDelete() **********/
+bool wxTable::execDelete(char *pSqlStmt)
+{
+ // Execute the DELETE statement
+ if (SQLExecDirect(hstmtDelete, (UCHAR FAR *) pSqlStmt, SQL_NTS) != SQL_SUCCESS)
+ return(pDb->DispAllErrors(henv, hdbc, hstmtDelete));
+
+ // Record deleted successfully
+ return(TRUE);
+
+} // wxTable::execDelete()
+
+/********** wxTable::execUpdate() **********/
+bool wxTable::execUpdate(char *pSqlStmt)
+{
+ // Execute the UPDATE statement
+ if (SQLExecDirect(hstmtUpdate, (UCHAR FAR *) pSqlStmt, SQL_NTS) != SQL_SUCCESS)
+ return(pDb->DispAllErrors(henv, hdbc, hstmtUpdate));
+
+ // Record deleted successfully
+ return(TRUE);
+
+} // wxTable::execUpdate()
+
+/********** wxTable::GetUpdateStmt() **********/
+void wxTable::GetUpdateStmt(char *pSqlStmt, int typeOfUpd, char *pWhereClause)
+{
+ char whereClause[DB_MAX_WHERE_CLAUSE_LEN];
+ bool firstColumn = TRUE;
+
+ whereClause[0] = 0;
+ sprintf(pSqlStmt, "UPDATE %s SET ", tableName);
+
+ // Append a list of columns to be updated
+ for (int i = 0; i < noCols; i++)
+ {
+ // Only append Updateable columns
+ if (colDefs[i].Updateable)
+ {
+ if (! firstColumn)
+ strcat(pSqlStmt, ",");
+ else
+ firstColumn = FALSE;
+ strcat(pSqlStmt, colDefs[i].ColName);
+ strcat(pSqlStmt, " = ?");
+ }
+ }
+
+ // Append the WHERE clause to the SQL UPDATE statement
+ strcat(pSqlStmt, " WHERE ");
+ switch(typeOfUpd)
+ {
+ case DB_UPD_KEYFIELDS:
+ // If the datasource supports the ROWID column, build
+ // the where on ROWID for efficiency purposes.
+ // e.g. UPDATE PARTS SET C1 = ?, C2 = ? WHERE ROWID = '111.222.333'
+ if (CanUpdByROWID())
+ {
+ SDWORD cb;
+ char rowid[ROWID_LEN];
+
+ // Get the ROWID value. If not successful retreiving the ROWID,
+ // simply fall down through the code and build the WHERE clause
+ // based on the key fields.
+ if (SQLGetData(hstmt, noCols+1, SQL_C_CHAR, rowid, ROWID_LEN, &cb) == SQL_SUCCESS)
+ {
+ strcat(pSqlStmt, "ROWID = '");
+ strcat(pSqlStmt, rowid);
+ strcat(pSqlStmt, "'");
+ break;
+ }
+ }
+ // Unable to delete by ROWID, so build a WHERE
+ // clause based on the keyfields.
+ GetWhereClause(whereClause, DB_WHERE_KEYFIELDS);
+ strcat(pSqlStmt, whereClause);
+ break;
+ case DB_UPD_WHERE:
+ strcat(pSqlStmt, pWhereClause);
+ break;
+ }
+
+} // GetUpdateStmt()
+
+/********** wxTable::GetDeleteStmt() **********/
+void wxTable::GetDeleteStmt(char *pSqlStmt, int typeOfDel, char *pWhereClause)
+{
+ char whereClause[DB_MAX_WHERE_CLAUSE_LEN];
+
+ whereClause[0] = 0;
+
+ // Handle the case of DeleteWhere() and the where clause is blank. It should
+ // delete all records from the database in this case.
+ if (typeOfDel == DB_DEL_WHERE && (pWhereClause == 0 || strlen(pWhereClause) == 0))
+ {
+ sprintf(pSqlStmt, "DELETE FROM %s", tableName);
+ return;
+ }
+
+ sprintf(pSqlStmt, "DELETE FROM %s WHERE ", tableName);
+
+ // Append the WHERE clause to the SQL DELETE statement
+ switch(typeOfDel)
+ {
+ case DB_DEL_KEYFIELDS:
+ // If the datasource supports the ROWID column, build
+ // the where on ROWID for efficiency purposes.
+ // e.g. DELETE FROM PARTS WHERE ROWID = '111.222.333'
+ if (CanUpdByROWID())
+ {
+ SDWORD cb;
+ char rowid[ROWID_LEN];
+
+ // Get the ROWID value. If not successful retreiving the ROWID,
+ // simply fall down through the code and build the WHERE clause
+ // based on the key fields.
+ if (SQLGetData(hstmt, noCols+1, SQL_C_CHAR, rowid, ROWID_LEN, &cb) == SQL_SUCCESS)
+ {
+ strcat(pSqlStmt, "ROWID = '");
+ strcat(pSqlStmt, rowid);
+ strcat(pSqlStmt, "'");
+ break;
+ }
+ }
+ // Unable to delete by ROWID, so build a WHERE
+ // clause based on the keyfields.
+ GetWhereClause(whereClause, DB_WHERE_KEYFIELDS);
+ strcat(pSqlStmt, whereClause);
+ break;
+ case DB_DEL_WHERE:
+ strcat(pSqlStmt, pWhereClause);
+ break;
+ case DB_DEL_MATCHING:
+ GetWhereClause(whereClause, DB_WHERE_MATCHING);
+ strcat(pSqlStmt, whereClause);
+ break;
+ }
+
+} // GetDeleteStmt()
+
+/********** wxTable::GetWhereClause() **********/
+/*
+ * Note: GetWhereClause() currently ignores timestamp columns.
+ * They are not included as part of the where clause.
+ */
+
+void wxTable::GetWhereClause(char *pWhereClause, int typeOfWhere)
+{
+ bool moreThanOneColumn = FALSE;
+ char colValue[255];
+
+ // Loop through the columns building a where clause as you go
+ for (int i = 0; i < noCols; i++)
+ {
+ // Determine if this column should be included in the WHERE clause
+ if ((typeOfWhere == DB_WHERE_KEYFIELDS && colDefs[i].KeyField) ||
+ (typeOfWhere == DB_WHERE_MATCHING && (! IsColNull(i))))
+ {
+ // Skip over timestamp columns
+ if (colDefs[i].SqlCtype == SQL_C_TIMESTAMP)
+ continue;
+ // If there is more than 1 column, join them with the keyword "AND"
+ if (moreThanOneColumn)
+ strcat(pWhereClause, " AND ");
+ else
+ moreThanOneColumn = TRUE;
+ // Concatenate where phrase for the column
+ strcat(pWhereClause, colDefs[i].ColName);
+ strcat(pWhereClause, " = ");
+ switch(colDefs[i].SqlCtype)
+ {
+ case SQL_C_CHAR:
+ sprintf(colValue, "'%s'", (UCHAR FAR *) colDefs[i].PtrDataObj);
+ break;
+ case SQL_C_SSHORT:
+ sprintf(colValue, "%hi", *((SWORD *) colDefs[i].PtrDataObj));
+ break;
+ case SQL_C_USHORT:
+ sprintf(colValue, "%hu", *((UWORD *) colDefs[i].PtrDataObj));
+ break;
+ case SQL_C_SLONG:
+ sprintf(colValue, "%li", *((SDWORD *) colDefs[i].PtrDataObj));
+ break;
+ case SQL_C_ULONG:
+ sprintf(colValue, "%lu", *((UDWORD *) colDefs[i].PtrDataObj));
+ break;
+ case SQL_C_FLOAT:
+ sprintf(colValue, "%.6f", *((SFLOAT *) colDefs[i].PtrDataObj));
+ break;
+ case SQL_C_DOUBLE:
+ sprintf(colValue, "%.6f", *((SDOUBLE *) colDefs[i].PtrDataObj));
+ break;
+ }
+ strcat(pWhereClause, colValue);
+ }
+ }
+
+} // wxTable::GetWhereClause()
+
+/********** wxTable::IsColNull() **********/
+bool wxTable::IsColNull(int colNo)
+{
+ switch(colDefs[colNo].SqlCtype)
+ {
+ case SQL_C_CHAR:
+ return(((UCHAR FAR *) colDefs[colNo].PtrDataObj)[0] == 0);
+ case SQL_C_SSHORT:
+ return(( *((SWORD *) colDefs[colNo].PtrDataObj)) == 0);
+ case SQL_C_USHORT:
+ return(( *((UWORD*) colDefs[colNo].PtrDataObj)) == 0);
+ case SQL_C_SLONG:
+ return(( *((SDWORD *) colDefs[colNo].PtrDataObj)) == 0);
+ case SQL_C_ULONG:
+ return(( *((UDWORD *) colDefs[colNo].PtrDataObj)) == 0);
+ case SQL_C_FLOAT:
+ return(( *((SFLOAT *) colDefs[colNo].PtrDataObj)) == 0);
+ case SQL_C_DOUBLE:
+ return((*((SDOUBLE *) colDefs[colNo].PtrDataObj)) == 0);
+ case SQL_C_TIMESTAMP:
+ TIMESTAMP_STRUCT *pDt;
+ pDt = (TIMESTAMP_STRUCT *) colDefs[colNo].PtrDataObj;
+ if (pDt->year == 0 && pDt->month == 0 && pDt->day == 0)
+ return(TRUE);
+ else
+ return(FALSE);
+ default:
+ return(TRUE);
+ }
+
+} // wxTable::IsColNull()
+
+/********** wxTable::CanSelectForUpdate() **********/
+
+bool wxTable::CanSelectForUpdate(void)
+{
+ if (pDb->dbInf.posStmts & SQL_PS_SELECT_FOR_UPDATE)
+ return(TRUE);
+ else
+ return(FALSE);
+
+} // wxTable::CanSelectForUpdate()
+
+/********** wxTable::CanUpdByROWID() **********/
+bool wxTable::CanUpdByROWID(void)
+{
+
+//@@@@@@glt - returning FALSE for testing purposes, as the ROWID is not getting updated correctly
+ return FALSE;
+
+ if ((! strcmp(pDb->dbInf.dbmsName, "Oracle")) || (! strcmp(pDb->dbInf.dbmsName, "ORACLE")))
+ return(TRUE);
+ else
+ return(FALSE);
+
+} // wxTable::CanUpdByROWID()
+
+/********** wxTable::IsCursorClosedOnCommit() **********/
+bool wxTable::IsCursorClosedOnCommit(void)
+{
+ if (pDb->dbInf.cursorCommitBehavior == SQL_CB_PRESERVE)
+ return(FALSE);
+ else
+ return(TRUE);
+
+} // wxTable::IsCursorClosedOnCommit()
+
+/********** wxTable::ClearMemberVars() **********/
+void wxTable::ClearMemberVars(void)
+{
+ // Loop through the columns setting each member variable to zero
+ for (int i = 0; i < noCols; i++)
+ {
+ switch(colDefs[i].SqlCtype)
+ {
+ case SQL_C_CHAR:
+ ((UCHAR FAR *) colDefs[i].PtrDataObj)[0] = 0;
+ break;
+ case SQL_C_SSHORT:
+ *((SWORD *) colDefs[i].PtrDataObj) = 0;
+ break;
+ case SQL_C_USHORT:
+ *((UWORD*) colDefs[i].PtrDataObj) = 0;
+ break;
+ case SQL_C_SLONG:
+ *((SDWORD *) colDefs[i].PtrDataObj) = 0;
+ break;
+ case SQL_C_ULONG:
+ *((UDWORD *) colDefs[i].PtrDataObj) = 0;
+ break;
+ case SQL_C_FLOAT:
+ *((SFLOAT *) colDefs[i].PtrDataObj) = 0.0f;
+ break;
+ case SQL_C_DOUBLE:
+ *((SDOUBLE *) colDefs[i].PtrDataObj) = 0.0f;
+ break;
+ case SQL_C_TIMESTAMP:
+ TIMESTAMP_STRUCT *pDt;
+ pDt = (TIMESTAMP_STRUCT *) colDefs[i].PtrDataObj;
+ pDt->year = 0;
+ pDt->month = 0;
+ pDt->day = 0;
+ pDt->hour = 0;
+ pDt->minute = 0;
+ pDt->second = 0;
+ pDt->fraction = 0;
+ break;
+ }
+ }
+
+} // wxTable::ClearMemberVars()
+
+/********** wxTable::SetQueryTimeout() **********/
+bool wxTable::SetQueryTimeout(UDWORD nSeconds)
+{
+ if (SQLSetStmtOption(c0, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
+ return(pDb->DispAllErrors(henv, hdbc, c0));
+ if (SQLSetStmtOption(c1, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
+ return(pDb->DispAllErrors(henv, hdbc, c1));
+ if (SQLSetStmtOption(c2, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
+ return(pDb->DispAllErrors(henv, hdbc, c2));
+// if (SQLSetStmtOption(c3, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
+// return(pDb->DispAllErrors(henv, hdbc, c3));
+// if (SQLSetStmtOption(c4, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
+// return(pDb->DispAllErrors(henv, hdbc, c4));
+// if (SQLSetStmtOption(c5, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
+// return(pDb->DispAllErrors(henv, hdbc, c5));
+ if (SQLSetStmtOption(hstmtInsert, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
+ return(pDb->DispAllErrors(henv, hdbc, hstmtInsert));
+ if (SQLSetStmtOption(hstmtUpdate, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
+ return(pDb->DispAllErrors(henv, hdbc, hstmtUpdate));
+ if (SQLSetStmtOption(hstmtDelete, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
+ return(pDb->DispAllErrors(henv, hdbc, hstmtDelete));
+ if (SQLSetStmtOption(hstmtCount, SQL_QUERY_TIMEOUT, nSeconds) != SQL_SUCCESS)
+ return(pDb->DispAllErrors(henv, hdbc, hstmtCount));
+
+ // Completed Successfully
+ return(TRUE);
+
+} // wxTable::SetQueryTimeout()
+
+/********** wxTable::SetColDefs() **********/
+void wxTable::SetColDefs (int index, char *fieldName, int dataType, void *pData,
+ int cType, int size, bool keyField, bool upd,
+ bool insAllow, bool derivedCol)
+{
+ if (strlen(fieldName) > DB_MAX_COLUMN_NAME_LEN) // glt 4/21/97
+ {
+ strncpy (colDefs[index].ColName, fieldName, DB_MAX_COLUMN_NAME_LEN);
+ colDefs[index].ColName[DB_MAX_COLUMN_NAME_LEN] = 0; // glt 10/23/97
+ }
+ else
+ strcpy(colDefs[index].ColName, fieldName);
+
+ colDefs[index].DbDataType = dataType;
+ colDefs[index].PtrDataObj = pData;
+ colDefs[index].SqlCtype = cType;
+ colDefs[index].SzDataObj = size;
+ colDefs[index].KeyField = keyField;
+ colDefs[index].DerivedCol = derivedCol;
+ // Derived columns by definition would NOT be "Insertable" or "Updateable"
+ if (derivedCol)
+ {
+ colDefs[index].Updateable = FALSE;
+ colDefs[index].InsertAllowed = FALSE;
+ }
+ else
+ {
+ colDefs[index].Updateable = upd;
+ colDefs[index].InsertAllowed = insAllow;
+ }
+
+} // wxTable::SetColDefs()
+
+/********** wxTable::SetCursor() **********/
+bool wxTable::SetCursor(int cursorNo)
+{
+ switch(cursorNo)
+ {
+ case DB_CURSOR0:
+ hstmt = c0;
+ // currCursorNo doesn't change since Cursor0 is a temp cursor
+ break;
+ case DB_CURSOR1:
+ hstmt = c1;
+ currCursorNo = DB_CURSOR1;
+ break;
+ case DB_CURSOR2:
+ hstmt = c2;
+ currCursorNo = DB_CURSOR2;
+ break;
+// case DB_CURSOR3:
+// hstmt = c3;
+// currCursorNo = DB_CURSOR3;
+// break;
+// case DB_CURSOR4:
+// hstmt = c4;
+// currCursorNo = DB_CURSOR4;
+// break;
+// case DB_CURSOR5:
+// hstmt = c5;
+// currCursorNo = DB_CURSOR5;
+// break;
+ default:
+ return(FALSE);
+ }
+
+ // Refresh the current record
+#ifndef FWD_ONLY_CURSORS
+ UDWORD cRowsFetched;
+ UWORD rowStatus;
+ SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 0, &cRowsFetched, &rowStatus);
+ SQLExtendedFetch(hstmt, SQL_FETCH_PRIOR, 0, &cRowsFetched, &rowStatus);
+#endif
+
+ // Completed successfully
+ return(TRUE);
+
+} // wxTable::SetCursor()
+
+/********** wxTable::Count() **********/
+ULONG wxTable::Count(void)
+{
+ ULONG l;
+ char sqlStmt[DB_MAX_STATEMENT_LEN];
+ SDWORD cb;
+
+ // Build a "SELECT COUNT(*) FROM queryTableName [WHERE whereClause]" SQL Statement
+ strcpy(sqlStmt, "SELECT COUNT(*) FROM ");
+ strcat(sqlStmt, queryTableName);
+
+ // Add the where clause if one is provided
+ if (where && strlen(where))
+ {
+ strcat(sqlStmt, " WHERE ");
+ strcat(sqlStmt, where);
+ }
+
+ // Execute the SQL statement
+ if (SQLExecDirect(hstmtCount, (UCHAR FAR *) sqlStmt, SQL_NTS) != SQL_SUCCESS)
+ {
+ pDb->DispAllErrors(henv, hdbc, hstmtCount);
+ return(0);
+ }
+
+ // Fetch the record
+ if (SQLFetch(hstmtCount) != SQL_SUCCESS)
+ {
+ pDb->DispAllErrors(henv, hdbc, hstmtCount);
+ return(0);
+ }
+
+ // Obtain the result
+ if (SQLGetData(hstmtCount, 1, SQL_C_ULONG, &l, sizeof(l), &cb) != SQL_SUCCESS)
+ {
+ pDb->DispAllErrors(henv, hdbc, hstmtCount);
+ return(0);
+ }
+
+ // Free the cursor
+ if (SQLFreeStmt(hstmtCount, SQL_CLOSE) != SQL_SUCCESS)
+ pDb->DispAllErrors(henv, hdbc, hstmtCount);
+
+ // Return the record count
+ return(l);
+
+} // wxTable::Count()
+
+/********** wxTable::Refresh() **********/
+bool wxTable::Refresh(void)
+{
+ bool result = TRUE;
+
+ // Switch to cursor 0
+ int cursorNo = GetCursor();
+ if (!SetCursor())
+ return(FALSE);
+
+ // Save the where and order by clauses
+ char *saveWhere = where;
+ char *saveOrderBy = orderBy;
+
+ // Build a where clause to refetch the record with. Try and use the
+ // ROWID if it's available, ow use the key fields.
+ char whereClause[DB_MAX_WHERE_CLAUSE_LEN+1];
+ strcpy(whereClause, "");
+ if (CanUpdByROWID())
+ {
+ SDWORD cb;
+ char rowid[ROWID_LEN+1];
+
+ // Get the ROWID value. If not successful retreiving the ROWID,
+ // simply fall down through the code and build the WHERE clause
+ // based on the key fields.
+ if (SQLGetData(hstmt, noCols+1, SQL_C_CHAR, rowid, ROWID_LEN, &cb) == SQL_SUCCESS)
+ {
+ strcat(whereClause, "ROWID = '");
+ strcat(whereClause, rowid);
+ strcat(whereClause, "'");
+ }
+ }
+
+ // If unable to use the ROWID, build a where clause from the keyfields
+ if (strlen(whereClause) == 0)
+ GetWhereClause(whereClause, DB_WHERE_KEYFIELDS);
+
+ // Requery the record
+ where = whereClause;
+ orderBy = 0;
+ if (!Query())
+ result = FALSE;
+
+ if (result && !GetNext())
+ result = FALSE;
+
+ // Switch back to original cursor
+ if (!SetCursor(cursorNo))
+ result = FALSE;
+
+ // Restore the original where and order by clauses
+ where = saveWhere;
+ orderBy = saveOrderBy;
+
+ return(result);
+
+} // wxTable::Refresh()
+
+#endif
+ // USE_ODBC
COMMONOBJS = \
$(COMMDIR)\cmndata.obj \
$(COMMDIR)\config.obj \
+ $(COMMDIR)\db.obj \
+ $(COMMDIR)\dbtable.obj \
$(COMMDIR)\docview.obj \
$(COMMDIR)\dynarray.obj \
$(COMMDIR)\event.obj \
$(CPPFLAGS) /c /Tp $*.$(SRCSUFF) /Fo$@
<<
+$(COMMDIR)/db.obj: $*.$(SRCSUFF)
+ echo $(CPPFLAGS)
+ cl @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF) /Fo$@
+<<
+
+$(COMMDIR)/dbtable.obj: $*.$(SRCSUFF)
+ echo $(CPPFLAGS)
+ cl @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF) /Fo$@
+<<
+
$(COMMDIR)/docview.obj: $*.$(SRCSUFF)
cl @<<
$(CPPFLAGS) /c /Tp $*.$(SRCSUFF) /Fo$@