Skip to content

Commit

Permalink
fix: data validation is unsafe when used in concurrent condition(qax-…
Browse files Browse the repository at this point in the history
  • Loading branch information
chenwang committed Feb 27, 2024
1 parent 688808b commit 90781ad
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 20 deletions.
50 changes: 30 additions & 20 deletions datavalidation.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,31 @@ func NewDataValidation(allowBlank bool) *DataValidation {
}
}

// newXlsxDataValidation is a internal function to create xlsxDataValidation by given data validation object.
func newXlsxDataValidation(dv *DataValidation) *xlsxDataValidation {
dataValidation := &xlsxDataValidation{
AllowBlank: dv.AllowBlank,
Error: dv.Error,
ErrorStyle: dv.ErrorStyle,
ErrorTitle: dv.ErrorTitle,
Operator: dv.Operator,
Prompt: dv.Prompt,
PromptTitle: dv.PromptTitle,
ShowDropDown: dv.ShowDropDown,
ShowErrorMessage: dv.ShowErrorMessage,
ShowInputMessage: dv.ShowInputMessage,
Sqref: dv.Sqref,
Type: dv.Type,
}
if dv.Formula1 != "" {
dataValidation.Formula1 = &xlsxInnerXML{Content: dv.Formula1}
}
if dv.Formula2 != "" {
dataValidation.Formula2 = &xlsxInnerXML{Content: dv.Formula2}
}
return dataValidation
}

// SetError set error notice.
func (dv *DataValidation) SetError(style DataValidationErrorStyle, title, msg string) {
dv.Error = &msg
Expand Down Expand Up @@ -256,29 +281,12 @@ func (f *File) AddDataValidation(sheet string, dv *DataValidation) error {
if err != nil {
return err
}
f.mu.Lock()
defer f.mu.Unlock()
if nil == ws.DataValidations {
ws.DataValidations = new(xlsxDataValidations)
}
dataValidation := &xlsxDataValidation{
AllowBlank: dv.AllowBlank,
Error: dv.Error,
ErrorStyle: dv.ErrorStyle,
ErrorTitle: dv.ErrorTitle,
Operator: dv.Operator,
Prompt: dv.Prompt,
PromptTitle: dv.PromptTitle,
ShowDropDown: dv.ShowDropDown,
ShowErrorMessage: dv.ShowErrorMessage,
ShowInputMessage: dv.ShowInputMessage,
Sqref: dv.Sqref,
Type: dv.Type,
}
if dv.Formula1 != "" {
dataValidation.Formula1 = &xlsxInnerXML{Content: dv.Formula1}
}
if dv.Formula2 != "" {
dataValidation.Formula2 = &xlsxInnerXML{Content: dv.Formula2}
}
dataValidation := newXlsxDataValidation(dv)
ws.DataValidations.DataValidation = append(ws.DataValidations.DataValidation, dataValidation)
ws.DataValidations.Count = len(ws.DataValidations.DataValidation)
return err
Expand Down Expand Up @@ -330,6 +338,8 @@ func (f *File) DeleteDataValidation(sheet string, sqref ...string) error {
if err != nil {
return err
}
f.mu.Lock()
defer f.mu.Unlock()
if ws.DataValidations == nil {
return nil
}
Expand Down
38 changes: 38 additions & 0 deletions datavalidation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
package excelize

import (
"fmt"
"math"
"path/filepath"
"strconv"
"strings"
"sync"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -106,6 +109,41 @@ func TestDataValidation(t *testing.T) {
assert.Equal(t, []*DataValidation(nil), dataValidations)
}

func TestConcurrentAddDataValidation(t *testing.T) {
var (
resultFile = filepath.Join("test", "TestConcurrentAddDataValidation.xlsx")
f = NewFile()
sheet1 = "Sheet1"
dataValidationLen = 1000
)

// data validation list
dvs := make([]*DataValidation, dataValidationLen)
for i := 0; i < dataValidationLen; i++ {
dvi := NewDataValidation(true)
dvi.Sqref = fmt.Sprintf("A%d:B%d", i+1, i+1)
dvi.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorGreaterThan)
dvi.SetInput(fmt.Sprintf("title:%d", i+1), strconv.Itoa(i+1))
dvs[i] = dvi
}
assert.Len(t, dvs, dataValidationLen)
// simulated concurrency
var wg sync.WaitGroup
wg.Add(dataValidationLen)
for i := 0; i < dataValidationLen; i++ {
go func(i int) {
f.AddDataValidation(sheet1, dvs[i])
wg.Done()
}(i)
}
wg.Wait()
// Test the length of data validation after concurrent
dataValidations, err := f.GetDataValidations(sheet1)
assert.NoError(t, err)
assert.Len(t, dataValidations, dataValidationLen)
assert.NoError(t, f.SaveAs(resultFile))
}

func TestDataValidationError(t *testing.T) {
resultFile := filepath.Join("test", "TestDataValidationError.xlsx")

Expand Down

0 comments on commit 90781ad

Please sign in to comment.