Skip to content

Commit d5bd71e

Browse files
committed
feat(avro): add alias support for field and record name matching
The Avro compatibility checker now considers aliases when matching fields and record names, per the Avro specification: - Reader field aliases are checked against writer field names - Writer field aliases are checked against reader field names - Record-level aliases checked for name matching This enables backward-compatible field renaming using aliases. 1298 BDD scenarios passing, 0 failures. 7 @pending-impl remaining (all Protobuf import resolution).
1 parent 1f3a6c4 commit d5bd71e

File tree

3 files changed

+47
-7
lines changed

3 files changed

+47
-7
lines changed

internal/compatibility/avro/checker.go

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,25 +102,30 @@ func (c *Checker) checkSchemas(reader, writer avro.Schema, path string) *compati
102102
func (c *Checker) checkRecord(reader, writer *avro.RecordSchema, path string) *compatibility.Result {
103103
result := compatibility.NewCompatibleResult()
104104

105-
// Check that names match
106-
if reader.FullName() != writer.FullName() {
105+
// Check that names match (considering aliases)
106+
if !c.recordNamesMatch(reader, writer) {
107107
result.AddMessage("%s: record name mismatch: reader has %s, writer has %s",
108108
pathOrRoot(path), reader.FullName(), writer.FullName())
109109
return result
110110
}
111111

112-
// Build a map of writer fields
112+
// Build maps of writer fields by name and aliases
113113
writerFields := make(map[string]*avro.Field)
114114
for _, f := range writer.Fields() {
115115
writerFields[f.Name()] = f
116+
for _, alias := range f.Aliases() {
117+
writerFields[alias] = f
118+
}
116119
}
117120

118121
// Check each reader field
119122
for _, rf := range reader.Fields() {
120123
fieldPath := appendPath(path, rf.Name())
121-
wf, exists := writerFields[rf.Name()]
122124

123-
if !exists {
125+
// Try to find matching writer field by name or reader's aliases
126+
wf := c.findWriterField(rf, writerFields)
127+
128+
if wf == nil {
124129
// Field doesn't exist in writer - reader must have a default
125130
if !rf.HasDefault() {
126131
result.AddMessage("%s: reader field '%s' has no default and is missing from writer",
@@ -137,6 +142,43 @@ func (c *Checker) checkRecord(reader, writer *avro.RecordSchema, path string) *c
137142
return result
138143
}
139144

145+
// recordNamesMatch checks if reader and writer record names match,
146+
// considering aliases on both sides per the Avro specification.
147+
func (c *Checker) recordNamesMatch(reader, writer *avro.RecordSchema) bool {
148+
if reader.FullName() == writer.FullName() {
149+
return true
150+
}
151+
// Check if reader name matches any writer alias
152+
for _, alias := range writer.Aliases() {
153+
if reader.FullName() == alias {
154+
return true
155+
}
156+
}
157+
// Check if writer name matches any reader alias
158+
for _, alias := range reader.Aliases() {
159+
if writer.FullName() == alias {
160+
return true
161+
}
162+
}
163+
return false
164+
}
165+
166+
// findWriterField finds a matching writer field for a reader field,
167+
// checking by name and aliases per the Avro specification.
168+
func (c *Checker) findWriterField(readerField *avro.Field, writerFields map[string]*avro.Field) *avro.Field {
169+
// Direct name match (also matches writer field aliases via the map)
170+
if wf, exists := writerFields[readerField.Name()]; exists {
171+
return wf
172+
}
173+
// Check reader field aliases against writer field names/aliases
174+
for _, alias := range readerField.Aliases() {
175+
if wf, exists := writerFields[alias]; exists {
176+
return wf
177+
}
178+
}
179+
return nil
180+
}
181+
140182
// checkEnum checks compatibility between two enum schemas.
141183
func (c *Checker) checkEnum(reader, writer *avro.EnumSchema, path string) *compatibility.Result {
142184
result := compatibility.NewCompatibleResult()

tests/bdd/features/avro_compatibility_exhaustive.feature

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ Feature: Avro Compatibility — Exhaustive (Confluent v8.1.1 Compatibility)
4646
"""
4747
Then the response status should be 200
4848

49-
@pending-impl
5049
Scenario: Backward — changing field name with alias is compatible
5150
Given the global compatibility level is "NONE"
5251
And subject "avro-ex-back-alias" has compatibility level "BACKWARD"

tests/bdd/features/schema_parsing_exhaustive.feature

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ Feature: Schema Parsing & Validation — Exhaustive (Confluent v8.1.1 Compatibil
9090
# AVRO COMPATIBILITY — FIELD NAME ALIAS
9191
# ==========================================================================
9292

93-
@pending-impl
9493
Scenario: Backward compatible — field rename with alias
9594
Given the global compatibility level is "NONE"
9695
And subject "parse-avro-alias" has compatibility level "BACKWARD"

0 commit comments

Comments
 (0)