X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/f6bcfd974ef26faf6f91a62cac09827e09463fd1..f0ce3409421a7c5c800a2255295274187220e8de:/src/common/dbtable.cpp diff --git a/src/common/dbtable.cpp b/src/common/dbtable.cpp index 9daf02fcb7..a1f0a15eaf 100644 --- a/src/common/dbtable.cpp +++ b/src/common/dbtable.cpp @@ -3,11 +3,8 @@ // Purpose: Implementation of the wxDbTable class. // Author: Doug Card // Modified by: George Tasker -// Mods: April 1999 -// -Dynamic cursor support - Only one predefined cursor, as many others as -// you need may be created on demand -// -Reduced number of active cursors significantly -// -Query-Only wxDbTable objects +// Bart Jourquin +// Mark Johnson // Created: 9.96 // RCS-ID: $Id$ // Copyright: (c) 1996 Remstar International, Inc. @@ -35,9 +32,9 @@ #include "wx/version.h" #if wxMAJOR_VERSION == 2 -# ifdef __GNUG__ -# pragma implementation "dbtable.h" -# endif + #ifdef __GNUG__ + #pragma implementation "dbtable.h" + #endif #endif #ifdef DBDEBUG_CONSOLE @@ -78,7 +75,7 @@ #include #include #include -#include +//#include #if wxMAJOR_VERSION == 1 #include "table.h" @@ -105,10 +102,62 @@ ULONG lastTableID = 0; #endif -/********** wxDbTable::wxDbTable() **********/ -wxDbTable::wxDbTable(wxDb *pwxDb, const char *tblName, const int nCols, - const char *qryTblName, bool qryOnly, const char *tblPath) +/********** wxDbColDef::wxDbColDef() Constructor **********/ +wxDbColDef::wxDbColDef() +{ + Initialize(); +} // Constructor + + +bool wxDbColDef::Initialize() +{ + ColName[0] = 0; + DbDataType = DB_DATA_TYPE_INTEGER; + SqlCtype = SQL_C_LONG; + PtrDataObj = NULL; + SzDataObj = 0; + KeyField = FALSE; + Updateable = FALSE; + InsertAllowed = FALSE; + DerivedCol = FALSE; + CbValue = 0; + Null = FALSE; + + return TRUE; +} // wxDbColDef::Initialize() + + +/********** wxDbTable::wxDbTable() Constructor **********/ +wxDbTable::wxDbTable(wxDb *pwxDb, const wxString &tblName, const int nCols, + const wxString &qryTblName, bool qryOnly, const wxString &tblPath) +{ + if (!initialize(pwxDb, tblName, nCols, qryTblName, qryOnly, tblPath)) + cleanup(); +} // wxDbTable::wxDbTable() + + +/***** DEPRECATED: use wxDbTable::wxDbTable() format above *****/ +wxDbTable::wxDbTable(wxDb *pwxDb, const wxString &tblName, const int nCols, + const wxChar *qryTblName, bool qryOnly, const wxString &tblPath) +{ + wxString tempQryTblName; + tempQryTblName = qryTblName; + if (!initialize(pwxDb, tblName, nCols, tempQryTblName, qryOnly, tblPath)) + cleanup(); +} // wxDbTable::wxDbTable() + + +/********** wxDbTable::~wxDbTable() **********/ +wxDbTable::~wxDbTable() { + this->cleanup(); +} // wxDbTable::~wxDbTable() + + +bool wxDbTable::initialize(wxDb *pwxDb, const wxString &tblName, const int nCols, + const wxString &qryTblName, bool qryOnly, const wxString &tblPath) +{ + // Initializing member variables pDb = pwxDb; // Pointer to the wxDb object henv = 0; hdbc = 0; @@ -122,33 +171,39 @@ wxDbTable::wxDbTable(wxDb *pwxDb, const char *tblName, const int nCols, colDefs = 0; tableID = 0; noCols = nCols; // No. of cols in the table - where = ""; // Where clause - orderBy = ""; // Order By clause - from = ""; // From clause + where.Empty(); // Where clause + orderBy.Empty(); // Order By clause + from.Empty(); // From clause selectForUpdate = FALSE; // SELECT ... FOR UPDATE; Indicates whether to include the FOR UPDATE phrase queryOnly = qryOnly; insertable = TRUE; + tablePath.Empty(); + tableName.Empty(); + queryTableName.Empty(); - assert (tblName); - - wxStrcpy(tableName, tblName); // Table Name - if (tblPath) - wxStrcpy(tablePath, tblPath); // Table Path - used for dBase files - - if (qryTblName) // Name of the table/view to query - wxStrcpy(queryTableName, qryTblName); - else - wxStrcpy(queryTableName, tblName); + wxASSERT(tblName.Length()); + wxASSERT(pDb); if (!pDb) - return; + return FALSE; + tableName = tblName; // Table Name + if (tblPath.Length()) + tablePath = tblPath; // Table Path - used for dBase files + else + tablePath.Empty(); + + if (qryTblName.Length()) // Name of the table/view to query + queryTableName = qryTblName; + else + queryTableName = tblName; + pDb->incrementTableCount(); - + wxString s; tableID = ++lastTableID; - s.sprintf("wxDbTable constructor (%-20s) tableID:[%6lu] pDb:[%p]", tblName,tableID,pDb); - + s.Printf(wxT("wxDbTable constructor (%-20s) tableID:[%6lu] pDb:[%p]"), tblName.c_str(), tableID, pDb); + #ifdef __WXDEBUG__ wxTablesInUse *tableInUse; tableInUse = new wxTablesInUse(); @@ -157,17 +212,17 @@ wxDbTable::wxDbTable(wxDb *pwxDb, const char *tblName, const int nCols, tableInUse->pDb = pDb; TablesInUse.Append(tableInUse); #endif - - pDb->WriteSqlLog(s.c_str()); - + + pDb->WriteSqlLog(s); + // Grab the HENV and HDBC from the wxDb object henv = pDb->GetHENV(); hdbc = pDb->GetHDBC(); - + // Allocate space for column definitions if (noCols) - colDefs = new wxDbColDef[noCols]; // Points to the first column defintion - + colDefs = new wxDbColDef[noCols]; // Points to the first column definition + // Allocate statement handles for the table if (!queryOnly) { @@ -184,62 +239,64 @@ wxDbTable::wxDbTable(wxDb *pwxDb, const char *tblName, const int nCols, // Allocate a separate statement handle for internal use if (SQLAllocStmt(hdbc, &hstmtInternal) != SQL_SUCCESS) pDb->DispAllErrors(henv, hdbc); - + // Set the cursor type for the statement handles cursorType = SQL_CURSOR_STATIC; - + if (SQLSetStmtOption(hstmtInternal, SQL_CURSOR_TYPE, cursorType) != SQL_SUCCESS) - { + { // Check to see if cursor type is supported pDb->GetNextError(henv, hdbc, hstmtInternal); - if (! wxStrcmp(pDb->sqlState, "01S02")) // Option Value Changed - { - + if (! wxStrcmp(pDb->sqlState, wxT("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(hstmtInternal, SQL_CURSOR_TYPE, &cursorType) != SQL_SUCCESS) - pDb->DispAllErrors(henv, hdbc, hstmtInternal); + pDb->DispAllErrors(henv, hdbc, hstmtInternal); #ifdef DBDEBUG_CONSOLE - cout << "Static cursor changed to: "; + cout << wxT("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; - } + { + case SQL_CURSOR_FORWARD_ONLY: + cout << wxT("Forward Only"); + break; + case SQL_CURSOR_STATIC: + cout << wxT("Static"); + break; + case SQL_CURSOR_KEYSET_DRIVEN: + cout << wxT("Keyset Driven"); + break; + case SQL_CURSOR_DYNAMIC: + cout << wxT("Dynamic"); + break; + } cout << endl << endl; #endif - - // BJO20000425 - if (pDb->FwdOnlyCursors() && cursorType != SQL_CURSOR_FORWARD_ONLY) - { - // Force the use of a forward only cursor... - cursorType = SQL_CURSOR_FORWARD_ONLY; - if (SQLSetStmtOption(hstmtInternal, SQL_CURSOR_TYPE, cursorType) != SQL_SUCCESS) - { - // Should never happen - pDb->GetNextError(henv, hdbc, hstmtInternal); - return; - } - } - } - else - { + // BJO20000425 + if (pDb->FwdOnlyCursors() && cursorType != SQL_CURSOR_FORWARD_ONLY) + { + // Force the use of a forward only cursor... + cursorType = SQL_CURSOR_FORWARD_ONLY; + if (SQLSetStmtOption(hstmtInternal, SQL_CURSOR_TYPE, cursorType) != SQL_SUCCESS) + { + // Should never happen + pDb->GetNextError(henv, hdbc, hstmtInternal); + return FALSE; + } + } + } + else + { pDb->DispNextError(); pDb->DispAllErrors(henv, hdbc, hstmtInternal); - } + } } #ifdef DBDEBUG_CONSOLE else - cout << "Cursor Type set to STATIC" << endl << endl; + cout << wxT("Cursor Type set to STATIC") << endl << endl; #endif - + if (!queryOnly) { // Set the cursor type for the INSERT statement handle @@ -252,23 +309,24 @@ wxDbTable::wxDbTable(wxDb *pwxDb, const char *tblName, const int nCols, if (SQLSetStmtOption(hstmtUpdate, SQL_CURSOR_TYPE, SQL_CURSOR_FORWARD_ONLY) != SQL_SUCCESS) pDb->DispAllErrors(henv, hdbc, hstmtUpdate); } - + // Make the default cursor the active cursor hstmtDefault = GetNewCursor(FALSE,FALSE); - assert(hstmtDefault); + wxASSERT(hstmtDefault); hstmt = *hstmtDefault; -} // wxDbTable::wxDbTable() + return TRUE; +} // wxDbTable::initialize() -/********** wxDbTable::~wxDbTable() **********/ -wxDbTable::~wxDbTable() + +void wxDbTable::cleanup() { wxString s; if (pDb) { - s.sprintf("wxDbTable destructor (%-20s) tableID:[%6lu] pDb:[%p]", tableName,tableID,pDb); - pDb->WriteSqlLog(s.c_str()); + s.Printf(wxT("wxDbTable destructor (%-20s) tableID:[%6lu] pDb:[%p]"), tableName.c_str(), tableID, pDb); + pDb->WriteSqlLog(s); } #ifdef __WXDEBUG__ @@ -285,7 +343,7 @@ wxDbTable::~wxDbTable() { found = TRUE; if (!TablesInUse.DeleteNode(pNode)) - wxLogDebug (s.c_str(),wxT("Unable to delete node!")); + wxLogDebug (s,wxT("Unable to delete node!")); } else pNode = pNode->Next(); @@ -293,8 +351,8 @@ wxDbTable::~wxDbTable() if (!found) { wxString msg; - msg.sprintf(wxT("Unable to find the tableID in the linked\nlist of tables in use.\n\n%s"),s.c_str()); - wxLogDebug (msg.c_str(),wxT("NOTICE...")); + msg.Printf(wxT("Unable to find the tableID in the linked\nlist of tables in use.\n\n%s"),s); + wxLogDebug (msg,wxT("NOTICE...")); } } #endif @@ -311,20 +369,41 @@ wxDbTable::~wxDbTable() if (!queryOnly) { if (hstmtInsert) + { +/* +ODBC 3.0 says to use this form + if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS) +*/ if (SQLFreeStmt(hstmtInsert, SQL_DROP) != SQL_SUCCESS) pDb->DispAllErrors(henv, hdbc); + } if (hstmtDelete) + { +/* +ODBC 3.0 says to use this form + if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS) +*/ if (SQLFreeStmt(hstmtDelete, SQL_DROP) != SQL_SUCCESS) + pDb->DispAllErrors(henv, hdbc); + } if (hstmtUpdate) + { +/* +ODBC 3.0 says to use this form + if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS) +*/ if (SQLFreeStmt(hstmtUpdate, SQL_DROP) != SQL_SUCCESS) pDb->DispAllErrors(henv, hdbc); - + } } + if (hstmtInternal) + { if (SQLFreeStmt(hstmtInternal, SQL_DROP) != SQL_SUCCESS) pDb->DispAllErrors(henv, hdbc); + } // Delete dynamically allocated cursors if (hstmtDefault) @@ -332,160 +411,148 @@ wxDbTable::~wxDbTable() if (hstmtCount) DeleteCursor(hstmtCount); - - -} // wxDbTable::~wxDbTable() - +} // wxDbTable::cleanup() /***************************** PRIVATE FUNCTIONS *****************************/ - -/********** wxDbTable::bindInsertParams() **********/ -bool wxDbTable::bindInsertParams(void) +/********** wxDbTable::bindUpdateParams() **********/ +bool wxDbTable::bindParams(bool forUpdate) { - assert(!queryOnly); + wxASSERT(!queryOnly); if (queryOnly) return(FALSE); - + SWORD fSqlType = 0; UDWORD precision = 0; SWORD scale = 0; - - // Bind each column (that can be inserted) of the table to a parameter marker + + // Bind each column of the table that should be bound + // to a parameter marker int i,colNo; for (i = 0, colNo = 1; i < noCols; i++) { - if (! colDefs[i].InsertAllowed) - continue; - switch(colDefs[i].DbDataType) + if (forUpdate) { - - case DB_DATA_TYPE_VARCHAR: - fSqlType = pDb->GetTypeInfVarchar().FsqlType; - precision = colDefs[i].SzDataObj; - scale = 0; - colDefs[i].CbValue = SQL_NTS; - break; - case DB_DATA_TYPE_INTEGER: - fSqlType = pDb->GetTypeInfInteger().FsqlType; - precision = pDb->GetTypeInfInteger().Precision; - scale = 0; - colDefs[i].CbValue = 0; - break; - case DB_DATA_TYPE_FLOAT: - fSqlType = pDb->GetTypeInfFloat().FsqlType; - precision = pDb->GetTypeInfFloat().Precision; - scale = pDb->GetTypeInfFloat().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->GetTypeInfDate().FsqlType; - precision = pDb->GetTypeInfDate().Precision; - scale = 0; - colDefs[i].CbValue = 0; - break; + if (! colDefs[i].Updateable) + continue; } - // Null values - if (colDefs[i].Null) + else { - colDefs[i].CbValue = SQL_NULL_DATA; - colDefs[i].Null = FALSE; + if (! colDefs[i].InsertAllowed) + continue; } - if (SQLBindParameter(hstmtInsert, colNo++, SQL_PARAM_INPUT, colDefs[i].SqlCtype, - fSqlType, precision, scale, (UCHAR*) colDefs[i].PtrDataObj, - precision+1,&colDefs[i].CbValue) != SQL_SUCCESS) - return(pDb->DispAllErrors(henv, hdbc, hstmtInsert)); + switch(colDefs[i].DbDataType) + { + case DB_DATA_TYPE_VARCHAR: + fSqlType = pDb->GetTypeInfVarchar().FsqlType; + precision = colDefs[i].SzDataObj; + scale = 0; + if (colDefs[i].Null) + colDefs[i].CbValue = SQL_NULL_DATA; + else + colDefs[i].CbValue = SQL_NTS; + break; + case DB_DATA_TYPE_INTEGER: + fSqlType = pDb->GetTypeInfInteger().FsqlType; + precision = pDb->GetTypeInfInteger().Precision; + scale = 0; + if (colDefs[i].Null) + colDefs[i].CbValue = SQL_NULL_DATA; + else + colDefs[i].CbValue = 0; + break; + case DB_DATA_TYPE_FLOAT: + fSqlType = pDb->GetTypeInfFloat().FsqlType; + precision = pDb->GetTypeInfFloat().Precision; + scale = pDb->GetTypeInfFloat().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; + if (colDefs[i].Null) + colDefs[i].CbValue = SQL_NULL_DATA; + else + colDefs[i].CbValue = 0; + break; + case DB_DATA_TYPE_DATE: + fSqlType = pDb->GetTypeInfDate().FsqlType; + precision = pDb->GetTypeInfDate().Precision; + scale = 0; + if (colDefs[i].Null) + colDefs[i].CbValue = SQL_NULL_DATA; + else + colDefs[i].CbValue = 0; + break; + case DB_DATA_TYPE_BLOB: + fSqlType = pDb->GetTypeInfBlob().FsqlType; + precision = 50000; + scale = 0; + if (colDefs[i].Null) + colDefs[i].CbValue = SQL_NULL_DATA; + else + colDefs[i].CbValue = SQL_LEN_DATA_AT_EXEC(colDefs[i].SzDataObj); + break; + } + if (forUpdate) + { + if (SQLBindParameter(hstmtUpdate, colNo++, SQL_PARAM_INPUT, colDefs[i].SqlCtype, + fSqlType, precision, scale, (UCHAR*) colDefs[i].PtrDataObj, + precision+1, &colDefs[i].CbValue) != SQL_SUCCESS) + { + return(pDb->DispAllErrors(henv, hdbc, hstmtUpdate)); + } + } + else + { + if (SQLBindParameter(hstmtInsert, colNo++, SQL_PARAM_INPUT, colDefs[i].SqlCtype, + fSqlType, precision, scale, (UCHAR*) colDefs[i].PtrDataObj, + precision+1,&colDefs[i].CbValue) != SQL_SUCCESS) + { + return(pDb->DispAllErrors(henv, hdbc, hstmtInsert)); + } + } } - + // Completed successfully return(TRUE); +} // wxDbTable::bindParams() + + +/********** wxDbTable::bindInsertParams() **********/ +bool wxDbTable::bindInsertParams(void) +{ + return bindParams(FALSE); } // wxDbTable::bindInsertParams() /********** wxDbTable::bindUpdateParams() **********/ bool wxDbTable::bindUpdateParams(void) { - assert(!queryOnly); - if (queryOnly) - return(FALSE); - - SWORD fSqlType = 0; - UDWORD precision = 0; - SWORD scale = 0; - - // Bind each UPDATEABLE column of the table to a parameter marker - int i,colNo; - for (i = 0, colNo = 1; i < noCols; i++) - { - if (! colDefs[i].Updateable) - continue; - switch(colDefs[i].DbDataType) - { - case DB_DATA_TYPE_VARCHAR: - fSqlType = pDb->GetTypeInfVarchar().FsqlType; - precision = colDefs[i].SzDataObj; - scale = 0; - colDefs[i].CbValue = SQL_NTS; - break; - case DB_DATA_TYPE_INTEGER: - fSqlType = pDb->GetTypeInfInteger().FsqlType; - precision = pDb->GetTypeInfInteger().Precision; - scale = 0; - colDefs[i].CbValue = 0; - break; - case DB_DATA_TYPE_FLOAT: - fSqlType = pDb->GetTypeInfFloat().FsqlType; - precision = pDb->GetTypeInfFloat().Precision; - scale = pDb->GetTypeInfFloat().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->GetTypeInfDate().FsqlType; - precision = pDb->GetTypeInfDate().Precision; - scale = 0; - colDefs[i].CbValue = 0; - break; - } - - if (SQLBindParameter(hstmtUpdate, colNo++, SQL_PARAM_INPUT, colDefs[i].SqlCtype, - fSqlType, precision, scale, (UCHAR*) colDefs[i].PtrDataObj, - precision+1, &colDefs[i].CbValue) != SQL_SUCCESS) - return(pDb->DispAllErrors(henv, hdbc, hstmtUpdate)); - } - - // Completed successfully - return(TRUE); - + return bindParams(TRUE); } // wxDbTable::bindUpdateParams() /********** wxDbTable::bindCols() **********/ bool wxDbTable::bindCols(HSTMT cursor) { - static SDWORD cb; +//RG-NULL static SDWORD cb; // Bind each column of the table to a memory address for fetching data int i; for (i = 0; i < noCols; i++) - { - if (SQLBindCol(cursor, i+1, colDefs[i].SqlCtype, (UCHAR*) colDefs[i].PtrDataObj, - colDefs[i].SzDataObj, &cb) != SQL_SUCCESS) - return (pDb->DispAllErrors(henv, hdbc, cursor)); - } + { + if (SQLBindCol(cursor, i+1, colDefs[i].SqlCtype, (UCHAR*) colDefs[i].PtrDataObj, +//RG-NULL colDefs[i].SzDataObj, &cb) != SQL_SUCCESS) + colDefs[i].SzDataObj, &colDefs[i].CbValue ) != SQL_SUCCESS) + { + return (pDb->DispAllErrors(henv, hdbc, cursor)); + } + } // Completed successfully return(TRUE); @@ -506,10 +573,20 @@ bool wxDbTable::getRec(UWORD fetchType) retcode = SQLExtendedFetch(hstmt, fetchType, 0, &cRowsFetched, &rowStatus); if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) + { if (retcode == SQL_NO_DATA_FOUND) return(FALSE); else return(pDb->DispAllErrors(henv, hdbc, hstmt)); + } + else + { + // Set the Null member variable to indicate the Null state + // of each column just read in. + int i; + for (i = 0; i < noCols; i++) + colDefs[i].Null = (colDefs[i].CbValue == SQL_NULL_DATA); + } } else { @@ -522,6 +599,14 @@ bool wxDbTable::getRec(UWORD fetchType) else return(pDb->DispAllErrors(henv, hdbc, hstmt)); } + else + { + // Set the Null member variable to indicate the Null state + // of each column just read in. + int i; + for (i = 0; i < noCols; i++) + colDefs[i].Null = (colDefs[i].CbValue == SQL_NULL_DATA); + } } // Completed successfully @@ -531,10 +616,10 @@ bool wxDbTable::getRec(UWORD fetchType) /********** wxDbTable::execDelete() **********/ -bool wxDbTable::execDelete(const char *pSqlStmt) +bool wxDbTable::execDelete(const wxString &pSqlStmt) { // Execute the DELETE statement - if (SQLExecDirect(hstmtDelete, (UCHAR FAR *) pSqlStmt, SQL_NTS) != SQL_SUCCESS) + if (SQLExecDirect(hstmtDelete, (UCHAR FAR *) pSqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS) return(pDb->DispAllErrors(henv, hdbc, hstmtDelete)); // Record deleted successfully @@ -544,10 +629,10 @@ bool wxDbTable::execDelete(const char *pSqlStmt) /********** wxDbTable::execUpdate() **********/ -bool wxDbTable::execUpdate(const char *pSqlStmt) +bool wxDbTable::execUpdate(const wxString &pSqlStmt) { // Execute the UPDATE statement - if (SQLExecDirect(hstmtUpdate, (UCHAR FAR *) pSqlStmt, SQL_NTS) != SQL_SUCCESS) + if (SQLExecDirect(hstmtUpdate, (UCHAR FAR *) pSqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS) return(pDb->DispAllErrors(henv, hdbc, hstmtUpdate)); // Record deleted successfully @@ -557,25 +642,10 @@ bool wxDbTable::execUpdate(const char *pSqlStmt) /********** wxDbTable::query() **********/ -bool wxDbTable::query(int queryType, bool forUpdate, bool distinct, const char *pSqlStmt) +bool wxDbTable::query(int queryType, bool forUpdate, bool distinct, const wxString &pSqlStmt) { + wxString sqlStmt; - - -/* SQLFreeStmt(hstmt, SQL_CLOSE); - if (SQLExecDirect(hstmt, (UCHAR FAR *) pSqlStmt, SQL_NTS) == SQL_SUCCESS) - return(TRUE); - else - { - pDb->DispAllErrors(henv, hdbc, hstmt); - return(FALSE); - } -*/ - - - 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(); @@ -584,29 +654,35 @@ bool wxDbTable::query(int queryType, bool forUpdate, bool distinct, const char * // Set the SQL SELECT string if (queryType != DB_SELECT_STATEMENT) // A select statement was not passed in, - { // so generate a select statement. + { // so generate a select statement. BuildSelectStmt(sqlStmt, queryType, distinct); pDb->WriteSqlLog(sqlStmt); - } else wxStrcpy(sqlStmt, pSqlStmt); + } +/* + This is the block of code that got added during the 2.2.1 merge with + the 2.2 main branch that somehow got added here when it should not have. - gt + + else + wxStrcpy(sqlStmt, pSqlStmt); SQLFreeStmt(hstmt, SQL_CLOSE); if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt, SQL_NTS) == SQL_SUCCESS) - return(TRUE); + return(TRUE); else - { + { pDb->DispAllErrors(henv, hdbc, hstmt); return(FALSE); - } - + } +*/ // Make sure the cursor is closed first - if (! CloseCursor(hstmt)) - return(FALSE); + if (!CloseCursor(hstmt)) + return(FALSE); // Execute the SQL SELECT statement int retcode; - retcode = SQLExecDirect(hstmt, (UCHAR FAR *) (queryType == DB_SELECT_STATEMENT ? pSqlStmt : sqlStmt), SQL_NTS); + retcode = SQLExecDirect(hstmt, (UCHAR FAR *) (queryType == DB_SELECT_STATEMENT ? pSqlStmt.c_str() : sqlStmt.c_str()), SQL_NTS); if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) - return(pDb->DispAllErrors(henv, hdbc, hstmt)); + return(pDb->DispAllErrors(henv, hdbc, hstmt)); // Completed successfully return(TRUE); @@ -618,26 +694,50 @@ bool wxDbTable::query(int queryType, bool forUpdate, bool distinct, const char * /********** wxDbTable::Open() **********/ -bool wxDbTable::Open(void) +bool wxDbTable::Open(bool checkPrivileges) { if (!pDb) return FALSE; + int i; wxString sqlStmt; + wxString s; + s.Empty(); // Verify that the table exists in the database - if (!pDb->TableExists(tableName,pDb->GetUsername(),tablePath)) + if (!pDb->TableExists(tableName,/*pDb->GetUsername()*/NULL,tablePath)) { - wxString s; - if (wxStrcmp(tablePath,"")) - s.sprintf(wxT("Error opening '%s/%s'.\n"),tablePath,tableName); + s = wxT("Table/view does not exist in the database"); + if ( *(pDb->dbInf.accessibleTables) == wxT('Y')) + s += wxT(", or you have no permissions.\n"); else - s.sprintf(wxT("Error opening '%s'.\n"), tableName); - if (!pDb->TableExists(tableName,NULL,tablePath)) - s += wxT("Table/view does not exist in the database.\n"); + s += wxT(".\n"); + } + else if (checkPrivileges) + { + // Verify the user has rights to access the table. + // Shortcut boolean evaluation to optimize out call to + // TablePrivileges + // + // Unfortunately this optimization doesn't seem to be + // reliable! + if (// *(pDb->dbInf.accessibleTables) == 'N' && + !pDb->TablePrivileges(tableName,wxT("SELECT"),NULL,pDb->GetUsername(),tablePath)) + s = wxT("Current logged in user does not have sufficient privileges to access this table.\n"); + } + + if (!s.IsEmpty()) + { + wxString p; + + if (!tablePath.IsEmpty()) + p.Printf(wxT("Error opening '%s/%s'.\n"),tablePath.c_str(),tableName.c_str()); else - s += wxT("Current logged in user does not have sufficient privileges to access this table.\n"); - pDb->LogError(s.c_str()); + p.Printf(wxT("Error opening '%s'.\n"), tableName.c_str()); + + p += s; + pDb->LogError(p.GetData()); + return(FALSE); } @@ -645,21 +745,20 @@ bool wxDbTable::Open(void) // the wxDbTable object and the ODBC record. if (!queryOnly) { - if (!bindInsertParams()) // Inserts return(FALSE); if (!bindUpdateParams()) // Updates return(FALSE); - } + if (!bindCols(*hstmtDefault)) // Selects return(FALSE); - + if (!bindCols(hstmtInternal)) // Internal use only return(FALSE); - - /* + + /* * Do NOT bind the hstmtCount cursor!!! */ @@ -667,42 +766,40 @@ bool wxDbTable::Open(void) if (!queryOnly && noCols > 0) { bool needComma = FALSE; - sqlStmt.sprintf("INSERT INTO %s (", tableName); + sqlStmt.Printf(wxT("INSERT INTO %s ("), tableName.c_str()); for (i = 0; i < noCols; i++) { if (! colDefs[i].InsertAllowed) continue; if (needComma) - sqlStmt += ","; + sqlStmt += wxT(","); sqlStmt += colDefs[i].ColName; needComma = TRUE; } needComma = FALSE; - sqlStmt += ") VALUES ("; + sqlStmt += wxT(") VALUES ("); - int insertableCount = 0; + int insertableCount = 0; for (i = 0; i < noCols; i++) { if (! colDefs[i].InsertAllowed) continue; if (needComma) - sqlStmt += ","; - sqlStmt += "?"; + sqlStmt += wxT(","); + sqlStmt += wxT("?"); needComma = TRUE; - insertableCount++; + insertableCount++; } - sqlStmt += ")"; - -// pDb->WriteSqlLog(sqlStmt); - + sqlStmt += wxT(")"); + // Prepare the insert statement for execution if (insertableCount) - { + { if (SQLPrepare(hstmtInsert, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS) return(pDb->DispAllErrors(henv, hdbc, hstmtInsert)); } - else + else insertable= FALSE; } @@ -722,7 +819,7 @@ bool wxDbTable::Query(bool forUpdate, bool distinct) /********** wxDbTable::QueryBySqlStmt() **********/ -bool wxDbTable::QueryBySqlStmt(const char *pSqlStmt) +bool wxDbTable::QueryBySqlStmt(const wxString &pSqlStmt) { pDb->WriteSqlLog(pSqlStmt); @@ -759,6 +856,7 @@ bool wxDbTable::GetPrev(void) } else return(getRec(SQL_FETCH_PRIOR)); + } // wxDbTable::GetPrev() @@ -772,6 +870,7 @@ bool wxDbTable::operator--(int) } else return(getRec(SQL_FETCH_PRIOR)); + } // wxDbTable::operator-- @@ -785,6 +884,7 @@ bool wxDbTable::GetFirst(void) } else return(getRec(SQL_FETCH_FIRST)); + } // wxDbTable::GetFirst() @@ -798,22 +898,92 @@ bool wxDbTable::GetLast(void) } else return(getRec(SQL_FETCH_LAST)); + } // wxDbTable::GetLast() -/********** wxDbTable::BuildSelectStmt() **********/ -void wxDbTable::BuildSelectStmt(char *pSqlStmt, int typeOfSelect, bool distinct) +/********** wxDbTable::BuildDeleteStmt() **********/ +void wxDbTable::BuildDeleteStmt(wxString &pSqlStmt, int typeOfDel, const wxString &pWhereClause) +{ + wxASSERT(!queryOnly); + if (queryOnly) + return; + + wxString whereClause; + + whereClause.Empty(); + + // 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.Length() == 0)) + { + pSqlStmt.Printf(wxT("DELETE FROM %s"), tableName.c_str()); + return; + } + + pSqlStmt.Printf(wxT("DELETE FROM %s WHERE "), tableName.c_str()); + + // 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; + wxChar rowid[wxDB_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, (UCHAR*) rowid, wxDB_ROWID_LEN, &cb) == SQL_SUCCESS) + { + pSqlStmt += wxT("ROWID = '"); + pSqlStmt += rowid; + pSqlStmt += wxT("'"); + break; + } + } + // Unable to delete by ROWID, so build a WHERE + // clause based on the keyfields. + BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS); + pSqlStmt += whereClause; + break; + case DB_DEL_WHERE: + pSqlStmt += pWhereClause; + break; + case DB_DEL_MATCHING: + BuildWhereClause(whereClause, DB_WHERE_MATCHING); + pSqlStmt += whereClause; + break; + } + +} // BuildDeleteStmt() + + +/***** DEPRECATED: use wxDbTable::BuildDeleteStmt(wxString &....) form *****/ +void wxDbTable::BuildDeleteStmt(wxChar *pSqlStmt, int typeOfDel, const wxString &pWhereClause) { - char whereClause[DB_MAX_WHERE_CLAUSE_LEN]; + wxString tempSqlStmt; + BuildDeleteStmt(tempSqlStmt, typeOfDel, pWhereClause); + wxStrcpy(pSqlStmt, tempSqlStmt); +} // wxDbTable::BuildDeleteStmt() - whereClause[0] = 0; + +/********** wxDbTable::BuildSelectStmt() **********/ +void wxDbTable::BuildSelectStmt(wxString &pSqlStmt, int typeOfSelect, bool distinct) +{ + wxString whereClause; + whereClause.Empty(); // Build a select statement to query the database - wxStrcpy(pSqlStmt, "SELECT "); + pSqlStmt = wxT("SELECT "); // SELECT DISTINCT values only? if (distinct) - wxStrcat(pSqlStmt, "DISTINCT "); + pSqlStmt += wxT("DISTINCT "); // Was a FROM clause specified to join tables to the base table? // Available for ::Query() only!!! @@ -833,12 +1003,12 @@ void wxDbTable::BuildSelectStmt(char *pSqlStmt, int typeOfSelect, bool distinct) // If joining tables, the base table column names must be qualified to avoid ambiguity if (appendFromClause) { - wxStrcat(pSqlStmt, queryTableName); - wxStrcat(pSqlStmt, "."); + pSqlStmt += queryTableName; + pSqlStmt += wxT("."); } - wxStrcat(pSqlStmt, colDefs[i].ColName); + pSqlStmt += colDefs[i].ColName; if (i + 1 < noCols) - wxStrcat(pSqlStmt, ","); + pSqlStmt += wxT(","); } // If the datasource supports ROWID, get this column as well. Exception: Don't retrieve @@ -848,17 +1018,17 @@ void wxDbTable::BuildSelectStmt(char *pSqlStmt, int typeOfSelect, bool distinct) // If joining tables, the base table column names must be qualified to avoid ambiguity if (appendFromClause) { - wxStrcat(pSqlStmt, ","); - wxStrcat(pSqlStmt, queryTableName); - wxStrcat(pSqlStmt, ".ROWID"); + pSqlStmt += wxT(","); + pSqlStmt += queryTableName; + pSqlStmt += wxT(".ROWID"); } else - wxStrcat(pSqlStmt, ",ROWID"); + pSqlStmt += wxT(",ROWID"); } // Append the FROM tablename portion - wxStrcat(pSqlStmt, " FROM "); - wxStrcat(pSqlStmt, queryTableName); + pSqlStmt += wxT(" FROM "); + pSqlStmt += queryTableName; // Sybase uses the HOLDLOCK keyword to lock a record during query. // The HOLDLOCK keyword follows the table name in the from clause. @@ -866,62 +1036,224 @@ void wxDbTable::BuildSelectStmt(char *pSqlStmt, int typeOfSelect, bool distinct) // NOHOLDLOCK (the default). Note: The "FOR UPDATE" clause // is parsed but ignored in SYBASE Transact-SQL. if (selectForUpdate && (pDb->Dbms() == dbmsSYBASE_ASA || pDb->Dbms() == dbmsSYBASE_ASE)) - wxStrcat(pSqlStmt, " HOLDLOCK"); + pSqlStmt += wxT(" HOLDLOCK"); if (appendFromClause) - wxStrcat(pSqlStmt, from); + pSqlStmt += from; // 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: + case DB_SELECT_WHERE: #if wxODBC_BACKWARD_COMPATABILITY - if (where && wxStrlen(where)) // May not want a where clause!!! + if (where && wxStrlen(where)) // May not want a where clause!!! #else - if (where.Length()) // May not want a where clause!!! + if (where.Length()) // May not want a where clause!!! #endif + { + pSqlStmt += wxT(" WHERE "); + pSqlStmt += where; + } + break; + case DB_SELECT_KEYFIELDS: + BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS); + if (whereClause.Length()) + { + pSqlStmt += wxT(" WHERE "); + pSqlStmt += whereClause; + } + break; + case DB_SELECT_MATCHING: + BuildWhereClause(whereClause, DB_WHERE_MATCHING); + if (whereClause.Length()) + { + pSqlStmt += wxT(" WHERE "); + pSqlStmt += whereClause; + } + break; + } + + // Append the ORDER BY clause +#if wxODBC_BACKWARD_COMPATABILITY + if (orderBy && wxStrlen(orderBy)) +#else + if (orderBy.Length()) +#endif + { + pSqlStmt += wxT(" ORDER BY "); + pSqlStmt += orderBy; + } + + // SELECT FOR UPDATE if told to do so and the datasource is capable. Sybase + // parses the FOR UPDATE clause but ignores it. See the comment above on the + // HOLDLOCK for Sybase. + if (selectForUpdate && CanSelectForUpdate()) + pSqlStmt += wxT(" FOR UPDATE"); + +} // wxDbTable::BuildSelectStmt() + + +/***** DEPRECATED: use wxDbTable::BuildSelectStmt(wxString &....) form *****/ +void wxDbTable::BuildSelectStmt(wxChar *pSqlStmt, int typeOfSelect, bool distinct) +{ + wxString tempSqlStmt; + BuildSelectStmt(tempSqlStmt, typeOfSelect, distinct); + wxStrcpy(pSqlStmt, tempSqlStmt); +} // wxDbTable::BuildSelectStmt() + + +/********** wxDbTable::BuildUpdateStmt() **********/ +void wxDbTable::BuildUpdateStmt(wxString &pSqlStmt, int typeOfUpd, const wxString &pWhereClause) +{ + wxASSERT(!queryOnly); + if (queryOnly) + return; + + wxString whereClause; + whereClause.Empty(); + + bool firstColumn = TRUE; + + pSqlStmt.Printf(wxT("UPDATE %s SET "), tableName.c_str()); + + // Append a list of columns to be updated + int i; + for (i = 0; i < noCols; i++) + { + // Only append Updateable columns + if (colDefs[i].Updateable) { - wxStrcat(pSqlStmt, " WHERE "); - wxStrcat(pSqlStmt, where); - } - break; - case DB_SELECT_KEYFIELDS: - BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS); - if (wxStrlen(whereClause)) - { - wxStrcat(pSqlStmt, " WHERE "); - wxStrcat(pSqlStmt, whereClause); + if (! firstColumn) + pSqlStmt += wxT(","); + else + firstColumn = FALSE; + pSqlStmt += colDefs[i].ColName; + pSqlStmt += wxT(" = ?"); } - break; - case DB_SELECT_MATCHING: - BuildWhereClause(whereClause, DB_WHERE_MATCHING); - if (wxStrlen(whereClause)) + } + + // Append the WHERE clause to the SQL UPDATE statement + pSqlStmt += wxT(" 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 Col1 = ?, Col2 = ? WHERE ROWID = '111.222.333' + if (CanUpdByROWID()) + { + SDWORD cb; + wxChar rowid[wxDB_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, (UCHAR*) rowid, wxDB_ROWID_LEN, &cb) == SQL_SUCCESS) + { + pSqlStmt += wxT("ROWID = '"); + pSqlStmt += rowid; + pSqlStmt += wxT("'"); + break; + } + } + // Unable to delete by ROWID, so build a WHERE + // clause based on the keyfields. + BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS); + pSqlStmt += whereClause; + break; + case DB_UPD_WHERE: + pSqlStmt += pWhereClause; + break; + } +} // BuildUpdateStmt() + + +/***** DEPRECATED: use wxDbTable::BuildUpdateStmt(wxString &....) form *****/ +void wxDbTable::BuildUpdateStmt(wxChar *pSqlStmt, int typeOfUpd, const wxString &pWhereClause) +{ + wxString tempSqlStmt; + BuildUpdateStmt(tempSqlStmt, typeOfUpd, pWhereClause); + wxStrcpy(pSqlStmt, tempSqlStmt); +} // BuildUpdateStmt() + + +/********** wxDbTable::BuildWhereClause() **********/ +void wxDbTable::BuildWhereClause(wxString &pWhereClause, int typeOfWhere, + const wxString &qualTableName, bool useLikeComparison) +/* + * Note: BuildWhereClause() currently ignores timestamp columns. + * They are not included as part of the where clause. + */ +{ + bool moreThanOneColumn = FALSE; + wxString colValue; + + // Loop through the columns building a where clause as you go + int i; + for (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)))) { - wxStrcat(pSqlStmt, " WHERE "); - wxStrcat(pSqlStmt, whereClause); + // 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) + pWhereClause += wxT(" AND "); + else + moreThanOneColumn = TRUE; + // Concatenate where phrase for the column + if (qualTableName.Length()) + { + pWhereClause += qualTableName; + pWhereClause += wxT("."); + } + pWhereClause += colDefs[i].ColName; + if (useLikeComparison && (colDefs[i].SqlCtype == SQL_C_CHAR)) + pWhereClause += wxT(" LIKE "); + else + pWhereClause += wxT(" = "); + switch(colDefs[i].SqlCtype) + { + case SQL_C_CHAR: + colValue.Printf(wxT("'%s'"), (UCHAR FAR *) colDefs[i].PtrDataObj); + break; + case SQL_C_SSHORT: + colValue.Printf(wxT("%hi"), *((SWORD *) colDefs[i].PtrDataObj)); + break; + case SQL_C_USHORT: + colValue.Printf(wxT("%hu"), *((UWORD *) colDefs[i].PtrDataObj)); + break; + case SQL_C_SLONG: + colValue.Printf(wxT("%li"), *((SDWORD *) colDefs[i].PtrDataObj)); + break; + case SQL_C_ULONG: + colValue.Printf(wxT("%lu"), *((UDWORD *) colDefs[i].PtrDataObj)); + break; + case SQL_C_FLOAT: + colValue.Printf(wxT("%.6f"), *((SFLOAT *) colDefs[i].PtrDataObj)); + break; + case SQL_C_DOUBLE: + colValue.Printf(wxT("%.6f"), *((SDOUBLE *) colDefs[i].PtrDataObj)); + break; + } + pWhereClause += colValue; } - break; - } - - // Append the ORDER BY clause -#if wxODBC_BACKWARD_COMPATABILITY - if (orderBy && wxStrlen(orderBy)) -#else - if (orderBy.Length()) -#endif - { - wxStrcat(pSqlStmt, " ORDER BY "); - wxStrcat(pSqlStmt, orderBy); } +} // wxDbTable::BuildWhereClause() - // SELECT FOR UPDATE if told to do so and the datasource is capable. Sybase - // parses the FOR UPDATE clause but ignores it. See the comment above on the - // HOLDLOCK for Sybase. - if (selectForUpdate && CanSelectForUpdate()) - wxStrcat(pSqlStmt, " FOR UPDATE"); -} // wxDbTable::BuildSelectStmt() +/***** DEPRECATED: use wxDbTable::BuildWhereClause(wxString &....) form *****/ +void wxDbTable::BuildWhereClause(wxChar *pWhereClause, int typeOfWhere, + const wxString &qualTableName, bool useLikeComparison) +{ + wxString tempSqlStmt; + BuildWhereClause(tempSqlStmt, typeOfWhere, qualTableName, useLikeComparison); + wxStrcpy(pWhereClause, tempSqlStmt); +} // wxDbTable::BuildWhereClause() /********** wxDbTable::GetRowNum() **********/ @@ -960,11 +1292,10 @@ bool wxDbTable::CreateTable(bool attemptDrop) return FALSE; int i, j; -// char sqlStmt[DB_MAX_STATEMENT_LEN]; wxString sqlStmt; #ifdef DBDEBUG_CONSOLE - cout << "Creating Table " << tableName << "..." << endl; + cout << wxT("Creating Table ") << tableName << wxT("...") << endl; #endif // Drop table first @@ -978,11 +1309,11 @@ bool wxDbTable::CreateTable(bool attemptDrop) // Exclude derived columns since they are NOT part of the base table if (colDefs[i].DerivedCol) continue; - cout << i + 1 << ": " << colDefs[i].ColName << "; "; + cout << i + 1 << wxT(": ") << colDefs[i].ColName << wxT("; "); switch(colDefs[i].DbDataType) { case DB_DATA_TYPE_VARCHAR: - cout << pDb->typeInfVarchar.TypeName << "(" << colDefs[i].SzDataObj << ")"; + cout << pDb->typeInfVarchar.TypeName << wxT("(") << colDefs[i].SzDataObj << wxT(")"); break; case DB_DATA_TYPE_INTEGER: cout << pDb->typeInfInteger.TypeName; @@ -993,6 +1324,9 @@ bool wxDbTable::CreateTable(bool attemptDrop) case DB_DATA_TYPE_DATE: cout << pDb->typeInfDate.TypeName; break; + case DB_DATA_TYPE_BLOB: + cout << pDb->typeInfBlob.TypeName; + break; } cout << endl; } @@ -1000,7 +1334,7 @@ bool wxDbTable::CreateTable(bool attemptDrop) // Build a CREATE TABLE string from the colDefs structure. bool needComma = FALSE; - sqlStmt.sprintf("CREATE TABLE %s (", tableName); + sqlStmt.Printf(wxT("CREATE TABLE %s ("), tableName.c_str()); for (i = 0; i < noCols; i++) { @@ -1009,38 +1343,46 @@ bool wxDbTable::CreateTable(bool attemptDrop) continue; // Comma Delimiter if (needComma) - sqlStmt += ","; + sqlStmt += wxT(","); // Column Name sqlStmt += colDefs[i].ColName; - sqlStmt += " "; + sqlStmt += wxT(" "); // Column Type switch(colDefs[i].DbDataType) { case DB_DATA_TYPE_VARCHAR: - sqlStmt += pDb->GetTypeInfVarchar().TypeName; break; + sqlStmt += pDb->GetTypeInfVarchar().TypeName; + break; case DB_DATA_TYPE_INTEGER: - sqlStmt += pDb->GetTypeInfInteger().TypeName; break; + sqlStmt += pDb->GetTypeInfInteger().TypeName; + break; case DB_DATA_TYPE_FLOAT: - sqlStmt += pDb->GetTypeInfFloat().TypeName; break; + sqlStmt += pDb->GetTypeInfFloat().TypeName; + break; case DB_DATA_TYPE_DATE: - sqlStmt += pDb->GetTypeInfDate().TypeName; break; + sqlStmt += pDb->GetTypeInfDate().TypeName; + break; + case DB_DATA_TYPE_BLOB: + sqlStmt += pDb->GetTypeInfBlob().TypeName; + break; } // For varchars, append the size of the string - if (colDefs[i].DbDataType == DB_DATA_TYPE_VARCHAR) + if (colDefs[i].DbDataType == DB_DATA_TYPE_VARCHAR)// || +// colDefs[i].DbDataType == DB_DATA_TYPE_BLOB) { wxString s; - // wxStrcat(sqlStmt, "("); - // wxStrcat(sqlStmt, itoa(colDefs[i].SzDataObj, s, 10)); - // wxStrcat(sqlStmt, ")"); - s.sprintf("(%d)", colDefs[i].SzDataObj); - sqlStmt += s.c_str(); + s.Printf(wxT("(%d)"), colDefs[i].SzDataObj); + sqlStmt += s; } - if (pDb->Dbms() == dbmsSYBASE_ASE || pDb->Dbms() == dbmsMY_SQL) + if (pDb->Dbms() == dbmsDB2 || + pDb->Dbms() == dbmsMY_SQL || + pDb->Dbms() == dbmsSYBASE_ASE || + pDb->Dbms() == dbmsMS_SQL_SERVER) { if (colDefs[i].KeyField) { - sqlStmt += " NOT NULL"; + sqlStmt += wxT(" NOT NULL"); } } @@ -1059,14 +1401,14 @@ bool wxDbTable::CreateTable(bool attemptDrop) { if (pDb->Dbms() != dbmsMY_SQL) { - sqlStmt += ",CONSTRAINT "; + sqlStmt += wxT(",CONSTRAINT "); sqlStmt += tableName; - sqlStmt += "_PIDX PRIMARY KEY ("; + sqlStmt += wxT("_PIDX PRIMARY KEY ("); } else { /* MySQL goes out on this one. We also declare the relevant key NON NULL above */ - sqlStmt += ", PRIMARY KEY ("; + sqlStmt += wxT(", PRIMARY KEY ("); } // List column name(s) of column(s) comprising the primary key @@ -1075,16 +1417,16 @@ bool wxDbTable::CreateTable(bool attemptDrop) if (colDefs[i].KeyField) { if (j++) // Multi part key, comma separate names - sqlStmt += ","; + sqlStmt += wxT(","); sqlStmt += colDefs[i].ColName; } } - sqlStmt += ")"; + sqlStmt += wxT(")"); } // Append the closing parentheses for the create table statement - sqlStmt += ")"; + sqlStmt += wxT(")"); - pDb->WriteSqlLog(sqlStmt.c_str()); + pDb->WriteSqlLog(sqlStmt); #ifdef DBDEBUG_CONSOLE cout << endl << sqlStmt.c_str() << endl; @@ -1101,9 +1443,9 @@ bool wxDbTable::CreateTable(bool attemptDrop) } // Commit the transaction and close the cursor - if (! pDb->CommitTrans()) + if (!pDb->CommitTrans()) return(FALSE); - if (! CloseCursor(hstmt)) + if (!CloseCursor(hstmt)) return(FALSE); // Database table created successfully @@ -1122,9 +1464,9 @@ bool wxDbTable::DropTable() wxString sqlStmt; - sqlStmt.sprintf("DROP TABLE %s", tableName); + sqlStmt.Printf(wxT("DROP TABLE %s"), tableName.c_str()); - pDb->WriteSqlLog(sqlStmt.c_str()); + pDb->WriteSqlLog(sqlStmt); #ifdef DBDEBUG_CONSOLE cout << endl << sqlStmt.c_str() << endl; @@ -1133,13 +1475,14 @@ bool wxDbTable::DropTable() if (SQLExecDirect(hstmt, (UCHAR FAR *) sqlStmt.c_str(), SQL_NTS) != SQL_SUCCESS) { // Check for "Base table not found" error and ignore - pDb->GetNextError(henv, hdbc, hstmt); - if (wxStrcmp(pDb->sqlState,"S0002") && wxStrcmp(pDb->sqlState, "S1000")) // "Base table not found" - { + pDb->GetNextError(henv, hdbc, hstmt); + if (wxStrcmp(pDb->sqlState, wxT("S0002")) && + wxStrcmp(pDb->sqlState, wxT("S1000"))) // "Base table not found" + { // Check for product specific error codes - if (!((pDb->Dbms() == dbmsSYBASE_ASA && !wxStrcmp(pDb->sqlState,"42000")) || // 5.x (and lower?) - (pDb->Dbms() == dbmsSYBASE_ASE && !wxStrcmp(pDb->sqlState,"37000")) || - (pDb->Dbms() == dbmsPOSTGRES && !wxStrcmp(pDb->sqlState,"08S01")))) + if (!((pDb->Dbms() == dbmsSYBASE_ASA && !wxStrcmp(pDb->sqlState,wxT("42000"))) || // 5.x (and lower?) + (pDb->Dbms() == dbmsSYBASE_ASE && !wxStrcmp(pDb->sqlState,wxT("37000"))) || + (pDb->Dbms() == dbmsPOSTGRES && !wxStrcmp(pDb->sqlState,wxT("08S01"))))) { pDb->DispNextError(); pDb->DispAllErrors(henv, hdbc, hstmt); @@ -1161,26 +1504,81 @@ bool wxDbTable::DropTable() /********** wxDbTable::CreateIndex() **********/ -bool wxDbTable::CreateIndex(const char * idxName, bool unique, int noIdxCols, wxDbIdxDef *pIdxDefs, bool attemptDrop) +bool wxDbTable::CreateIndex(const wxString &idxName, bool unique, int noIdxCols, wxDbIdxDef *pIdxDefs, bool attemptDrop) { -// char sqlStmt[DB_MAX_STATEMENT_LEN]; wxString sqlStmt; // Drop the index first if (attemptDrop && !DropIndex(idxName)) return (FALSE); + // MySQL (and possibly Sybase ASE?? - gt) require that any columns which are used as portions + // of an index have the columns defined as "NOT NULL". During initial table creation though, + // it may not be known which columns are necessarily going to be part of an index (e.g. the + // table was created, then months later you determine that an additional index while + // give better performance, so you want to add an index). + // + // The following block of code will modify the column definition to make the column be + // defined with the "NOT NULL" qualifier. + if (pDb->Dbms() == dbmsMY_SQL) + { + wxString sqlStmt; + int i; + bool ok = TRUE; + for (i = 0; i < noIdxCols && ok; i++) + { + int j = 0; + bool found = FALSE; + // Find the column definition that has the ColName that matches the + // index column name. We need to do this to get the DB_DATA_TYPE of + // the index column, as MySQL's syntax for the ALTER column requires + // this information + while (!found && (j < this->noCols)) + { + if (wxStrcmp(colDefs[j].ColName,pIdxDefs[i].ColName) == 0) + found = TRUE; + if (!found) + j++; + } + + if (found) + { + ok = pDb->ModifyColumn(tableName, pIdxDefs[i].ColName, + colDefs[j].DbDataType, colDefs[j].SzDataObj, + wxT("NOT NULL")); + + if (!ok) + { + wxODBC_ERRORS retcode; + // Oracle returns a DB_ERR_GENERAL_ERROR if the column is already + // defined to be NOT NULL, but reportedly MySQL doesn't mind. + // This line is just here for debug checking of the value + retcode = (wxODBC_ERRORS)pDb->DB_STATUS; + } + } + else + ok = FALSE; + } + if (ok) + pDb->CommitTrans(); + else + { + pDb->RollbackTrans(); + return(FALSE); + } + } + // Build a CREATE INDEX statement - sqlStmt = "CREATE "; + sqlStmt = wxT("CREATE "); if (unique) - sqlStmt += "UNIQUE "; - - sqlStmt += "INDEX "; + sqlStmt += wxT("UNIQUE "); + + sqlStmt += wxT("INDEX "); sqlStmt += idxName; - sqlStmt += " ON "; + sqlStmt += wxT(" ON "); sqlStmt += tableName; - sqlStmt += " ("; - + sqlStmt += wxT(" ("); + // Append list of columns making up index int i; for (i = 0; i < noIdxCols; i++) @@ -1190,19 +1588,19 @@ bool wxDbTable::CreateIndex(const char * idxName, bool unique, int noIdxCols, wx if (pDb->Dbms() != dbmsPOSTGRES) { if (pIdxDefs[i].Ascending) - sqlStmt += " ASC"; + sqlStmt += wxT(" ASC"); else - sqlStmt += " DESC"; + sqlStmt += wxT(" DESC"); } if ((i + 1) < noIdxCols) - sqlStmt += ","; + sqlStmt += wxT(","); } // Append closing parentheses - sqlStmt += ")"; + sqlStmt += wxT(")"); - pDb->WriteSqlLog(sqlStmt.c_str()); + pDb->WriteSqlLog(sqlStmt); #ifdef DBDEBUG_CONSOLE cout << endl << sqlStmt.c_str() << endl << endl; @@ -1230,7 +1628,7 @@ bool wxDbTable::CreateIndex(const char * idxName, bool unique, int noIdxCols, wx /********** wxDbTable::DropIndex() **********/ -bool wxDbTable::DropIndex(const char * idxName) +bool wxDbTable::DropIndex(const wxString &idxName) { // NOTE: This function returns TRUE if the Index does not exist, but // only for identified databases. Code will need to be added @@ -1239,14 +1637,15 @@ bool wxDbTable::DropIndex(const char * idxName) wxString sqlStmt; - if (pDb->Dbms() == dbmsACCESS) - sqlStmt.sprintf("DROP INDEX %s ON %s",idxName,tableName); - else if (pDb->Dbms() == dbmsSYBASE_ASE) - sqlStmt.sprintf("DROP INDEX %s.%s",tableName,idxName); + if (pDb->Dbms() == dbmsACCESS || pDb->Dbms() == dbmsMY_SQL) + sqlStmt.Printf(wxT("DROP INDEX %s ON %s"),idxName.c_str(), tableName.c_str()); + else if ((pDb->Dbms() == dbmsMS_SQL_SERVER) || + (pDb->Dbms() == dbmsSYBASE_ASE)) + sqlStmt.Printf(wxT("DROP INDEX %s.%s"),tableName.c_str(), idxName.c_str()); else - sqlStmt.sprintf("DROP INDEX %s",idxName); + sqlStmt.Printf(wxT("DROP INDEX %s"),idxName.c_str()); - pDb->WriteSqlLog(sqlStmt.c_str()); + pDb->WriteSqlLog(sqlStmt); #ifdef DBDEBUG_CONSOLE cout << endl << sqlStmt.c_str() << endl; @@ -1256,16 +1655,16 @@ bool wxDbTable::DropIndex(const char * idxName) { // Check for "Index not found" error and ignore pDb->GetNextError(henv, hdbc, hstmt); - if (wxStrcmp(pDb->sqlState,"S0012")) // "Index not found" + if (wxStrcmp(pDb->sqlState,wxT("S0012"))) // "Index not found" { // Check for product specific error codes - if (!((pDb->Dbms() == dbmsSYBASE_ASA && !wxStrcmp(pDb->sqlState,"42000")) || // v5.x (and lower?) - (pDb->Dbms() == dbmsSYBASE_ASE && !wxStrcmp(pDb->sqlState,"37000")) || - (pDb->Dbms() == dbmsMS_SQL_SERVER && !wxStrcmp(pDb->sqlState,"S1000")) || - (pDb->Dbms() == dbmsSYBASE_ASE && !wxStrcmp(pDb->sqlState,"S0002")) || // Base table not found - (pDb->Dbms() == dbmsMY_SQL && !wxStrcmp(pDb->sqlState,"42S02")) || // untested - (pDb->Dbms() == dbmsPOSTGRES && !wxStrcmp(pDb->sqlState,"08S01")) - )) + if (!((pDb->Dbms() == dbmsSYBASE_ASA && !wxStrcmp(pDb->sqlState,wxT("42000"))) || // v5.x (and lower?) + (pDb->Dbms() == dbmsSYBASE_ASE && !wxStrcmp(pDb->sqlState,wxT("37000"))) || + (pDb->Dbms() == dbmsMS_SQL_SERVER && !wxStrcmp(pDb->sqlState,wxT("S1000"))) || + (pDb->Dbms() == dbmsSYBASE_ASE && !wxStrcmp(pDb->sqlState,wxT("S0002"))) || // Base table not found + (pDb->Dbms() == dbmsMY_SQL && !wxStrcmp(pDb->sqlState,wxT("42S12"))) || // tested by Christopher Ludwik Marino-Cebulski using v3.23.21beta + (pDb->Dbms() == dbmsPOSTGRES && !wxStrcmp(pDb->sqlState,wxT("08S01"))) + )) { pDb->DispNextError(); pDb->DispAllErrors(henv, hdbc, hstmt); @@ -1286,10 +1685,46 @@ bool wxDbTable::DropIndex(const char * idxName) } // wxDbTable::DropIndex() +/********** wxDbTable::SetOrderByColNums() **********/ +bool wxDbTable::SetOrderByColNums(int first, ... ) +{ + int colNo = first; + va_list argptr; + + bool abort = FALSE; + wxString tempStr; + + va_start(argptr, first); /* Initialize variable arguments. */ + while (!abort && (colNo != wxDB_NO_MORE_COLUMN_NUMBERS)) + { + // Make sure the passed in column number + // is within the valid range of columns + // + // Valid columns are 0 thru noCols-1 + if (colNo >= noCols || colNo < 0) + { + abort = TRUE; + continue; + } + + if (colNo != first) + tempStr += wxT(","); + + tempStr += colDefs[colNo].ColName; + colNo = va_arg (argptr, int); + } + va_end (argptr); /* Reset variable arguments. */ + + SetOrderByClause(tempStr); + + return (!abort); +} // wxDbTable::SetOrderByColNums() + + /********** wxDbTable::Insert() **********/ int wxDbTable::Insert(void) { - assert(!queryOnly); + wxASSERT(!queryOnly); if (queryOnly || !insertable) return(DB_FAILURE); @@ -1302,7 +1737,7 @@ int wxDbTable::Insert(void) { // Check to see if integrity constraint was violated pDb->GetNextError(henv, hdbc, hstmtInsert); - if (! wxStrcmp(pDb->sqlState, "23000")) // Integrity constraint violated + if (! wxStrcmp(pDb->sqlState, wxT("23000"))) // Integrity constraint violated return(DB_ERR_INTEGRITY_CONSTRAINT_VIOL); else { @@ -1321,11 +1756,11 @@ int wxDbTable::Insert(void) /********** wxDbTable::Update() **********/ bool wxDbTable::Update(void) { - assert(!queryOnly); + wxASSERT(!queryOnly); if (queryOnly) return(FALSE); - char sqlStmt[DB_MAX_STATEMENT_LEN]; + wxString sqlStmt; // Build the SQL UPDATE statement BuildUpdateStmt(sqlStmt, DB_UPD_KEYFIELDS); @@ -1333,7 +1768,7 @@ bool wxDbTable::Update(void) pDb->WriteSqlLog(sqlStmt); #ifdef DBDEBUG_CONSOLE - cout << endl << sqlStmt << endl << endl; + cout << endl << sqlStmt.c_str() << endl << endl; #endif // Execute the SQL UPDATE statement @@ -1343,9 +1778,9 @@ bool wxDbTable::Update(void) /********** wxDbTable::Update(pSqlStmt) **********/ -bool wxDbTable::Update(const char *pSqlStmt) +bool wxDbTable::Update(const wxString &pSqlStmt) { - assert(!queryOnly); + wxASSERT(!queryOnly); if (queryOnly) return(FALSE); @@ -1357,13 +1792,13 @@ bool wxDbTable::Update(const char *pSqlStmt) /********** wxDbTable::UpdateWhere() **********/ -bool wxDbTable::UpdateWhere(const char *pWhereClause) +bool wxDbTable::UpdateWhere(const wxString &pWhereClause) { - assert(!queryOnly); + wxASSERT(!queryOnly); if (queryOnly) return(FALSE); - char sqlStmt[DB_MAX_STATEMENT_LEN]; + wxString sqlStmt; // Build the SQL UPDATE statement BuildUpdateStmt(sqlStmt, DB_UPD_WHERE, pWhereClause); @@ -1371,7 +1806,7 @@ bool wxDbTable::UpdateWhere(const char *pWhereClause) pDb->WriteSqlLog(sqlStmt); #ifdef DBDEBUG_CONSOLE - cout << endl << sqlStmt << endl << endl; + cout << endl << sqlStmt.c_str() << endl << endl; #endif // Execute the SQL UPDATE statement @@ -1383,11 +1818,12 @@ bool wxDbTable::UpdateWhere(const char *pWhereClause) /********** wxDbTable::Delete() **********/ bool wxDbTable::Delete(void) { - assert(!queryOnly); + wxASSERT(!queryOnly); if (queryOnly) return(FALSE); - char sqlStmt[DB_MAX_STATEMENT_LEN]; + wxString sqlStmt; + sqlStmt.Empty(); // Build the SQL DELETE statement BuildDeleteStmt(sqlStmt, DB_DEL_KEYFIELDS); @@ -1401,13 +1837,14 @@ bool wxDbTable::Delete(void) /********** wxDbTable::DeleteWhere() **********/ -bool wxDbTable::DeleteWhere(const char *pWhereClause) +bool wxDbTable::DeleteWhere(const wxString &pWhereClause) { - assert(!queryOnly); + wxASSERT(!queryOnly); if (queryOnly) return(FALSE); - char sqlStmt[DB_MAX_STATEMENT_LEN]; + wxString sqlStmt; + sqlStmt.Empty(); // Build the SQL DELETE statement BuildDeleteStmt(sqlStmt, DB_DEL_WHERE, pWhereClause); @@ -1423,11 +1860,12 @@ bool wxDbTable::DeleteWhere(const char *pWhereClause) /********** wxDbTable::DeleteMatching() **********/ bool wxDbTable::DeleteMatching(void) { - assert(!queryOnly); + wxASSERT(!queryOnly); if (queryOnly) return(FALSE); - char sqlStmt[DB_MAX_STATEMENT_LEN]; + wxString sqlStmt; + sqlStmt.Empty(); // Build the SQL DELETE statement BuildDeleteStmt(sqlStmt, DB_DEL_MATCHING); @@ -1440,239 +1878,55 @@ bool wxDbTable::DeleteMatching(void) } // wxDbTable::DeleteMatching() -/********** wxDbTable::BuildUpdateStmt() **********/ -void wxDbTable::BuildUpdateStmt(char *pSqlStmt, int typeOfUpd, const char *pWhereClause) -{ - assert(!queryOnly); - if (queryOnly) - return; - - 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 - int i; - for (i = 0; i < noCols; i++) - { - // Only append Updateable columns - if (colDefs[i].Updateable) - { - if (! firstColumn) - wxStrcat(pSqlStmt, ","); - else - firstColumn = FALSE; - wxStrcat(pSqlStmt, colDefs[i].ColName); - wxStrcat(pSqlStmt, " = ?"); - } - } - - // Append the WHERE clause to the SQL UPDATE statement - wxStrcat(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 Col1 = ?, Col2 = ? WHERE ROWID = '111.222.333' - if (CanUpdByROWID()) - { - SDWORD cb; - char rowid[wxDB_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, (UCHAR*) rowid, wxDB_ROWID_LEN, &cb) == SQL_SUCCESS) - { - wxStrcat(pSqlStmt, "ROWID = '"); - wxStrcat(pSqlStmt, rowid); - wxStrcat(pSqlStmt, "'"); - break; - } - } - // Unable to delete by ROWID, so build a WHERE - // clause based on the keyfields. - BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS); - wxStrcat(pSqlStmt, whereClause); - break; - case DB_UPD_WHERE: - wxStrcat(pSqlStmt, pWhereClause); - break; - } -} // BuildUpdateStmt() - - -/********** wxDbTable::BuildDeleteStmt() **********/ -void wxDbTable::BuildDeleteStmt(char *pSqlStmt, int typeOfDel, const char *pWhereClause) -{ - assert(!queryOnly); - if (queryOnly) - return; - - 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 || wxStrlen(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[wxDB_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, (UCHAR*) rowid, wxDB_ROWID_LEN, &cb) == SQL_SUCCESS) - { - wxStrcat(pSqlStmt, "ROWID = '"); - wxStrcat(pSqlStmt, rowid); - wxStrcat(pSqlStmt, "'"); - break; - } - } - // Unable to delete by ROWID, so build a WHERE - // clause based on the keyfields. - BuildWhereClause(whereClause, DB_WHERE_KEYFIELDS); - wxStrcat(pSqlStmt, whereClause); - break; - case DB_DEL_WHERE: - wxStrcat(pSqlStmt, pWhereClause); - break; - case DB_DEL_MATCHING: - BuildWhereClause(whereClause, DB_WHERE_MATCHING); - wxStrcat(pSqlStmt, whereClause); - break; - } - -} // BuildDeleteStmt() - - -/********** wxDbTable::BuildWhereClause() **********/ -void wxDbTable::BuildWhereClause(char *pWhereClause, int typeOfWhere, - const char *qualTableName, bool useLikeComparison) -/* - * Note: BuildWhereClause() currently ignores timestamp columns. - * They are not included as part of the where clause. - */ -{ - bool moreThanOneColumn = FALSE; - char colValue[255]; - - // Loop through the columns building a where clause as you go - int i; - for (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) - wxStrcat(pWhereClause, " AND "); - else - moreThanOneColumn = TRUE; - // Concatenate where phrase for the column - if (qualTableName && wxStrlen(qualTableName)) - { - wxStrcat(pWhereClause, qualTableName); - wxStrcat(pWhereClause, "."); - } - wxStrcat(pWhereClause, colDefs[i].ColName); - if (useLikeComparison && (colDefs[i].SqlCtype == SQL_C_CHAR)) - wxStrcat(pWhereClause, " LIKE "); - else - wxStrcat(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; - } - wxStrcat(pWhereClause, colValue); - } - } -} // wxDbTable::BuildWhereClause() - - /********** wxDbTable::IsColNull() **********/ bool wxDbTable::IsColNull(int colNo) { +/* + This logic is just not right. It would indicate TRUE + if a numeric field were set to a value of 0. + 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) + 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); - else - return(FALSE); - default: - return(TRUE); } +*/ + return (colDefs[colNo].Null); } // wxDbTable::IsColNull() /********** wxDbTable::CanSelectForUpdate() **********/ bool wxDbTable::CanSelectForUpdate(void) { + if (queryOnly) + return FALSE; + if (pDb->Dbms() == dbmsMY_SQL) return FALSE; - if (pDb->dbInf.posStmts & SQL_PS_SELECT_FOR_UPDATE) + if ((pDb->Dbms() == dbmsORACLE) || + (pDb->dbInf.posStmts & SQL_PS_SELECT_FOR_UPDATE)) return(TRUE); else return(FALSE); @@ -1708,39 +1962,38 @@ bool wxDbTable::IsCursorClosedOnCommit(void) } // wxDbTable::IsCursorClosedOnCommit() -/********** wxDbTable::ClearMemberVars() **********/ -void wxDbTable::ClearMemberVars(void) + +/********** wxDbTable::ClearMemberVar() **********/ +void wxDbTable::ClearMemberVar(int colNo, bool setToNull) { - // Loop through the columns setting each member variable to zero - int i; - for (i = 0; i < noCols; i++) + wxASSERT(colNo < noCols); + + switch(colDefs[colNo].SqlCtype) { - switch(colDefs[i].SqlCtype) - { case SQL_C_CHAR: - ((UCHAR FAR *) colDefs[i].PtrDataObj)[0] = 0; + ((UCHAR FAR *) colDefs[colNo].PtrDataObj)[0] = 0; break; case SQL_C_SSHORT: - *((SWORD *) colDefs[i].PtrDataObj) = 0; + *((SWORD *) colDefs[colNo].PtrDataObj) = 0; break; case SQL_C_USHORT: - *((UWORD*) colDefs[i].PtrDataObj) = 0; + *((UWORD*) colDefs[colNo].PtrDataObj) = 0; break; case SQL_C_SLONG: - *((SDWORD *) colDefs[i].PtrDataObj) = 0; + *((SDWORD *) colDefs[colNo].PtrDataObj) = 0; break; case SQL_C_ULONG: - *((UDWORD *) colDefs[i].PtrDataObj) = 0; + *((UDWORD *) colDefs[colNo].PtrDataObj) = 0; break; case SQL_C_FLOAT: - *((SFLOAT *) colDefs[i].PtrDataObj) = 0.0f; + *((SFLOAT *) colDefs[colNo].PtrDataObj) = 0.0f; break; case SQL_C_DOUBLE: - *((SDOUBLE *) colDefs[i].PtrDataObj) = 0.0f; + *((SDOUBLE *) colDefs[colNo].PtrDataObj) = 0.0f; break; case SQL_C_TIMESTAMP: TIMESTAMP_STRUCT *pDt; - pDt = (TIMESTAMP_STRUCT *) colDefs[i].PtrDataObj; + pDt = (TIMESTAMP_STRUCT *) colDefs[colNo].PtrDataObj; pDt->year = 0; pDt->month = 0; pDt->day = 0; @@ -1749,10 +2002,22 @@ void wxDbTable::ClearMemberVars(void) pDt->second = 0; pDt->fraction = 0; break; - - } } + if (setToNull) + SetColNull(colNo); +} // wxDbTable::ClearMemberVar() + + +/********** wxDbTable::ClearMemberVars() **********/ +void wxDbTable::ClearMemberVars(bool setToNull) +{ + int i; + + // Loop through the columns setting each member variable to zero + for (i=0; i < noCols; i++) + ClearMemberVar(i,setToNull); + } // wxDbTable::ClearMemberVars() @@ -1775,14 +2040,14 @@ bool wxDbTable::SetQueryTimeout(UDWORD nSeconds) /********** wxDbTable::SetColDefs() **********/ -void wxDbTable::SetColDefs (int index, const char *fieldName, int dataType, void *pData, - int cType, int size, bool keyField, bool upd, - bool insAllow, bool derivedCol) +void wxDbTable::SetColDefs(int index, const wxString &fieldName, int dataType, void *pData, + int cType, int size, bool keyField, bool upd, + bool insAllow, bool derivedCol) { if (!colDefs) // May happen if the database connection fails return; - if (wxStrlen(fieldName) > (unsigned int) DB_MAX_COLUMN_NAME_LEN) + if (fieldName.Length() > (unsigned int) DB_MAX_COLUMN_NAME_LEN) { wxStrncpy (colDefs[index].ColName, fieldName, DB_MAX_COLUMN_NAME_LEN); colDefs[index].ColName[DB_MAX_COLUMN_NAME_LEN] = 0; @@ -1813,16 +2078,15 @@ void wxDbTable::SetColDefs (int index, const char *fieldName, int dataType, void } // wxDbTable::SetColDefs() -/********** wxDbTable::SetColDef() **********/ -wxDbColDataPtr* wxDbTable::SetColDefs (wxDbColInf *pColInfs, ULONG numCols) +/********** wxDbTable::SetColDefs() **********/ +wxDbColDataPtr* wxDbTable::SetColDefs(wxDbColInf *pColInfs, ULONG numCols) { - assert(pColInfs); + wxASSERT(pColInfs); wxDbColDataPtr *pColDataPtrs = NULL; if (pColInfs) { ULONG index; - pColDataPtrs = new wxDbColDataPtr[numCols+1]; @@ -1832,14 +2096,11 @@ wxDbColDataPtr* wxDbTable::SetColDefs (wxDbColInf *pColInfs, ULONG numCols) switch (pColInfs[index].dbDataType) { case DB_DATA_TYPE_VARCHAR: - { - pColDataPtrs[index].PtrDataObj = new char[pColInfs[index].bufferLength+1]; + pColDataPtrs[index].PtrDataObj = new wxChar[pColInfs[index].bufferLength+1]; pColDataPtrs[index].SzDataObj = pColInfs[index].columnSize; pColDataPtrs[index].SqlCtype = SQL_C_CHAR; break; - } case DB_DATA_TYPE_INTEGER: - { // Can be long or short if (pColInfs[index].bufferLength == sizeof(long)) { @@ -1854,9 +2115,7 @@ wxDbColDataPtr* wxDbTable::SetColDefs (wxDbColInf *pColInfs, ULONG numCols) pColDataPtrs[index].SqlCtype = SQL_C_SSHORT; } break; - } case DB_DATA_TYPE_FLOAT: - { // Can be float or double if (pColInfs[index].bufferLength == sizeof(float)) { @@ -1871,21 +2130,37 @@ wxDbColDataPtr* wxDbTable::SetColDefs (wxDbColInf *pColInfs, ULONG numCols) pColDataPtrs[index].SqlCtype = SQL_C_DOUBLE; } break; - } case DB_DATA_TYPE_DATE: - { pColDataPtrs[index].PtrDataObj = new TIMESTAMP_STRUCT; pColDataPtrs[index].SzDataObj = sizeof(TIMESTAMP_STRUCT); pColDataPtrs[index].SqlCtype = SQL_C_TIMESTAMP; break; - } + case DB_DATA_TYPE_BLOB: + int notSupportedYet = 0; + wxASSERT_MSG(notSupportedYet, wxT("This form of ::SetColDefs() cannot be used with BLOB columns")); + pColDataPtrs[index].PtrDataObj = /*BLOB ADDITION NEEDED*/NULL; + pColDataPtrs[index].SzDataObj = /*BLOB ADDITION NEEDED*/sizeof(void *); + pColDataPtrs[index].SqlCtype = SQL_VARBINARY; + break; + } + if (pColDataPtrs[index].PtrDataObj != NULL) + SetColDefs (index,pColInfs[index].colName,pColInfs[index].dbDataType, pColDataPtrs[index].PtrDataObj, pColDataPtrs[index].SqlCtype, pColDataPtrs[index].SzDataObj); + else + { + // Unable to build all the column definitions, as either one of + // the calls to "new" failed above, or there was a BLOB field + // to have a column definition for. If BLOBs are to be used, + // the other form of ::SetColDefs() must be used, as it is impossible + // to know the maximum size to create the PtrDataObj to be. + delete [] pColDataPtrs; + return NULL; } - - SetColDefs (index,pColInfs[index].colName,pColInfs[index].dbDataType, pColDataPtrs[index].PtrDataObj, pColDataPtrs[index].SqlCtype, pColDataPtrs[index].SzDataObj); } } + return (pColDataPtrs); -} // wxDbTable::SetColDef() + +} // wxDbTable::SetColDefs() /********** wxDbTable::SetCursor() **********/ @@ -1899,17 +2174,17 @@ void wxDbTable::SetCursor(HSTMT *hstmtActivate) } // wxDbTable::SetCursor() -/********** wxDbTable::Count(const char *) **********/ -ULONG wxDbTable::Count(const char *args) +/********** wxDbTable::Count(const wxString &) **********/ +ULONG wxDbTable::Count(const wxString &args) { - ULONG l; + ULONG count; wxString sqlStmt; SDWORD cb; // Build a "SELECT COUNT(*) FROM queryTableName [WHERE whereClause]" SQL Statement - sqlStmt = "SELECT COUNT("; + sqlStmt = wxT("SELECT COUNT("); sqlStmt += args; - sqlStmt += ") FROM "; + sqlStmt += wxT(") FROM "); sqlStmt += queryTableName; #if wxODBC_BACKWARD_COMPATABILITY if (from && wxStrlen(from)) @@ -1925,17 +2200,17 @@ ULONG wxDbTable::Count(const char *args) if (where.Length()) #endif { - sqlStmt += " WHERE "; + sqlStmt += wxT(" WHERE "); sqlStmt += where; } - pDb->WriteSqlLog(sqlStmt.c_str()); + pDb->WriteSqlLog(sqlStmt); // Initialize the Count cursor if it's not already initialized if (!hstmtCount) { hstmtCount = GetNewCursor(FALSE,FALSE); - assert(hstmtCount); + wxASSERT(hstmtCount); if (!hstmtCount) return(0); } @@ -1955,7 +2230,7 @@ ULONG wxDbTable::Count(const char *args) } // Obtain the result - if (SQLGetData(*hstmtCount, 1, SQL_C_ULONG, &l, sizeof(l), &cb) != SQL_SUCCESS) + if (SQLGetData(*hstmtCount, 1, SQL_C_ULONG, &count, sizeof(count), &cb) != SQL_SUCCESS) { pDb->DispAllErrors(henv, hdbc, *hstmtCount); return(0); @@ -1966,7 +2241,7 @@ ULONG wxDbTable::Count(const char *args) pDb->DispAllErrors(henv, hdbc, *hstmtCount); // Return the record count - return(l); + return(count); } // wxDbTable::Count() @@ -1989,22 +2264,23 @@ bool wxDbTable::Refresh(void) #endif // 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]; - wxStrcpy(whereClause, ""); + wxString whereClause; + whereClause.Empty(); + if (CanUpdByROWID()) { SDWORD cb; - char rowid[wxDB_ROWID_LEN+1]; + wxChar rowid[wxDB_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, (UCHAR*) rowid, wxDB_ROWID_LEN, &cb) == SQL_SUCCESS) { - wxStrcat(whereClause, queryTableName); - wxStrcat(whereClause, ".ROWID = '"); - wxStrcat(whereClause, rowid); - wxStrcat(whereClause, "'"); + whereClause += queryTableName; + whereClause += wxT(".ROWID = '"); + whereClause += rowid; + whereClause += wxT("'"); } } @@ -2014,7 +2290,7 @@ bool wxDbTable::Refresh(void) // Requery the record where = whereClause; - orderBy = ""; + orderBy.Empty(); if (!Query()) result = FALSE; @@ -2037,19 +2313,24 @@ bool wxDbTable::Refresh(void) } // wxDbTable::Refresh() -/********** wxDbTable::SetNull(int colNo) **********/ -bool wxDbTable::SetNull(int colNo) +/********** wxDbTable::SetColNull(int colNo, bool set) **********/ +bool wxDbTable::SetColNull(int colNo, bool set) { if (colNo < noCols) - return(colDefs[colNo].Null = TRUE); + { + colDefs[colNo].Null = set; + if (set) // Blank out the values in the member variable + ClearMemberVar(colNo,FALSE); // Must call with FALSE, or infinite recursion will happen + return(TRUE); + } else return(FALSE); -} // wxDbTable::SetNull(int colNo) +} // wxDbTable::SetColNull() -/********** wxDbTable::SetNull(char *colName) **********/ -bool wxDbTable::SetNull(const char *colName) +/********** wxDbTable::SetColNull(const wxString &colName, bool set) **********/ +bool wxDbTable::SetColNull(const wxString &colName, bool set) { int i; for (i = 0; i < noCols; i++) @@ -2059,18 +2340,23 @@ bool wxDbTable::SetNull(const char *colName) } if (i < noCols) - return(colDefs[i].Null = TRUE); + { + colDefs[i].Null = set; + if (set) // Blank out the values in the member variable + ClearMemberVar(i,FALSE); // Must call with FALSE, or infinite recursion will happen + return(TRUE); + } else return(FALSE); -} // wxDbTable::SetNull(char *colName) +} // wxDbTable::SetColNull() /********** wxDbTable::GetNewCursor() **********/ HSTMT *wxDbTable::GetNewCursor(bool setCursor, bool bindColumns) { HSTMT *newHSTMT = new HSTMT; - assert(newHSTMT); + wxASSERT(newHSTMT); if (!newHSTMT) return(0); @@ -2113,6 +2399,11 @@ bool wxDbTable::DeleteCursor(HSTMT *hstmtDel) if (!hstmtDel) // Cursor already deleted return(result); +/* +ODBC 3.0 says to use this form + if (SQLFreeHandle(*hstmtDel, SQL_DROP) != SQL_SUCCESS) + +*/ if (SQLFreeStmt(*hstmtDel, SQL_DROP) != SQL_SUCCESS) { pDb->DispAllErrors(henv, hdbc);