diff --git a/pkg/deprecate/deprecate.go b/pkg/deprecate/deprecate.go index 21f73c77..9547a5f5 100644 --- a/pkg/deprecate/deprecate.go +++ b/pkg/deprecate/deprecate.go @@ -11,8 +11,33 @@ import ( "github.com/sirupsen/logrus" ) +type sourceContextCache struct { + sync.RWMutex + m map[string]struct{} +} + +func (d *sourceContextCache) encountered(scanner parser.Scanner) bool { + s := scanner.String() + d.RLock() + if _, has := d.m[s]; has { + defer d.RUnlock() + return true + } + d.RUnlock() + + d.Lock() + defer d.Unlock() + d.m[s] = struct{}{} + return false +} + +func newDeprecatorMap() *sourceContextCache { + return &sourceContextCache{m: make(map[string]struct{})} +} + // Deprecator contains information required to do deprecation checks and deprecation event trigger type Deprecator struct { + cache *sourceContextCache featureDesc string weakWarning, strongWarning, crash time.Time } @@ -77,12 +102,17 @@ func NewDeprecator(featureDesc, weakWarningDate, strongWarningDate, crashDate st weakWarning: weak, strongWarning: strong, crash: crash, + cache: newDeprecatorMap(), }, nil } // Deprecate does extracts build information from context once and does deprecation checks and trigger deprecation // events. func (d *Deprecator) Deprecate(ctx context.Context, scanner parser.Scanner) error { + if d.cache.encountered(scanner) { + return nil + } + buildDate, err := buildinfo.BuildDateFrom(ctx) if err != nil { return err diff --git a/pkg/deprecate/deprecate_test.go b/pkg/deprecate/deprecate_test.go index 99ccf8f6..39dcd661 100644 --- a/pkg/deprecate/deprecate_test.go +++ b/pkg/deprecate/deprecate_test.go @@ -22,7 +22,6 @@ func TestNewDeprecator(t *testing.T) { func TestDeprecate(t *testing.T) { // FIXME: use hook attached to a local logger instead of the global one hook := test.NewGlobal() - deprecator := MustNewDeprecator("feature", "2000-01-02", "2000-01-04", "2000-01-06") scanner := parser.NewScanner("expression") scannerStr := scanner.Context(parser.DefaultLimit) @@ -30,30 +29,36 @@ func TestDeprecate(t *testing.T) { return fmt.Sprintf("%s\n%s", msg, scannerStr) } + deprecator := MustNewDeprecator("feature", "2000-01-02", "2000-01-04", "2000-01-06") assert.NoError(t, deprecator.Deprecate(context.Background(), *scanner)) assert.Equal(t, errMsg("feature is being deprecated"), hook.LastEntry().Message) hook.Reset() + deprecator = MustNewDeprecator("feature", "2000-01-02", "2000-01-04", "2000-01-06") ctx := buildinfo.WithBuildData(context.Background(), buildinfo.BuildData{Date: "unspecified"}) assert.NoError(t, deprecator.Deprecate(ctx, *scanner)) assert.Equal(t, errMsg("feature is being deprecated"), hook.LastEntry().Message) hook.Reset() + deprecator = MustNewDeprecator("feature", "2000-01-02", "2000-01-04", "2000-01-06") ctx = buildinfo.WithBuildData(context.Background(), buildinfo.BuildData{Date: ""}) assert.NoError(t, deprecator.Deprecate(ctx, *scanner)) assert.Equal(t, errMsg("feature is being deprecated"), hook.LastEntry().Message) hook.Reset() + deprecator = MustNewDeprecator("feature", "2000-01-02", "2000-01-04", "2000-01-06") ctx = buildinfo.WithBuildData(context.Background(), buildinfo.BuildData{Date: "2000-01-01T00:00:00Z"}) assert.NoError(t, deprecator.Deprecate(ctx, *scanner)) assert.Equal(t, 0, len(hook.Entries)) hook.Reset() + deprecator = MustNewDeprecator("feature", "2000-01-02", "2000-01-04", "2000-01-06") ctx = buildinfo.WithBuildData(context.Background(), buildinfo.BuildData{Date: "2000-01-03T00:00:00Z"}) assert.NoError(t, deprecator.Deprecate(ctx, *scanner)) assert.Equal(t, errMsg("feature is being deprecated"), hook.LastEntry().Message) hook.Reset() + deprecator = MustNewDeprecator("feature", "2000-01-02", "2000-01-04", "2000-01-06") ctx = buildinfo.WithBuildData(context.Background(), buildinfo.BuildData{Date: "2000-01-05T00:00:00Z"}) assert.NoError(t, deprecator.Deprecate(ctx, *scanner)) assert.Equal(t, @@ -62,8 +67,19 @@ func TestDeprecate(t *testing.T) { ) hook.Reset() + deprecator = MustNewDeprecator("feature", "2000-01-02", "2000-01-04", "2000-01-06") ctx = buildinfo.WithBuildData(context.Background(), buildinfo.BuildData{Date: "2000-01-07T00:00:00Z"}) assert.EqualError(t, deprecator.Deprecate(ctx, *scanner), "feature is deprecated") assert.Equal(t, 0, len(hook.Entries)) hook.Reset() + + // test scanner storing + deprecator = MustNewDeprecator("feature", "2000-01-02", "2000-01-04", "2000-01-06") + assert.NoError(t, deprecator.Deprecate(context.Background(), *scanner)) + assert.Equal(t, errMsg("feature is being deprecated"), hook.LastEntry().Message) + assert.True(t, deprecator.cache.encountered(*scanner)) + hook.Reset() + assert.NoError(t, deprecator.Deprecate(context.Background(), *scanner)) + assert.Equal(t, 0, len(hook.Entries)) + hook.Reset() } diff --git a/rel/expr_offset.go b/rel/expr_offset.go index 828e9f05..e946d11a 100644 --- a/rel/expr_offset.go +++ b/rel/expr_offset.go @@ -41,6 +41,8 @@ func (o *OffsetExpr) Eval(ctx context.Context, local Scope) (_ Value, err error) return NewOffsetBytes(a.Bytes(), a.offset+int(offset.(Number))), nil case String: return NewOffsetString(a.s, a.offset+int(offset.(Number))), nil + case EmptySet: + return None, nil } return nil, WrapContextErr(errors.Errorf("offset not applicable to %s", ValueTypeAsString(array)), o, local) } diff --git a/rel/expr_offset_test.go b/rel/expr_offset_test.go index 7b9971cd..e57bc30e 100644 --- a/rel/expr_offset_test.go +++ b/rel/expr_offset_test.go @@ -136,6 +136,15 @@ func TestOffsetExprString(t *testing.T) { ) } +func TestOffsetExprEmptySet(t *testing.T) { + t.Parallel() + + AssertExprsEvalToSameValue(t, + None, + NewOffsetExpr(*parser.NewScanner(""), NewNumber(float64(-10)), None), + ) +} + func TestOffsetExprEvalFail(t *testing.T) { t.Parallel()