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

36
calc.go
View File

@ -198,7 +198,9 @@ var (
type calcContext struct { type calcContext struct {
sync.Mutex sync.Mutex
entry string entry string
maxCalcIterations uint
iterations map[string]uint iterations map[string]uint
iterationsCache map[string]formulaArg
} }
// cellRef defines the structure of a cell reference. // cellRef defines the structure of a cell reference.
@ -775,7 +777,9 @@ func (f *File) CalcCellValue(sheet, cell string, opts ...Options) (result string
) )
if token, err = f.calcCellValue(&calcContext{ if token, err = f.calcCellValue(&calcContext{
entry: fmt.Sprintf("%s!%s", sheet, cell), entry: fmt.Sprintf("%s!%s", sheet, cell),
maxCalcIterations: getOptions(opts...).MaxCalcIterations,
iterations: make(map[string]uint), iterations: make(map[string]uint),
iterationsCache: make(map[string]formulaArg),
}, sheet, cell); err != nil { }, sheet, cell); err != nil {
return return
} }
@ -1527,17 +1531,6 @@ func (f *File) cellResolver(ctx *calcContext, sheet, cell string) (formulaArg, e
value string value string
err error 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 { if value, err = f.GetCellValue(sheet, cell, Options{RawCellValue: true}); err != nil {
return arg, err return arg, err
} }
@ -1551,8 +1544,25 @@ func (f *File) cellResolver(ctx *calcContext, sheet, cell string) (formulaArg, e
return newEmptyFormulaArg(), err return newEmptyFormulaArg(), err
} }
return arg.ToNumber(), err return arg.ToNumber(), err
default: case CellTypeInlineString, CellTypeSharedString:
return arg, err 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 var count float64
for _, cell := range argsList.Front().Value.(formulaArg).ToList() { for _, cell := range argsList.Front().Value.(formulaArg).ToList() {
if cell.Value() == "" { if cell.Type == ArgEmpty {
count++ count++
} }
} }

View File

@ -1023,7 +1023,7 @@ func TestCalcCellValue(t *testing.T) {
"=COUNTBLANK(MUNIT(1))": "0", "=COUNTBLANK(MUNIT(1))": "0",
"=COUNTBLANK(1)": "0", "=COUNTBLANK(1)": "0",
"=COUNTBLANK(B1:C1)": "1", "=COUNTBLANK(B1:C1)": "1",
"=COUNTBLANK(C1)": "1", "=COUNTBLANK(C1)": "0",
// COUNTIF // COUNTIF
"=COUNTIF(D1:D9,\"Jan\")": "4", "=COUNTIF(D1:D9,\"Jan\")": "4",
"=COUNTIF(D1:D9,\"<>Jan\")": "5", "=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, 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)) 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)
}
}