Skip to content

Commit

Permalink
Fix record and fixed alias evolution test
Browse files Browse the repository at this point in the history
  • Loading branch information
actgardner committed Nov 6, 2020
1 parent 45eb690 commit 6dc3217
Show file tree
Hide file tree
Showing 66 changed files with 685 additions and 174 deletions.
10 changes: 5 additions & 5 deletions v8/compiler/method.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func (p *irMethod) compileType(writer, reader schema.AvroType) error {
if _, ok := writer.(*schema.UnionField); !ok {
if readerUnion, ok := reader.(*schema.UnionField); ok {
for readerIndex, r := range readerUnion.AvroTypes() {
if writer.IsReadableBy(r, make(map[schema.QualifiedName]interface{})) {
if writer.IsReadableBy(r) {
p.addLiteral(vm.SetLong, readerIndex)
p.addLiteral(vm.Set, vm.Long)
p.addLiteral(vm.Enter, readerIndex)
Expand Down Expand Up @@ -160,7 +160,7 @@ func (p *irMethod) compileType(writer, reader schema.AvroType) error {

func (p *irMethod) compileRef(writer, reader *schema.Reference) error {
log("compileRef()\n writer:\n %v\n---\nreader: %v\n---\n", writer, reader)
if !p.program.allowLaxNames && reader != nil && writer.TypeName.Name != reader.TypeName.Name {
if !p.program.allowLaxNames && reader != nil && !writer.IsReadableBy(reader) {
return fmt.Errorf("Incompatible types by name: %v %v", reader, writer)
}

Expand Down Expand Up @@ -263,7 +263,7 @@ func (p *irMethod) compileRecord(writer, reader *schema.RecordDefinition) error
readerField = reader.FieldByName(field.Name())
if readerField != nil {
delete(defaultFields, readerField.Name())
if !field.Type().IsReadableBy(readerField.Type(), make(map[schema.QualifiedName]interface{})) {
if !field.Type().IsReadableBy(readerField.Type()) {
return fmt.Errorf("Incompatible schemas: field %v in reader has incompatible type in writer", field.Name())
}
readerType = readerField.Type()
Expand Down Expand Up @@ -346,7 +346,7 @@ writer:
}

for readerIndex, r := range unionReader.AvroTypes() {
if t.IsReadableBy(r, make(map[schema.QualifiedName]interface{})) {
if t.IsReadableBy(r) {
log("Union types are readable: %q %q", r.Name(), t.Name())
p.addSwitchCase(switchId, i, readerIndex)
if _, ok := t.(*schema.NullField); ok {
Expand All @@ -367,7 +367,7 @@ writer:
p.addSwitchCase(switchId, i, -1)
typedErrId := p.addError(fmt.Sprintf("Reader schema has no field for type %v in union", t.Name()))
p.addLiteral(vm.Halt, typedErrId)
} else if t.IsReadableBy(reader, make(map[schema.QualifiedName]interface{})) {
} else if t.IsReadableBy(reader) {
// If the reader is not a union but it can read this union field, support it
if _, ok := t.(*schema.NullField); ok {
p.addLiteral(vm.SetExitNull, vm.NoopField)
Expand Down
14 changes: 14 additions & 0 deletions v8/generator/flat/templates/union.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,20 @@ func Deserialize{{ .Name }}(r io.Reader) ({{ .GoType }}, error) {
return t, err
}
func Deserialize{{ .Name }}FromSchema(r io.Reader, schema string) ({{ .GoType }}, error) {
t := {{ .ConstructorMethod }}
deser, err := compiler.CompileSchemaBytes([]byte(schema), []byte(t.Schema()))
if err != nil {
return nil, err
}
err = vm.Eval(r, deser, t)
if err != nil {
return nil, err
}
return t, err
}
func (r {{ .GoType }}) Schema() string {
return {{ printf "%q" .Schema }}
}
Expand Down
6 changes: 3 additions & 3 deletions v8/schema/array.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,17 @@ func (s *ArrayField) WrapperType() string {
return fmt.Sprintf("%vWrapper", s.Name())
}

func (s *ArrayField) IsReadableBy(f AvroType, visited map[QualifiedName]interface{}) bool {
func (s *ArrayField) IsReadableBy(f AvroType) bool {
if union, ok := f.(*UnionField); ok {
for _, t := range union.AvroTypes() {
if s.IsReadableBy(t, visited) {
if s.IsReadableBy(t) {
return true
}
}
}

if reader, ok := f.(*ArrayField); ok {
return s.ItemType().IsReadableBy(reader.ItemType(), visited)
return s.ItemType().IsReadableBy(reader.ItemType())
}
return false
}
Expand Down
2 changes: 1 addition & 1 deletion v8/schema/avro_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ type AvroType interface {
DefaultValue(lvalue string, rvalue interface{}) (string, error)

WrapperType() string
IsReadableBy(f AvroType, visited map[QualifiedName]interface{}) bool
IsReadableBy(f AvroType) bool
}
4 changes: 2 additions & 2 deletions v8/schema/bool.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ func (s *BoolField) WrapperType() string {
return "types.Boolean"
}

func (s *BoolField) IsReadableBy(f AvroType, visited map[QualifiedName]interface{}) bool {
func (s *BoolField) IsReadableBy(f AvroType) bool {
if union, ok := f.(*UnionField); ok {
for _, t := range union.AvroTypes() {
if s.IsReadableBy(t, visited) {
if s.IsReadableBy(t) {
return true
}
}
Expand Down
10 changes: 7 additions & 3 deletions v8/schema/bytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package schema

import (
"fmt"

"github.com/actgardner/gogen-avro/v8/util"
)

type BytesField struct {
Expand All @@ -23,17 +25,19 @@ func (s *BytesField) DefaultValue(lvalue string, rvalue interface{}) (string, er
return "", fmt.Errorf("Expected string as default for field %v, got %q", lvalue, rvalue)
}

return fmt.Sprintf("%v = []byte(%q)", lvalue, rvalue), nil
b := util.DecodeByteString(rvalue.(string))

return fmt.Sprintf("%v = []byte(%q)", lvalue, b), nil
}

func (s *BytesField) WrapperType() string {
return "BytesWrapper"
}

func (s *BytesField) IsReadableBy(f AvroType, visited map[QualifiedName]interface{}) bool {
func (s *BytesField) IsReadableBy(f AvroType) bool {
if union, ok := f.(*UnionField); ok {
for _, t := range union.AvroTypes() {
if s.IsReadableBy(t, visited) {
if s.IsReadableBy(t) {
return true
}
}
Expand Down
14 changes: 13 additions & 1 deletion v8/schema/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,22 @@ type Definition interface {
Definition(scope map[QualifiedName]interface{}) (interface{}, error)
DefaultValue(lvalue string, rvalue interface{}) (string, error)

IsReadableBy(f Definition, visited map[QualifiedName]interface{}) bool
IsReadableBy(f Definition) bool
WrapperType() string
}

func hasMatchingName(writerName QualifiedName, reader Definition) bool {
if writerName.Name == reader.AvroName().Name {
return true
}
for _, name := range reader.Aliases() {
if name.Name == writerName.Name {
return true
}
}
return false
}

func copyDefinition(x map[string]interface{}) map[string]interface{} {
if x == nil {
return x
Expand Down
4 changes: 2 additions & 2 deletions v8/schema/double.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ func (s *DoubleField) WrapperType() string {
return "types.Double"
}

func (s *DoubleField) IsReadableBy(f AvroType, visited map[QualifiedName]interface{}) bool {
func (s *DoubleField) IsReadableBy(f AvroType) bool {
if union, ok := f.(*UnionField); ok {
for _, t := range union.AvroTypes() {
if s.IsReadableBy(t, visited) {
if s.IsReadableBy(t) {
return true
}
}
Expand Down
6 changes: 3 additions & 3 deletions v8/schema/enum.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ func (s *EnumDefinition) DefaultValue(lvalue string, rvalue interface{}) (string
return fmt.Sprintf("%v = %v", lvalue, generator.ToPublicName(s.GoType()+strings.Title(rvalue.(string)))), nil
}

func (s *EnumDefinition) IsReadableBy(d Definition, visited map[QualifiedName]interface{}) bool {
otherEnum, ok := d.(*EnumDefinition)
return ok && otherEnum.name == s.name
func (s *EnumDefinition) IsReadableBy(d Definition) bool {
_, ok := d.(*EnumDefinition)
return ok && hasMatchingName(s.AvroName(), d)
}

func (s *EnumDefinition) WrapperType() string {
Expand Down
3 changes: 1 addition & 2 deletions v8/schema/evolution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (

"github.com/actgardner/gogen-avro/v8/parser"
"github.com/actgardner/gogen-avro/v8/resolver"
"github.com/actgardner/gogen-avro/v8/schema"

"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -74,6 +73,6 @@ func TestIsReadableBy(t *testing.T) {
for _, def := range ns2.Roots {
assert.Nil(t, resolver.ResolveDefinition(def, ns2.Definitions))
}
assert.Equal(t, c.isReadable, writer.IsReadableBy(reader, make(map[schema.QualifiedName]interface{})))
assert.Equal(t, c.isReadable, writer.IsReadableBy(reader))
}
}
2 changes: 1 addition & 1 deletion v8/schema/file_root.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (f *FileRoot) DefaultValue(lvalue string, rvalue interface{}) (string, erro
return "", nil
}

func (f *FileRoot) IsReadableBy(_ Definition, _ map[QualifiedName]interface{}) bool {
func (f *FileRoot) IsReadableBy(_ Definition) bool {
return false
}

Expand Down
4 changes: 2 additions & 2 deletions v8/schema/fixed.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ func (s *FixedDefinition) DefaultValue(lvalue string, rvalue interface{}) (strin
return fmt.Sprintf("copy(%v[:], []byte(%q))", lvalue, rvalue), nil
}

func (s *FixedDefinition) IsReadableBy(d Definition, visited map[QualifiedName]interface{}) bool {
func (s *FixedDefinition) IsReadableBy(d Definition) bool {
if fixed, ok := d.(*FixedDefinition); ok {
return fixed.sizeBytes == s.sizeBytes && fixed.name == s.name
return fixed.sizeBytes == s.sizeBytes && hasMatchingName(s.name, d)
}
return false
}
Expand Down
4 changes: 2 additions & 2 deletions v8/schema/float.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ func (s *FloatField) WrapperType() string {
return "types.Float"
}

func (s *FloatField) IsReadableBy(f AvroType, visited map[QualifiedName]interface{}) bool {
func (s *FloatField) IsReadableBy(f AvroType) bool {
if union, ok := f.(*UnionField); ok {
for _, t := range union.AvroTypes() {
if s.IsReadableBy(t, visited) {
if s.IsReadableBy(t) {
return true
}
}
Expand Down
4 changes: 2 additions & 2 deletions v8/schema/int.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ func (s *IntField) WrapperType() string {
return "types.Int"
}

func (s *IntField) IsReadableBy(f AvroType, visited map[QualifiedName]interface{}) bool {
func (s *IntField) IsReadableBy(f AvroType) bool {
if union, ok := f.(*UnionField); ok {
for _, t := range union.AvroTypes() {
if s.IsReadableBy(t, visited) {
if s.IsReadableBy(t) {
return true
}
}
Expand Down
4 changes: 2 additions & 2 deletions v8/schema/long.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ func (s *LongField) WrapperType() string {
return "types.Long"
}

func (s *LongField) IsReadableBy(f AvroType, visited map[QualifiedName]interface{}) bool {
func (s *LongField) IsReadableBy(f AvroType) bool {
if union, ok := f.(*UnionField); ok {
for _, t := range union.AvroTypes() {
if s.IsReadableBy(t, visited) {
if s.IsReadableBy(t) {
return true
}
}
Expand Down
6 changes: 3 additions & 3 deletions v8/schema/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,16 @@ func (s *MapField) WrapperType() string {
return fmt.Sprintf("%vWrapper", s.Name())
}

func (s *MapField) IsReadableBy(f AvroType, visited map[QualifiedName]interface{}) bool {
func (s *MapField) IsReadableBy(f AvroType) bool {
if union, ok := f.(*UnionField); ok {
for _, t := range union.AvroTypes() {
if s.IsReadableBy(t, visited) {
if s.IsReadableBy(t) {
return true
}
}
}
if reader, ok := f.(*MapField); ok {
return s.ItemType().IsReadableBy(reader.ItemType(), visited)
return s.ItemType().IsReadableBy(reader.ItemType())
}
return false
}
Expand Down
4 changes: 2 additions & 2 deletions v8/schema/null.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ func (s *NullField) WrapperType() string {
return ""
}

func (s *NullField) IsReadableBy(f AvroType, visited map[QualifiedName]interface{}) bool {
func (s *NullField) IsReadableBy(f AvroType) bool {
if union, ok := f.(*UnionField); ok {
for _, t := range union.AvroTypes() {
if s.IsReadableBy(t, visited) {
if s.IsReadableBy(t) {
return true
}
}
Expand Down
14 changes: 3 additions & 11 deletions v8/schema/record.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,17 +124,9 @@ func (r *RecordDefinition) Fields() []*Field {
return r.fields
}

func (s *RecordDefinition) IsReadableBy(d Definition, visited map[QualifiedName]interface{}) bool {
// If there's a circular reference, don't evaluate every field on the second pass
if _, ok := visited[s.name]; ok {
return true
}
reader, ok := d.(*RecordDefinition)
if !ok {
return false
}

return reader.AvroName().Name == s.AvroName().Name
func (s *RecordDefinition) IsReadableBy(d Definition) bool {
_, ok := d.(*RecordDefinition)
return ok && hasMatchingName(s.AvroName(), d)
}

func (s *RecordDefinition) WrapperType() string {
Expand Down
6 changes: 3 additions & 3 deletions v8/schema/reference.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,16 @@ func (s *Reference) WrapperType() string {
return s.Def.WrapperType()
}

func (s *Reference) IsReadableBy(f AvroType, visited map[QualifiedName]interface{}) bool {
func (s *Reference) IsReadableBy(f AvroType) bool {
if union, ok := f.(*UnionField); ok {
for _, t := range union.AvroTypes() {
if s.IsReadableBy(t, visited) {
if s.IsReadableBy(t) {
return true
}
}
}
if reader, ok := f.(*Reference); ok {
return s.Def.IsReadableBy(reader.Def, visited)
return s.Def.IsReadableBy(reader.Def)
}
return false
}
Expand Down
4 changes: 2 additions & 2 deletions v8/schema/string.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ func (s *StringField) WrapperType() string {
return "types.String"
}

func (s *StringField) IsReadableBy(f AvroType, visited map[QualifiedName]interface{}) bool {
func (s *StringField) IsReadableBy(f AvroType) bool {
if union, ok := f.(*UnionField); ok {
for _, t := range union.AvroTypes() {
if s.IsReadableBy(t, visited) {
if s.IsReadableBy(t) {
return true
}
}
Expand Down
6 changes: 3 additions & 3 deletions v8/schema/union.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,17 +125,17 @@ func (s *UnionField) WrapperType() string {
return ""
}

func (s *UnionField) IsReadableBy(f AvroType, visited map[QualifiedName]interface{}) bool {
func (s *UnionField) IsReadableBy(f AvroType) bool {
// Report if *any* writer type could be deserialized by the reader
for _, t := range s.AvroTypes() {
if readerUnion, ok := f.(*UnionField); ok {
for _, rt := range readerUnion.AvroTypes() {
if t.IsReadableBy(rt, visited) {
if t.IsReadableBy(rt) {
return true
}
}
} else {
if t.IsReadableBy(f, visited) {
if t.IsReadableBy(f) {
return true
}
}
Expand Down
4 changes: 3 additions & 1 deletion v8/test/alias-field/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ func TestEvolution(t *testing.T) {
test.RoundTripEvolution(t,
func() container.AvroRecord { return NewAliasRecord() },
func() container.AvroRecord { return evolution.NewAliasRecord() },
func(r io.Reader) (container.AvroRecord, error) { return evolution.DeserializeAliasRecord(r) })
func(r io.Reader, schema string) (container.AvroRecord, error) {
return evolution.DeserializeAliasRecordFromSchema(r, schema)
})
}
Loading

0 comments on commit 6dc3217

Please sign in to comment.