From be9ccad6e934fa39230c4ace8ecacdd7380f911d Mon Sep 17 00:00:00 2001 From: Noah Treuhaft Date: Fri, 29 Sep 2023 14:37:01 -0400 Subject: [PATCH 1/2] Fix panic compiling record expression with duplicate field Closes #3365. --- compiler/kernel/expr.go | 4 ++++ compiler/ztests/record-dup-field.yaml | 3 +++ 2 files changed, 7 insertions(+) create mode 100644 compiler/ztests/record-dup-field.yaml diff --git a/compiler/kernel/expr.go b/compiler/kernel/expr.go index 80d5dae6d4..7acf7e28cb 100644 --- a/compiler/kernel/expr.go +++ b/compiler/kernel/expr.go @@ -13,6 +13,7 @@ import ( "github.com/brimdata/zed/runtime/op/traverse" "github.com/brimdata/zed/zbuf" "github.com/brimdata/zed/zson" + "golang.org/x/exp/slices" "golang.org/x/text/unicode/norm" ) @@ -397,6 +398,9 @@ func (b *Builder) compileRecordExpr(record *dag.RecordExpr) (expr.Evaluator, err for _, elem := range record.Elems { switch elem := elem.(type) { case *dag.Field: + if slices.ContainsFunc(elems, func(r expr.RecordElem) bool { return r.Name == elem.Name }) { + return nil, fmt.Errorf("record expression: %w", &zed.DuplicateFieldError{Name: elem.Name}) + } e, err := b.compileExpr(elem.Value) if err != nil { return nil, err diff --git a/compiler/ztests/record-dup-field.yaml b/compiler/ztests/record-dup-field.yaml new file mode 100644 index 0000000000..bd863558a0 --- /dev/null +++ b/compiler/ztests/record-dup-field.yaml @@ -0,0 +1,3 @@ +zed: yield {x:1,x:2} + +errorRE: 'record expression: duplicate field: "x"' From 7170e3e67aa120e70577fcdd0322acc746dfd4ba Mon Sep 17 00:00:00 2001 From: Noah Treuhaft Date: Fri, 29 Sep 2023 17:57:25 -0400 Subject: [PATCH 2/2] Move to semantic pass --- compiler/kernel/expr.go | 4 ---- compiler/semantic/expr.go | 9 +++++++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/compiler/kernel/expr.go b/compiler/kernel/expr.go index 7acf7e28cb..80d5dae6d4 100644 --- a/compiler/kernel/expr.go +++ b/compiler/kernel/expr.go @@ -13,7 +13,6 @@ import ( "github.com/brimdata/zed/runtime/op/traverse" "github.com/brimdata/zed/zbuf" "github.com/brimdata/zed/zson" - "golang.org/x/exp/slices" "golang.org/x/text/unicode/norm" ) @@ -398,9 +397,6 @@ func (b *Builder) compileRecordExpr(record *dag.RecordExpr) (expr.Evaluator, err for _, elem := range record.Elems { switch elem := elem.(type) { case *dag.Field: - if slices.ContainsFunc(elems, func(r expr.RecordElem) bool { return r.Name == elem.Name }) { - return nil, fmt.Errorf("record expression: %w", &zed.DuplicateFieldError{Name: elem.Name}) - } e, err := b.compileExpr(elem.Value) if err != nil { return nil, err diff --git a/compiler/semantic/expr.go b/compiler/semantic/expr.go index fd9ea551f0..68c21341eb 100644 --- a/compiler/semantic/expr.go +++ b/compiler/semantic/expr.go @@ -155,10 +155,15 @@ func (a *analyzer) semExpr(e ast.Expr) (dag.Expr, error) { Where: where, }, nil case *ast.RecordExpr: + fields := map[string]struct{}{} var out []dag.RecordElem for _, elem := range e.Elems { switch elem := elem.(type) { case *ast.Field: + if _, ok := fields[elem.Name]; ok { + return nil, fmt.Errorf("record expression: %w", &zed.DuplicateFieldError{Name: elem.Name}) + } + fields[elem.Name] = struct{}{} e, err := a.semExpr(elem.Value) if err != nil { return nil, err @@ -169,6 +174,10 @@ func (a *analyzer) semExpr(e ast.Expr) (dag.Expr, error) { Value: e, }) case *ast.ID: + if _, ok := fields[elem.Name]; ok { + return nil, fmt.Errorf("record expression: %w", &zed.DuplicateFieldError{Name: elem.Name}) + } + fields[elem.Name] = struct{}{} v, err := a.semID(elem) if err != nil { return nil, err