From bc9170f46d3f70adddc29c920387893a2c3a1102 Mon Sep 17 00:00:00 2001 From: Doug Davis Date: Mon, 22 May 2023 14:47:54 -0400 Subject: [PATCH] Short-circuit AND expressions (#899) Fixes: #898 Signed-off-by: Doug Davis --- sql/v2/expression/logic_expressions.go | 18 +++++++-- test/sql/go.mod | 19 +++++++++ test/sql/go.sum | 26 ++++++++++++ test/sql/shortcircuit_test.go | 55 ++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 test/sql/go.mod create mode 100644 test/sql/go.sum create mode 100644 test/sql/shortcircuit_test.go diff --git a/sql/v2/expression/logic_expressions.go b/sql/v2/expression/logic_expressions.go index 5c332a5f9..f75a0f9c4 100644 --- a/sql/v2/expression/logic_expressions.go +++ b/sql/v2/expression/logic_expressions.go @@ -13,7 +13,8 @@ import ( type logicExpression struct { baseBinaryExpression - fn func(x, y bool) bool + fn func(x, y bool) bool + verb string } func (s logicExpression) Evaluate(event cloudevents.Event) (interface{}, error) { @@ -22,12 +23,20 @@ func (s logicExpression) Evaluate(event cloudevents.Event) (interface{}, error) return nil, err } - rightVal, err := s.right.Evaluate(event) + leftVal, err = utils.Cast(leftVal, cesql.BooleanType) if err != nil { return nil, err } - leftVal, err = utils.Cast(leftVal, cesql.BooleanType) + // Don't bother to check the other expression unless we need to + if s.verb == "AND" && leftVal.(bool) == false { + return false, nil + } + if s.verb == "OR" && leftVal.(bool) == true { + return true, nil + } + + rightVal, err := s.right.Evaluate(event) if err != nil { return nil, err } @@ -49,6 +58,7 @@ func NewAndExpression(left cesql.Expression, right cesql.Expression) cesql.Expre fn: func(x, y bool) bool { return x && y }, + verb: "AND", } } @@ -61,6 +71,7 @@ func NewOrExpression(left cesql.Expression, right cesql.Expression) cesql.Expres fn: func(x, y bool) bool { return x || y }, + verb: "OR", } } @@ -73,5 +84,6 @@ func NewXorExpression(left cesql.Expression, right cesql.Expression) cesql.Expre fn: func(x, y bool) bool { return x != y }, + verb: "XOR", } } diff --git a/test/sql/go.mod b/test/sql/go.mod new file mode 100644 index 000000000..40568ed75 --- /dev/null +++ b/test/sql/go.mod @@ -0,0 +1,19 @@ +module github.com/cloudevents/sdk-go/test/sql + +go 1.20 + +require ( + github.com/cloudevents/sdk-go/sql/v2 v2.14.0 + github.com/cloudevents/sdk-go/v2 v2.14.0 +) + +require ( + github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect + github.com/google/uuid v1.1.1 // indirect + github.com/json-iterator/go v1.1.10 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect + go.uber.org/atomic v1.4.0 // indirect + go.uber.org/multierr v1.1.0 // indirect + go.uber.org/zap v1.10.0 // indirect +) diff --git a/test/sql/go.sum b/test/sql/go.sum new file mode 100644 index 000000000..c66119be9 --- /dev/null +++ b/test/sql/go.sum @@ -0,0 +1,26 @@ +github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves= +github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= +github.com/cloudevents/sdk-go/sql/v2 v2.14.0 h1:OPi78/DQqGxLQ1Ktg0XMMW+IxJHiJNhVUARXnkaYnh8= +github.com/cloudevents/sdk-go/sql/v2 v2.14.0/go.mod h1:Fp5OvNlqfYIpj3C/RiHx/6TjqZK89Ed706uyBN1u+aE= +github.com/cloudevents/sdk-go/v2 v2.14.0 h1:Nrob4FwVgi5L4tV9lhjzZcjYqFVyJzsA56CwPaPfv6s= +github.com/cloudevents/sdk-go/v2 v2.14.0/go.mod h1:xDmKfzNjM8gBvjaF8ijFjM1VYOVUEeUfapHMUX1T5To= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= diff --git a/test/sql/shortcircuit_test.go b/test/sql/shortcircuit_test.go new file mode 100644 index 000000000..99eb1ffc4 --- /dev/null +++ b/test/sql/shortcircuit_test.go @@ -0,0 +1,55 @@ +/* + Copyright 2021 The CloudEvents Authors + SPDX-License-Identifier: Apache-2.0 +*/ + +package sql + +import ( + "testing" + + cesql "github.com/cloudevents/sdk-go/sql/v2/parser" + cloudevents "github.com/cloudevents/sdk-go/v2" +) + +func TestShortCircuitAND(t *testing.T) { + + sql := "(EXISTS revisiontype AND revisiontype=Branch) OR (branch='master')" + + expression, err := cesql.Parse(string(sql)) + evt := cloudevents.NewEvent("1.0") + evt.SetID("evt-1") + evt.SetType("what-ever") + evt.SetSource("/event") + evt.SetExtension("branch", "master") + val, err := expression.Evaluate(evt) + + if err != nil { + t.Errorf("err should be nil: %s", err.Error()) + } else { + if !val.(bool) { + t.Errorf("should be true ,but :%s", val) + } + } +} + +func TestShortCircuitOR(t *testing.T) { + + sql := "(branch='master' OR revisiontype=Branch)" + + expression, err := cesql.Parse(string(sql)) + evt := cloudevents.NewEvent("1.0") + evt.SetID("evt-1") + evt.SetType("what-ever") + evt.SetSource("/event") + evt.SetExtension("branch", "master") + val, err := expression.Evaluate(evt) + + if err != nil { + t.Errorf("err should be nil: %s", err.Error()) + } else { + if !val.(bool) { + t.Errorf("should be true ,but :%s", val) + } + } +}