Here is my problem. I have a modified version of Five DBU ( set for just DBF files ) that works well except for the Replace capability.
I build a dialog for creating the replace command using:
- Code: Select all Expand view
METHOD ReplaceData( ) CLASS MLSEditor
LOCAL aOpsType := { "=", "<", "<=", "<>", ">=", ">" }
LOCAL cOldValue := SPACE(50 ), cNewValue := SPACE(50)
LOCAL uOldValue := SPACE(10), uNewValue := SPACE(10)
LOCAL cSearchField := SPACE(10), cReplaceField := SPACE(10), cOpsType := "="
LOCAL lAllRecords := .f., lDoProcess := .f.
LOCAL nReplaceField := 1 // , aType, aNames, nFlds := 1
LOCAL cOldType, nField2, cNewType, oRRec, oCbx1, oCbx2, oCbx3, oEdit2, oEdit1
MEMVAR oBrush, oMFont
// Create the DIALOG
DEFINE DIALOG oRRec RESOURCE "FEREPLACE" BRUSH oBrush TRANSPARENT TITLE "Record Data Replacement Selection" FONT oMFont
// Do we replace all records
REDEFINE CHECKBOX lAllRecords ID 601 OF oRRec MESSAGE "Check if you want the value changed in all records"
// Select the field
REDEFINE COMBOBOX oCbx1 VAR cSearchField ITEMS ::aNames ID 602 OF oRRec ;
STYLE CBS_DROPDOWN MESSAGE "Select the field for selecting records"
// Next select the operator
REDEFINE COMBOBOX oCbx2 VAR cOpsType ITEMS aOpsType ID 603 OF oRRec ;
STYLE CBS_DROPDOWN MESSAGE "Select the comparison operator"
// Then get the old value
REDEFINE GET oEdit1 VAR cOldValue ID 604 OF oRRec MESSAGE "Enter the current value to match"
// The replace field
REDEFINE COMBOBOX oCbx3 VAR cReplaceField ITEMS ::aNames ID 605 OF oRRec ;
STYLE CBS_DROPDOWN MESSAGE "Select the field to update"
// And the new value
REDEFINE GET oEdit2 VAR cNewValue ID 606 OF oRRec MESSAGE "Enter the new field value"
REDEFINE BTNBMP RESOURCE "HROK" PROMPT "Process Replace" ID 610 of oRRec NOBORDER TRANSPARENT ;
ACTION ( lDoProcess := .t., oRRec:end() )
REDEFINE BTNBMP RESOURCE "HREXIT" PROMPT "Exit Replace" ID 611 of oRRec NOBORDER TRANSPARENT ;
ACTION oRRec:end()
ACTIVATE DIALOG oRRec ON INIT ( oRRec:center(wndmain()) )
IF lDoProcess
// We want the field number of the replacement field
cReplaceField := TRIM( cReplaceField)
nReplaceField := Ascan( ::aNames, cReplaceField )
// We want the type of data of the selected field
cNewType := ::aTypes[ nReplaceField ]
// Make sure the new value matches the type of the field and is defined as vNewValue
IF cNewType = "C"
uNewValue := cNewValue
ELSEIF cNewType = "N"
uNewValue := VAL( cNewValue )
ELSEIF cNewType = "L"
uNewValue := IIF( cNewValue = "T", .t., .f. )
ELSEIF cNewType = "D"
uNewValue := CTOD( cNewValue )
ELSE
uNewValue := cNewValue
ENDIF
IF ! lAllRecords
// vOldValue must be of the right type to do the search
cSearchField := TRIM( cSearchField )
nField2 := Ascan( ::aNames, cSearchField )
// We want the type of data of the selected field
cOldType := ::aTypes[ nField2 ]
// Be sure the data type is converted to the field type
// Make sure the new value matches the type of the field and is defined as vNewValue
IF cOldType = "C"
uOldValue := cOldValue
ELSEIF cOldType = "N"
uOldValue := VAL( cOldValue )
ELSEIF cOldType = "L"
uOldValue := IIF( cOldValue = "T", .t., .f. )
ELSEIF cOldType = "D"
uOldValue := CTOD( cOldValue )
ELSE
uOldValue := cOldValue
ENDIF
ENDIF
// If desired, execute the replace
IF MsgNoYes( "Do you wish to do this replacement ?" )
::oDbf:ReplaceFor( nReplaceField, uNewValue, cSearchField, uOldValue, cOpsType, lAllRecords )
MsgInfo( "Replace complete" )
ENDIF
::oDbf:gotop( )
ENDIF // Do process
RETURN NIL
Toward the bottom you will see ::oDbfr:ReplaceFor( ) as a called method. Here is it's code:
- Code: Select all Expand view
/*
Purpose: Do a REPLACE FOR for certain records or all records.
nReplaceField - Number of field that you want to replace with uNewValue
uNewValue - Value to replace with
cSearchField - Name of search field. Could be the same as nReplaceField or not
nOldValue - Value to look for to trigger replacement operation
cOpsType - Mathmatical value to use ("=", "<", "<=", etc.)
lAllRecords - Set to .T. to replace all records. Only need nReplaceField and uNewValue when using lAllRecords = .T.
I.E. You can't specify a condition.
*/
Method ReplaceFor( nReplaceField, uNewValue, cSearchField, uOldValue, cOpsType, lAllRecords ) CLASS TData
Local bFor, cOldType, lSuccess:=.F., nOldRecNo
Default lAllRecords:=.F.
cSearchField:= upper(cSearchField)
nOldRecNo:= ::recno()
::GoTop( )
::Lock( )
IF lAllRecords
(::cAlias)->(dbeval( { || FieldPut( nReplaceField, uNewValue ) },,,,, .T. ) )
else
cOldType:=valtype( uOldValue)
do case
case ( cOldType = "C" .or. cOldType = "M" ) // Character or Memo type
bFor := compile( "_Field->" + upper(cSearchField) + cOpsType + '"' + TRIM(uOldValue) + '"' )
case cOldType = "D" // Date type
bFor := compile( "DTOS(_Field->" + cSearchField +") " + cOpsType + '"' + DTOS(uOldValue)+'"' )
case cOldType = "L" // Logical
bFor:= compile( "_Field->"+ upper(cSearchField) + cOpsType + cValToChar(uOldValue ) )
case cOldType = "N" // Numeric
bFor:= compile( "_Field->"+ upper(cSearchField) + cOpsType + cValToChar(uOldValue ) )
otherwise
bFor := ""
MsgAlert( cOldType + " Value Not Supported" )
endcase
(::cAlias)->(dbeval( { || FieldPut( nReplaceField, uNewValue ) }, bFor,,,, .T. ) )
lSuccess:= .T.
ENDIF
::Unlock( )
::goto(nOldRecNo)
Return lSuccess
//-----------------------------------------------------------------------------//
STATIC FUNCTION Compile( cExp1 )
// MsgInfo( cExp1, "Inside Compile() Function" ) // For debugging only
Return &( "{||" + cExp1 + "}" )
//-----------------------------------------------------------------------------//
This works fine for everything except numerics. When the field in the database is numeric, then I do not get a match, and thus no replacement. FOR ALL OTHER DATA TYPES this process works fine.
I did not write the original function ( an extension of tDatabase ). I need to find a way to be able to do a replace for the numeric fields. I've spent a long time trying different things. Every other option generates an error. This does not generate the error but it does not change the value in the field as desired.
Your observations would be appreciated.
Tim