This closes #1469, fix cell resolver caused incorrect calculation result (#1470)

This commit is contained in:
jaby 2023-02-15 14:38:11 +01:00 committed by GitHub
parent 363fa940ac
commit ad90cea78b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 45 additions and 18 deletions

44
calc.go
View File

@ -197,8 +197,10 @@ var (
// calcContext defines the formula execution context.
type calcContext struct {
sync.Mutex
entry string
iterations map[string]uint
entry string
maxCalcIterations uint
iterations map[string]uint
iterationsCache map[string]formulaArg
}
// cellRef defines the structure of a cell reference.
@ -774,8 +776,10 @@ func (f *File) CalcCellValue(sheet, cell string, opts ...Options) (result string
token formulaArg
)
if token, err = f.calcCellValue(&calcContext{
entry: fmt.Sprintf("%s!%s", sheet, cell),
iterations: make(map[string]uint),
entry: fmt.Sprintf("%s!%s", sheet, cell),
maxCalcIterations: getOptions(opts...).MaxCalcIterations,
iterations: make(map[string]uint),
iterationsCache: make(map[string]formulaArg),
}, sheet, cell); err != nil {
return
}
@ -1527,17 +1531,6 @@ func (f *File) cellResolver(ctx *calcContext, sheet, cell string) (formulaArg, e
value string
err error
)
ref := fmt.Sprintf("%s!%s", sheet, cell)
if formula, _ := f.GetCellFormula(sheet, cell); len(formula) != 0 {
ctx.Lock()
if ctx.entry != ref && ctx.iterations[ref] <= f.options.MaxCalcIterations {
ctx.iterations[ref]++
ctx.Unlock()
arg, _ = f.calcCellValue(ctx, sheet, cell)
return arg, nil
}
ctx.Unlock()
}
if value, err = f.GetCellValue(sheet, cell, Options{RawCellValue: true}); err != nil {
return arg, err
}
@ -1551,8 +1544,25 @@ func (f *File) cellResolver(ctx *calcContext, sheet, cell string) (formulaArg, e
return newEmptyFormulaArg(), err
}
return arg.ToNumber(), err
default:
case CellTypeInlineString, CellTypeSharedString:
return arg, err
case CellTypeFormula:
ref := fmt.Sprintf("%s!%s", sheet, cell)
if ctx.entry != ref {
ctx.Lock()
if ctx.iterations[ref] <= ctx.maxCalcIterations {
ctx.iterations[ref]++
ctx.Unlock()
arg, _ = f.calcCellValue(ctx, sheet, cell)
ctx.iterationsCache[ref] = arg
return arg, nil
}
ctx.Unlock()
return ctx.iterationsCache[ref], nil
}
fallthrough
default:
return newEmptyFormulaArg(), err
}
}
@ -7746,7 +7756,7 @@ func (fn *formulaFuncs) COUNTBLANK(argsList *list.List) formulaArg {
}
var count float64
for _, cell := range argsList.Front().Value.(formulaArg).ToList() {
if cell.Value() == "" {
if cell.Type == ArgEmpty {
count++
}
}

View File

@ -1023,7 +1023,7 @@ func TestCalcCellValue(t *testing.T) {
"=COUNTBLANK(MUNIT(1))": "0",
"=COUNTBLANK(1)": "0",
"=COUNTBLANK(B1:C1)": "1",
"=COUNTBLANK(C1)": "1",
"=COUNTBLANK(C1)": "0",
// COUNTIF
"=COUNTIF(D1:D9,\"Jan\")": "4",
"=COUNTIF(D1:D9,\"<>Jan\")": "5",
@ -5871,3 +5871,20 @@ func TestCalcColRowQRDecomposition(t *testing.T) {
assert.False(t, calcRowQRDecomposition([][]float64{{0, 0}, {0, 0}}, []float64{0, 0}, 1, 0))
assert.False(t, calcColQRDecomposition([][]float64{{0, 0}, {0, 0}}, []float64{0, 0}, 1, 0))
}
func TestCalcCellResolver(t *testing.T) {
f := NewFile()
// Test reference a cell multiple times in a formula
assert.NoError(t, f.SetCellValue("Sheet1", "A1", "VALUE1"))
assert.NoError(t, f.SetCellFormula("Sheet1", "A2", "=A1"))
for formula, expected := range map[string]string{
"=CONCATENATE(A1,\"_\",A1)": "VALUE1_VALUE1",
"=CONCATENATE(A1,\"_\",A2)": "VALUE1_VALUE1",
"=CONCATENATE(A2,\"_\",A2)": "VALUE1_VALUE1",
} {
assert.NoError(t, f.SetCellFormula("Sheet1", "A3", formula))
result, err := f.CalcCellValue("Sheet1", "A3")
assert.NoError(t, err, formula)
assert.Equal(t, expected, result, formula)
}
}