diff --git a/examples/basic_test.go b/examples/basic_test.go index 18a3115..d7a3374 100644 --- a/examples/basic_test.go +++ b/examples/basic_test.go @@ -29,6 +29,20 @@ func ExampleNewConverter() { // []interface {}{"2020-01-01T00:00:00Z", "John"} } +func ExampleNewConverter_emptyfilter() { + converter := filter.NewConverter(filter.WithEmptyCondition("TRUE")) // The default is FALSE if you don't change it. + + mongoFilterQuery := `{}` + conditions, _, err := converter.Convert([]byte(mongoFilterQuery), 1) + if err != nil { + // handle error + } + + fmt.Println(conditions) + // Output: + // TRUE +} + func ExampleNewConverter_nonIsolatedConditions() { converter := filter.NewConverter() diff --git a/filter/converter.go b/filter/converter.go index 2f207f5..08ebf4f 100644 --- a/filter/converter.go +++ b/filter/converter.go @@ -26,13 +26,16 @@ type Converter struct { driver.Valuer sql.Scanner } + emptyCondition string } // NewConverter creates a new Converter with optional nested JSONB field mapping. // // Note: When using github.com/lib/pq, the filter.WithArrayDriver should be set to pq.Array. func NewConverter(options ...Option) *Converter { - converter := &Converter{} + converter := &Converter{ + emptyCondition: "FALSE", + } for _, option := range options { if option != nil { option(converter) @@ -56,6 +59,10 @@ func (c *Converter) Convert(query []byte, startAtParameterIndex int) (conditions return "", nil, err } + if len(mongoFilter) == 0 { + return c.emptyCondition, []any{}, nil + } + conditions, values, err = c.convertFilter(mongoFilter, startAtParameterIndex) if err != nil { return "", nil, err @@ -68,6 +75,10 @@ func (c *Converter) convertFilter(filter map[string]any, paramIndex int) (string var conditions []string var values []any + if len(filter) == 0 { + return "", nil, fmt.Errorf("empty objects not allowed") + } + keys := []string{} for key := range filter { keys = append(keys, key) diff --git a/filter/converter_test.go b/filter/converter_test.go index 9fb0311..8777060 100644 --- a/filter/converter_test.go +++ b/filter/converter_test.go @@ -193,6 +193,21 @@ func TestConverter_Convert(t *testing.T) { []any{[]any{}}, nil, }, + { + "empty filter", + nil, + `{}`, + `FALSE`, + []any{}, + nil, + }, { + "empty or conditions", + nil, + `{"$or": [{}, {}]}`, + ``, + nil, + fmt.Errorf("empty objects not allowed"), + }, } for _, tt := range tests { @@ -245,3 +260,17 @@ func TestConverter_Convert_startAtParameterIndex(t *testing.T) { t.Errorf("Converter.Convert(..., 1234551231231231231) error = %v, want nil", err) } } + +func TestConverter_WithEmptyCondition(t *testing.T) { + c := filter.NewConverter(filter.WithEmptyCondition("TRUE")) + conditions, values, err := c.Convert([]byte(`{}`), 1) + if err != nil { + t.Fatal(err) + } + if want := "TRUE"; conditions != want { + t.Errorf("Converter.Convert() conditions = %v, want %v", conditions, want) + } + if len(values) != 0 { + t.Errorf("Converter.Convert() values = %v, want nil", values) + } +} diff --git a/filter/options.go b/filter/options.go index 399cb9a..769b004 100644 --- a/filter/options.go +++ b/filter/options.go @@ -39,3 +39,13 @@ func WithArrayDriver(f func(a any) interface { c.arrayDriver = f } } + +// WithEmptyCondition is an option to specify the condition to be used when the +// input query filter is empty. (e.g. you have a query with no conditions) +// +// The default value is `FALSE`, because it's the safer choice in most cases. +func WithEmptyCondition(condition string) Option { + return func(c *Converter) { + c.emptyCondition = condition + } +} diff --git a/fuzz/testdata/fuzz/FuzzConverter/439ca8de24f74c60 b/fuzz/testdata/fuzz/FuzzConverter/439ca8de24f74c60 new file mode 100644 index 0000000..405b42d --- /dev/null +++ b/fuzz/testdata/fuzz/FuzzConverter/439ca8de24f74c60 @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"$or\":[{},{}]}") diff --git a/integration/postgres_test.go b/integration/postgres_test.go index 94c1038..0a56768 100644 --- a/integration/postgres_test.go +++ b/integration/postgres_test.go @@ -291,6 +291,12 @@ func TestIntegration_BasicOperators(t *testing.T) { nil, errors.New("pq: invalid input syntax for type integer: \"town1\""), }, + { + `empty object`, + `{}`, // Should return FALSE as the condition. + []int{}, + nil, + }, } for _, tt := range tests {