]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/dbtable.cpp
fixed bug in parsing filenames without paths, added more/better tests
[wxWidgets.git] / src / common / dbtable.cpp
index 2e248698b673761ce6abaa5bca22e2fb8e3cf245..de3b913141d9b31410423e56bb3a4fc47cc2d205 100644 (file)
@@ -125,12 +125,17 @@ wxDbTable::wxDbTable(wxDb *pwxDb, const char *tblName, const int nCols,
     selectForUpdate     = FALSE;                    // SELECT ... FOR UPDATE; Indicates whether to include the FOR UPDATE phrase
     queryOnly           = qryOnly;
     insertable          = TRUE;
     selectForUpdate     = FALSE;                    // SELECT ... FOR UPDATE; Indicates whether to include the FOR UPDATE phrase
     queryOnly           = qryOnly;
     insertable          = TRUE;
+    wxStrcpy(tablePath,"");
+    wxStrcpy(tableName,"");
+    wxStrcpy(queryTableName,"");
 
     assert (tblName);
 
     wxStrcpy(tableName, tblName);               // Table Name
     if (tblPath)
         wxStrcpy(tablePath, tblPath);           // Table Path - used for dBase files
 
     assert (tblName);
 
     wxStrcpy(tableName, tblName);               // Table Name
     if (tblPath)
         wxStrcpy(tablePath, tblPath);           // Table Path - used for dBase files
+    else
+        tablePath[0] = 0;
     
     if (qryTblName)                             // Name of the table/view to query
         wxStrcpy(queryTableName, qryTblName);
     
     if (qryTblName)                             // Name of the table/view to query
         wxStrcpy(queryTableName, qryTblName);
@@ -363,13 +368,19 @@ bool wxDbTable::bindInsertParams(void)
                 fSqlType = pDb->GetTypeInfVarchar().FsqlType;      
                 precision = colDefs[i].SzDataObj;
                 scale = 0;
                 fSqlType = pDb->GetTypeInfVarchar().FsqlType;      
                 precision = colDefs[i].SzDataObj;
                 scale = 0;
-                colDefs[i].CbValue = SQL_NTS;
+                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;
                 break;
             case DB_DATA_TYPE_INTEGER:
                 fSqlType = pDb->GetTypeInfInteger().FsqlType;
                 precision = pDb->GetTypeInfInteger().Precision;
                 scale = 0;
-                colDefs[i].CbValue = 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;
                 break;
             case DB_DATA_TYPE_FLOAT:
                 fSqlType = pDb->GetTypeInfFloat().FsqlType;
@@ -380,21 +391,28 @@ bool wxDbTable::bindInsertParams(void)
                 // I check for this here and set the scale = precision.
                 //if (scale < 0)
                 //  scale = (short) precision;
                 // I check for this here and set the scale = precision.
                 //if (scale < 0)
                 //  scale = (short) precision;
-                colDefs[i].CbValue = 0;
+                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;
                 break;
             case DB_DATA_TYPE_DATE:
                 fSqlType = pDb->GetTypeInfDate().FsqlType;
                 precision = pDb->GetTypeInfDate().Precision;
                 scale = 0;
-                colDefs[i].CbValue = 0;
+                if (colDefs[i].Null)
+                    colDefs[i].CbValue = SQL_NULL_DATA;
+                else
+                    colDefs[i].CbValue = 0;
                 break;
         }
         // Null values
                 break;
         }
         // Null values
-        if (colDefs[i].Null)
-        {
-            colDefs[i].CbValue = SQL_NULL_DATA;
-            colDefs[i].Null = FALSE;
-        }
+//RG-NULL
+//RG-NULL        if (colDefs[i].Null)
+//RG-NULL        {
+//RG-NULL            colDefs[i].CbValue = SQL_NULL_DATA;
+//RG-NULL            colDefs[i].Null = FALSE;
+//RG-NULL        }
 
         if (SQLBindParameter(hstmtInsert, colNo++, SQL_PARAM_INPUT, colDefs[i].SqlCtype,
                              fSqlType, precision, scale, (UCHAR*) colDefs[i].PtrDataObj, 
 
         if (SQLBindParameter(hstmtInsert, colNo++, SQL_PARAM_INPUT, colDefs[i].SqlCtype,
                              fSqlType, precision, scale, (UCHAR*) colDefs[i].PtrDataObj, 
@@ -433,13 +451,19 @@ bool wxDbTable::bindUpdateParams(void)
                 fSqlType = pDb->GetTypeInfVarchar().FsqlType;
                 precision = colDefs[i].SzDataObj;
                 scale = 0;
                 fSqlType = pDb->GetTypeInfVarchar().FsqlType;
                 precision = colDefs[i].SzDataObj;
                 scale = 0;
-                colDefs[i].CbValue = SQL_NTS;
+                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;
                 break;
             case DB_DATA_TYPE_INTEGER:
                 fSqlType = pDb->GetTypeInfInteger().FsqlType;
                 precision = pDb->GetTypeInfInteger().Precision;
                 scale = 0;
-                colDefs[i].CbValue = 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;
                 break;
             case DB_DATA_TYPE_FLOAT:
                 fSqlType = pDb->GetTypeInfFloat().FsqlType;
@@ -450,13 +474,19 @@ bool wxDbTable::bindUpdateParams(void)
                 // I check for this here and set the scale = precision.
                 //if (scale < 0)
                 // scale = (short) precision;
                 // I check for this here and set the scale = precision.
                 //if (scale < 0)
                 // scale = (short) precision;
-                colDefs[i].CbValue = 0;
+                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;
                 break;
             case DB_DATA_TYPE_DATE:
                 fSqlType = pDb->GetTypeInfDate().FsqlType;
                 precision = pDb->GetTypeInfDate().Precision;
                 scale = 0;
-                colDefs[i].CbValue = 0;
+                if (colDefs[i].Null)
+                    colDefs[i].CbValue = SQL_NULL_DATA;
+                else
+                    colDefs[i].CbValue = 0;
                 break;
         }
         
                 break;
         }
         
@@ -477,14 +507,15 @@ bool wxDbTable::bindUpdateParams(void)
 /********** wxDbTable::bindCols() **********/
 bool wxDbTable::bindCols(HSTMT cursor)
 {
 /********** 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,
     
     // 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)     
+//RG-NULL                       colDefs[i].SzDataObj, &cb) != SQL_SUCCESS)     
+                       colDefs[i].SzDataObj, &colDefs[i].CbValue ) != SQL_SUCCESS)
         {
           return (pDb->DispAllErrors(henv, hdbc, cursor));
         }
         {
           return (pDb->DispAllErrors(henv, hdbc, cursor));
         }
@@ -515,6 +546,14 @@ bool wxDbTable::getRec(UWORD fetchType)
             else
                 return(pDb->DispAllErrors(henv, hdbc, hstmt));
         }
             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
     {
     }
     else
     {
@@ -527,6 +566,14 @@ bool wxDbTable::getRec(UWORD fetchType)
             else
                 return(pDb->DispAllErrors(henv, hdbc, hstmt));
         }
             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
     }
 
     // Completed successfully
@@ -566,7 +613,6 @@ bool wxDbTable::query(int queryType, bool forUpdate, bool distinct, const char *
 {
     char sqlStmt[DB_MAX_STATEMENT_LEN];
 
 {
     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();
     if (forUpdate)
         // The user may wish to select for update, but the DBMS may not be capable
         selectForUpdate = CanSelectForUpdate();
@@ -579,6 +625,10 @@ bool wxDbTable::query(int queryType, bool forUpdate, bool distinct, const char *
         BuildSelectStmt(sqlStmt, queryType, distinct);
         pDb->WriteSqlLog(sqlStmt);
     }
         BuildSelectStmt(sqlStmt, queryType, distinct);
         pDb->WriteSqlLog(sqlStmt);
     }
+/*
+   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);
 
     else 
         wxStrcpy(sqlStmt, pSqlStmt);
 
@@ -590,9 +640,9 @@ bool wxDbTable::query(int queryType, bool forUpdate, bool distinct, const char *
         pDb->DispAllErrors(henv, hdbc, hstmt);
         return(FALSE);
     }
         pDb->DispAllErrors(henv, hdbc, hstmt);
         return(FALSE);
     }
-
+*/
     // Make sure the cursor is closed first
     // Make sure the cursor is closed first
-    if (! CloseCursor(hstmt))
+    if (!CloseCursor(hstmt))
         return(FALSE);
 
     // Execute the SQL SELECT statement
         return(FALSE);
 
     // Execute the SQL SELECT statement
@@ -611,27 +661,50 @@ bool wxDbTable::query(int queryType, bool forUpdate, bool distinct, const char *
 
 
 /********** wxDbTable::Open() **********/
 
 
 /********** wxDbTable::Open() **********/
-bool wxDbTable::Open(void)
+bool wxDbTable::Open(bool checkPrivileges)
 {
     if (!pDb)
         return FALSE;   
 
     int i;
     wxString sqlStmt;
 {
     if (!pDb)
         return FALSE;   
 
     int i;
     wxString sqlStmt;
+    wxString s;
 
 
+    s = "";
     // Verify that the table exists in the database
     // 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 = "Table/view does not exist in the database";
+        if ( *(pDb->dbInf.accessibleTables) == 'Y')
+            s += ", or you have no permissions.\n";
         else
         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 += ".\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,"SELECT",NULL,pDb->GetUsername(),tablePath))
+            s = "Current logged in user does not have sufficient privileges to access this table.\n";
+    }
+
+    if (!s.IsEmpty())
+    {
+        wxString p;
+
+        if (wxStrcmp(tablePath,""))
+            p.sprintf("Error opening '%s/%s'.\n",tablePath,tableName);
         else
         else
-            s += wxT("Current logged in user does not have sufficient privileges to access this table.\n");
-        pDb->LogError(s.c_str());
+            p.sprintf("Error opening '%s'.\n", tableName);
+
+        p += s;
+        pDb->LogError(p.GetData());
+
         return(FALSE);
     }
 
         return(FALSE);
     }
 
@@ -651,8 +724,8 @@ bool wxDbTable::Open(void)
     
     if (!bindCols(hstmtInternal))                   // Internal use only
         return(FALSE);
     
     if (!bindCols(hstmtInternal))                   // Internal use only
         return(FALSE);
-    
-    /*
+
+     /*
      * Do NOT bind the hstmtCount cursor!!!
      */
 
      * Do NOT bind the hstmtCount cursor!!!
      */
 
@@ -1034,7 +1107,10 @@ bool wxDbTable::CreateTable(bool attemptDrop)
             sqlStmt += s.c_str();
         }
 
             sqlStmt += s.c_str();
         }
 
-        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)
             {
         {
             if (colDefs[i].KeyField)
             {
@@ -1319,7 +1395,8 @@ bool wxDbTable::DropIndex(const char * idxName)
 
     if (pDb->Dbms() == dbmsACCESS || pDb->Dbms() == dbmsMY_SQL)
         sqlStmt.sprintf("DROP INDEX %s ON %s",idxName,tableName);
 
     if (pDb->Dbms() == dbmsACCESS || pDb->Dbms() == dbmsMY_SQL)
         sqlStmt.sprintf("DROP INDEX %s ON %s",idxName,tableName);
-    else if (pDb->Dbms() == dbmsSYBASE_ASE)
+    else if ((pDb->Dbms() == dbmsMS_SQL_SERVER) ||
+             (pDb->Dbms() == dbmsSYBASE_ASE))
         sqlStmt.sprintf("DROP INDEX %s.%s",tableName,idxName);
     else
         sqlStmt.sprintf("DROP INDEX %s",idxName);
         sqlStmt.sprintf("DROP INDEX %s.%s",tableName,idxName);
     else
         sqlStmt.sprintf("DROP INDEX %s",idxName);
@@ -1364,6 +1441,42 @@ bool wxDbTable::DropIndex(const char * idxName)
 }  // wxDbTable::DropIndex()
 
 
 }  // 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 += ",";
+
+        tempStr += colDefs[colNo].ColName;
+        colNo = va_arg (argptr, int);
+    }
+    va_end (argptr);              /* Reset variable arguments.      */
+
+    SetOrderByClause(tempStr.c_str());
+
+    return (!abort);
+}  // wxDbTable::SetOrderByColNums()
+
+
 /********** wxDbTable::Insert() **********/
 int wxDbTable::Insert(void)
 {
 /********** wxDbTable::Insert() **********/
 int wxDbTable::Insert(void)
 {
@@ -1661,7 +1774,7 @@ void wxDbTable::BuildWhereClause(char *pWhereClause, int typeOfWhere,
     {
         // Determine if this column should be included in the WHERE clause
         if ((typeOfWhere == DB_WHERE_KEYFIELDS && colDefs[i].KeyField) ||
     {
         // 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))))
+             (typeOfWhere == DB_WHERE_MATCHING  && (!IsColNull(i))))
         {
             // Skip over timestamp columns
             if (colDefs[i].SqlCtype == SQL_C_TIMESTAMP)
         {
             // Skip over timestamp columns
             if (colDefs[i].SqlCtype == SQL_C_TIMESTAMP)
@@ -1715,6 +1828,10 @@ void wxDbTable::BuildWhereClause(char *pWhereClause, int typeOfWhere,
 /********** wxDbTable::IsColNull() **********/
 bool wxDbTable::IsColNull(int colNo)
 {
 /********** 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:
     switch(colDefs[colNo].SqlCtype)
     {
         case SQL_C_CHAR:
@@ -1741,16 +1858,22 @@ bool wxDbTable::IsColNull(int colNo)
         default:
             return(TRUE);
     }
         default:
             return(TRUE);
     }
+*/
+    return (colDefs[colNo].Null);
 }  // wxDbTable::IsColNull()
 
 
 /********** wxDbTable::CanSelectForUpdate() **********/
 bool wxDbTable::CanSelectForUpdate(void)
 {
 }  // wxDbTable::IsColNull()
 
 
 /********** wxDbTable::CanSelectForUpdate() **********/
 bool wxDbTable::CanSelectForUpdate(void)
 {
+    if (queryOnly)
+        return FALSE;
+
     if (pDb->Dbms() == dbmsMY_SQL)
         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);
         return(TRUE);
     else
         return(FALSE);
@@ -1786,50 +1909,62 @@ bool wxDbTable::IsCursorClosedOnCommit(void)
 }  // wxDbTable::IsCursorClosedOnCommit()
 
 
 }  // 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++)
+    assert(colNo < noCols);
+
+    switch(colDefs[colNo].SqlCtype)
     {
     {
-        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;
-        }
+        case SQL_C_CHAR:
+            ((UCHAR FAR *) colDefs[colNo].PtrDataObj)[0]    = 0;
+            break;
+        case SQL_C_SSHORT:
+            *((SWORD *) colDefs[colNo].PtrDataObj)          = 0;
+            break;
+        case SQL_C_USHORT:
+            *((UWORD*) colDefs[colNo].PtrDataObj)           = 0;
+            break;
+        case SQL_C_SLONG:
+            *((SDWORD *) colDefs[colNo].PtrDataObj)         = 0;
+            break;
+        case SQL_C_ULONG:
+            *((UDWORD *) colDefs[colNo].PtrDataObj)         = 0;
+            break;
+        case SQL_C_FLOAT:
+            *((SFLOAT *) colDefs[colNo].PtrDataObj)         = 0.0f;
+            break;
+        case SQL_C_DOUBLE:
+            *((SDOUBLE *) colDefs[colNo].PtrDataObj)        = 0.0f;
+            break;
+        case SQL_C_TIMESTAMP:
+            TIMESTAMP_STRUCT *pDt;
+            pDt = (TIMESTAMP_STRUCT *) colDefs[colNo].PtrDataObj;
+            pDt->year = 0;
+            pDt->month = 0;
+            pDt->day = 0;
+            pDt->hour = 0;
+            pDt->minute = 0;
+            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()
 
 
 }  // wxDbTable::ClearMemberVars()
 
 
@@ -1852,9 +1987,9 @@ bool wxDbTable::SetQueryTimeout(UDWORD nSeconds)
 
 
 /********** wxDbTable::SetColDefs() **********/
 
 
 /********** 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 char *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 (!colDefs)  // May happen if the database connection fails
         return;
@@ -1890,8 +2025,8 @@ void wxDbTable::SetColDefs (int index, const char *fieldName, int dataType, void
 }  // wxDbTable::SetColDefs()
 
 
 }  // wxDbTable::SetColDefs()
 
 
-/********** wxDbTable::SetColDef() **********/
-wxDbColDataPtr* wxDbTable::SetColDefs (wxDbColInf *pColInfs, ULONG numCols)
+/********** wxDbTable::SetColDefs() **********/
+wxDbColDataPtr* wxDbTable::SetColDefs(wxDbColInf *pColInfs, ULONG numCols)
 {
     assert(pColInfs);
     wxDbColDataPtr *pColDataPtrs = NULL;
 {
     assert(pColInfs);
     wxDbColDataPtr *pColDataPtrs = NULL;
@@ -1954,7 +2089,7 @@ wxDbColDataPtr* wxDbTable::SetColDefs (wxDbColInf *pColInfs, ULONG numCols)
 
     return (pColDataPtrs);
 
 
     return (pColDataPtrs);
 
-} // wxDbTable::SetColDef()
+} // wxDbTable::SetColDefs()
 
 
 /********** wxDbTable::SetCursor() **********/
 
 
 /********** wxDbTable::SetCursor() **********/
@@ -2106,19 +2241,24 @@ bool wxDbTable::Refresh(void)
 }  // wxDbTable::Refresh()
 
 
 }  // 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)
 {
     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);
 
     else
         return(FALSE);
 
-}  // wxDbTable::SetNull(int colNo)
+}  // wxDbTable::SetColNull(int colNo)
 
 
 
 
-/********** wxDbTable::SetNull(char *colName) **********/
-bool wxDbTable::SetNull(const char *colName)
+/********** wxDbTable::SetColNull(char *colName, bool set) **********/
+bool wxDbTable::SetColNull(const char *colName, bool set)
 {
     int i;
     for (i = 0; i < noCols; i++)
 {
     int i;
     for (i = 0; i < noCols; i++)
@@ -2128,11 +2268,16 @@ bool wxDbTable::SetNull(const char *colName)
     }
 
     if (i < noCols)
     }
 
     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);
 
     else
         return(FALSE);
 
-}  // wxDbTable::SetNull(char *colName)
+}  // wxDbTable::SetColNull(char *colName)
 
 
 /********** wxDbTable::GetNewCursor() **********/
 
 
 /********** wxDbTable::GetNewCursor() **********/