You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by mg...@apache.org on 2024/02/22 13:16:37 UTC

(avro) branch main updated: AVRO-2397: [c++] Add support for type and field aliases (#2270)

This is an automated email from the ASF dual-hosted git repository.

mgrigorov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/avro.git


The following commit(s) were added to refs/heads/main by this push:
     new a4624984b AVRO-2397: [c++] Add support for type and field aliases (#2270)
a4624984b is described below

commit a4624984bf699446952b6ab5dbc8e1d90c6bed0f
Author: Morgan Parry <mp...@users.noreply.github.com>
AuthorDate: Thu Feb 22 13:16:32 2024 +0000

    AVRO-2397: [c++] Add support for type and field aliases (#2270)
    
    * Add support for type and field aliases
    
    * AVRO-2397: Improve code style consistency
    
    ---------
    
    Co-authored-by: Martin Grigorov <ma...@users.noreply.github.com>
---
 lang/c++/api/Node.hh                      | 22 +++++---
 lang/c++/api/NodeImpl.hh                  | 48 +++++------------
 lang/c++/impl/Compiler.cc                 | 46 +++++++++++-----
 lang/c++/impl/Node.cc                     | 62 +++++++++++++++++++++-
 lang/c++/impl/NodeImpl.cc                 | 87 +++++++++++++++++++++++++------
 lang/c++/impl/parsing/ResolvingDecoder.cc | 79 +++++++++++-----------------
 lang/c++/test/CodecTests.cc               | 40 ++++++++++++++
 lang/c++/test/unittest.cc                 |  3 +-
 8 files changed, 264 insertions(+), 123 deletions(-)

diff --git a/lang/c++/api/Node.hh b/lang/c++/api/Node.hh
index 3f5fe5b05..0b8f2269c 100644
--- a/lang/c++/api/Node.hh
+++ b/lang/c++/api/Node.hh
@@ -40,30 +40,38 @@ class GenericDatum;
 using NodePtr = std::shared_ptr<Node>;
 
 class AVRO_DECL Name {
+    struct Aliases;
+
     std::string ns_;
     std::string simpleName_;
+    std::unique_ptr<Aliases> aliases_;
 
 public:
-    Name() = default;
-    explicit Name(const std::string &fullname);
-    Name(std::string simpleName, std::string ns) : ns_(std::move(ns)), simpleName_(std::move(simpleName)) { check(); }
+    Name();
+    explicit Name(const std::string &name);
+    Name(std::string simpleName, std::string ns);
+    Name(const Name& other);
+    Name& operator=(const Name& other);
+    Name(Name&& other);
+    Name& operator=(Name&& other);
+    ~Name();
 
     std::string fullname() const;
     const std::string &ns() const { return ns_; }
     const std::string &simpleName() const { return simpleName_; }
+    const std::vector<std::string> &aliases() const;
 
     void ns(std::string n) { ns_ = std::move(n); }
     void simpleName(std::string n) { simpleName_ = std::move(n); }
     void fullname(const std::string &n);
+    void addAlias(const std::string &alias);
 
     bool operator<(const Name &n) const;
     void check() const;
     bool operator==(const Name &n) const;
     bool operator!=(const Name &n) const { return !((*this) == n); }
-    void clear() {
-        ns_.clear();
-        simpleName_.clear();
-    }
+    bool equalOrAliasedBy(const Name &n) const;
+    void clear();
     explicit operator std::string() const {
         return fullname();
     }
diff --git a/lang/c++/api/NodeImpl.hh b/lang/c++/api/NodeImpl.hh
index ef1c1dac9..8372a3887 100644
--- a/lang/c++/api/NodeImpl.hh
+++ b/lang/c++/api/NodeImpl.hh
@@ -294,42 +294,30 @@ protected:
 };
 
 class AVRO_DECL NodeRecord : public NodeImplRecord {
-    std::vector<GenericDatum> defaultValues;
+    std::vector<std::vector<std::string>> fieldsAliases_;
+    std::vector<GenericDatum> fieldsDefaultValues_;
 
 public:
     NodeRecord() : NodeImplRecord(AVRO_RECORD) {}
+
     NodeRecord(const HasName &name, const MultiLeaves &fields,
-               const LeafNames &fieldsNames,
-               std::vector<GenericDatum> dv);
+               const LeafNames &fieldsNames, std::vector<GenericDatum> dv);
 
     NodeRecord(const HasName &name, const HasDoc &doc, const MultiLeaves &fields,
-               const LeafNames &fieldsNames,
-               std::vector<GenericDatum> dv) : NodeImplRecord(AVRO_RECORD, name, doc, fields, fieldsNames, MultiAttributes(), NoSize()),
-                                               defaultValues(std::move(dv)) {
-        leafNameCheck();
-    }
+               const LeafNames &fieldsNames, std::vector<GenericDatum> dv);
 
     NodeRecord(const HasName &name, const MultiLeaves &fields,
-               const LeafNames &fieldsNames,
-               const std::vector<GenericDatum>& dv,
-               const MultiAttributes &customAttributes) :
-        NodeImplRecord(AVRO_RECORD, name, fields, fieldsNames, customAttributes, NoSize()),
-        defaultValues(dv) {
-        leafNameCheck();
-    }
+               const LeafNames &fieldsNames, std::vector<std::vector<std::string>> fieldsAliases,
+               std::vector<GenericDatum> dv, const MultiAttributes &customAttributes);
 
     NodeRecord(const HasName &name, const HasDoc &doc, const MultiLeaves &fields,
-               const LeafNames &fieldsNames,
-               const std::vector<GenericDatum>& dv,
-               const MultiAttributes &customAttributes) :
-        NodeImplRecord(AVRO_RECORD, name, doc, fields, fieldsNames, customAttributes, NoSize()),
-        defaultValues(dv) {
-        leafNameCheck();
-    }
+               const LeafNames &fieldsNames, std::vector<std::vector<std::string>> fieldsAliases,
+               std::vector<GenericDatum> dv, const MultiAttributes &customAttributes);
 
     void swap(NodeRecord &r) {
         NodeImplRecord::swap(r);
-        defaultValues.swap(r.defaultValues);
+        fieldsAliases_.swap(r.fieldsAliases_);
+        fieldsDefaultValues_.swap(r.fieldsDefaultValues_);
     }
 
     SchemaResolution resolve(const Node &reader) const override;
@@ -344,22 +332,10 @@ public:
     }
 
     const GenericDatum &defaultValueAt(size_t index) override {
-        return defaultValues[index];
+        return fieldsDefaultValues_[index];
     }
 
     void printDefaultToJson(const GenericDatum &g, std::ostream &os, size_t depth) const override;
-
-private:
-    // check if leaf name is valid Name and is not duplicate
-    void leafNameCheck() {
-        for (size_t i = 0; i < leafNameAttributes_.size(); ++i) {
-            if (!nameIndex_.add(leafNameAttributes_.get(i), i)) {
-                throw Exception(boost::format(
-                                    "Cannot add duplicate field: %1%")
-                                % leafNameAttributes_.get(i));
-            }
-        }
-    }
 };
 
 class AVRO_DECL NodeEnum : public NodeImplEnum {
diff --git a/lang/c++/impl/Compiler.cc b/lang/c++/impl/Compiler.cc
index 383798c4d..f46de055e 100644
--- a/lang/c++/impl/Compiler.cc
+++ b/lang/c++/impl/Compiler.cc
@@ -147,10 +147,13 @@ string getDocField(const Entity &e, const Object &m) {
 
 struct Field {
     const string name;
+    const vector<string> aliases;
     const NodePtr schema;
     const GenericDatum defaultValue;
     const CustomAttributes customAttributes;
-    Field(string n, NodePtr v, GenericDatum dv, const CustomAttributes& ca) : name(std::move(n)), schema(std::move(v)), defaultValue(std::move(dv)), customAttributes(std::move(ca)) {}
+
+    Field(string n, vector<string> a, NodePtr v, GenericDatum dv, const CustomAttributes& ca)
+        : name(std::move(n)), aliases(std::move(a)), schema(std::move(v)), defaultValue(std::move(dv)), customAttributes(ca) {}
 };
 
 static void assertType(const Entity &e, EntityType et) {
@@ -263,7 +266,7 @@ static GenericDatum makeGenericDatum(NodePtr n,
 static const std::unordered_set<std::string>& getKnownFields() {
     // return known fields
     static const std::unordered_set<std::string> kKnownFields =
-        {"name", "type", "default", "doc", "size", "logicalType",
+        {"name", "type", "aliases", "default", "doc", "size", "logicalType",
          "values", "precision", "scale", "namespace"};
       return kKnownFields;
 }
@@ -282,7 +285,13 @@ static void getCustomAttributes(const Object& m, CustomAttributes &customAttribu
 
 static Field makeField(const Entity &e, SymbolTable &st, const string &ns) {
     const Object &m = e.objectValue();
-    const string &n = getStringField(e, m, "name");
+    string n = getStringField(e, m, "name");
+    vector<string> aliases;
+    if (containsField(m, "aliases")) {
+        for (const auto &alias : getArrayField(e, m, "aliases")) {
+            aliases.emplace_back(alias.stringValue());
+        }
+    }
     auto it = findField(e, m, "type");
     auto it2 = m.find("default");
     NodePtr node = makeNode(it->second, st, ns);
@@ -293,34 +302,34 @@ static Field makeField(const Entity &e, SymbolTable &st, const string &ns) {
     // Get custom attributes
     CustomAttributes customAttributes;
     getCustomAttributes(m, customAttributes);
-
-    return Field(n, node, d, customAttributes);
+    return Field(std::move(n), std::move(aliases), node, d, customAttributes);
 }
 
 // Extended makeRecordNode (with doc).
 static NodePtr makeRecordNode(const Entity &e, const Name &name,
                               const string *doc, const Object &m,
                               SymbolTable &st, const string &ns) {
-    const Array &v = getArrayField(e, m, "fields");
     concepts::MultiAttribute<string> fieldNames;
+    vector<vector<string>> fieldAliases;
     concepts::MultiAttribute<NodePtr> fieldValues;
     concepts::MultiAttribute<CustomAttributes> customAttributes;
     vector<GenericDatum> defaultValues;
-
-    for (const auto &it : v) {
+    for (const auto &it : getArrayField(e, m, "fields")) {
         Field f = makeField(it, st, ns);
         fieldNames.add(f.name);
+        fieldAliases.push_back(f.aliases);
         fieldValues.add(f.schema);
         defaultValues.push_back(f.defaultValue);
         customAttributes.add(f.customAttributes);
     }
+
     NodeRecord *node;
     if (doc == nullptr) {
         node = new NodeRecord(asSingleAttribute(name), fieldValues, fieldNames,
-                              defaultValues, customAttributes);
+                              fieldAliases, defaultValues, customAttributes);
     } else {
         node = new NodeRecord(asSingleAttribute(name), asSingleAttribute(*doc),
-                              fieldValues, fieldNames, defaultValues, customAttributes);
+                              fieldValues, fieldNames, fieldAliases, defaultValues, customAttributes);
     }
     return NodePtr(node);
 }
@@ -422,8 +431,9 @@ static NodePtr makeMapNode(const Entity &e, const Object &m,
 static Name getName(const Entity &e, const Object &m, const string &ns) {
     const string &name = getStringField(e, m, "name");
 
+    Name result;
     if (isFullName(name)) {
-        return Name(name);
+        result = Name(name);
     } else {
         auto it = m.find("namespace");
         if (it != m.end()) {
@@ -432,11 +442,19 @@ static Name getName(const Entity &e, const Object &m, const string &ns) {
                                     "Json field \"%1%\" is not a %2%: %3%")
                                 % "namespace" % json::type_traits<string>::name() % it->second.toString());
             }
-            Name result = Name(name, it->second.stringValue());
-            return result;
+            result = Name(name, it->second.stringValue());
+        } else {
+            result = Name(name, ns);
+        }
+    }
+
+    if (containsField(m, "aliases")) {
+        for (const auto &alias : getArrayField(e, m, "aliases")) {
+            result.addAlias(alias.stringValue());
         }
-        return Name(name, ns);
     }
+
+    return result;
 }
 
 static NodePtr makeNode(const Entity &e, const Object &m,
diff --git a/lang/c++/impl/Node.cc b/lang/c++/impl/Node.cc
index 46310d0f9..56ad1044b 100644
--- a/lang/c++/impl/Node.cc
+++ b/lang/c++/impl/Node.cc
@@ -17,6 +17,7 @@
  */
 
 #include <cmath>
+#include <unordered_set>
 
 #include "Node.hh"
 
@@ -26,12 +27,44 @@ using std::string;
 
 Node::~Node() = default;
 
+struct Name::Aliases {
+    std::vector<std::string> raw;
+    std::unordered_set<std::string> fullyQualified;
+};
+
+Name::Name() = default;
+
 Name::Name(const std::string &name) {
     fullname(name);
 }
 
+Name::Name(std::string simpleName, std::string ns) : ns_(std::move(ns)), simpleName_(std::move(simpleName)) {
+    check();
+}
+
+Name::Name(const Name& other) {
+    *this = other;
+}
+
+Name& Name::operator=(const Name& other) {
+    if (this != &other) {
+        ns_ = other.ns_;
+        simpleName_ = other.simpleName_;
+        if (other.aliases_) {
+            aliases_ = std::make_unique<Aliases>(*other.aliases_);
+        }
+    }
+    return *this;
+}
+
+Name::Name(Name&& other) = default;
+
+Name& Name::operator=(Name&& other) = default;
+
+Name::~Name() = default;
+
 string Name::fullname() const {
-    return (ns_.empty()) ? simpleName_ : ns_ + "." + simpleName_;
+    return ns_.empty() ? simpleName_ : ns_ + "." + simpleName_;
 }
 
 void Name::fullname(const string &name) {
@@ -46,6 +79,23 @@ void Name::fullname(const string &name) {
     check();
 }
 
+const std::vector<std::string>& Name::aliases() const {
+    static const std::vector<std::string> emptyAliases;
+    return aliases_ ? aliases_->raw : emptyAliases;
+}
+
+void Name::addAlias(const std::string &alias) {
+    if (!aliases_) {
+        aliases_ = std::make_unique<Aliases>();
+    }
+    aliases_->raw.push_back(alias);
+    if (!ns_.empty() && alias.find_last_of('.') == string::npos) {
+        aliases_->fullyQualified.emplace(ns_ + "." + alias);
+    } else {
+        aliases_->fullyQualified.insert(alias);
+    }
+}
+
 bool Name::operator<(const Name &n) const {
     return (ns_ < n.ns_) || (!(n.ns_ < ns_) && (simpleName_ < n.simpleName_));
 }
@@ -72,6 +122,16 @@ bool Name::operator==(const Name &n) const {
     return ns_ == n.ns_ && simpleName_ == n.simpleName_;
 }
 
+bool Name::equalOrAliasedBy(const Name &n) const {
+    return *this == n || (n.aliases_ && n.aliases_->fullyQualified.find(fullname()) != n.aliases_->fullyQualified.end());
+}
+
+void Name::clear() {
+    ns_.clear();
+    simpleName_.clear();
+    aliases_.reset();
+}
+
 void Node::setLogicalType(LogicalType logicalType) {
     checkLock();
 
diff --git a/lang/c++/impl/NodeImpl.cc b/lang/c++/impl/NodeImpl.cc
index 5549c68fd..3d1f80a95 100644
--- a/lang/c++/impl/NodeImpl.cc
+++ b/lang/c++/impl/NodeImpl.cc
@@ -254,17 +254,34 @@ static void printName(std::ostream &os, const Name &n, size_t depth) {
 void NodeRecord::printJson(std::ostream &os, size_t depth) const {
     os << "{\n";
     os << indent(++depth) << "\"type\": \"record\",\n";
-    printName(os, nameAttribute_.get(), depth);
+    const Name &name = nameAttribute_.get();
+    printName(os, name, depth);
+
+    const auto &aliases = name.aliases();
+    if (!aliases.empty()) {
+        os << indent(depth) << "\"aliases\": [";
+        ++depth;
+        for (size_t i = 0; i < aliases.size(); ++i) {
+            if (i > 0) {
+                os << ',';
+            }
+            os << '\n'
+               << indent(depth) << "\"" << aliases[i] << "\"";
+        }
+        os << '\n'
+           << indent(--depth) << "]\n";
+    }
+
     if (!getDoc().empty()) {
         os << indent(depth) << R"("doc": ")"
            << escape(getDoc()) << "\",\n";
     }
-    os << indent(depth) << "\"fields\": [";
 
+    os << indent(depth) << "\"fields\": [";
     size_t fields = leafAttributes_.size();
     ++depth;
-    // Serialize "default" field:
-    assert(defaultValues.empty() || (defaultValues.size() == fields));
+    assert(fieldsAliases_.empty() || (fieldsAliases_.size() == fields));
+    assert(fieldsDefaultValues_.empty() || (fieldsDefaultValues_.size() == fields));
     assert(customAttributes_.size() == 0 || customAttributes_.size() == fields);
     for (size_t i = 0; i < fields; ++i) {
         if (i > 0) {
@@ -276,19 +293,37 @@ void NodeRecord::printJson(std::ostream &os, size_t depth) const {
         os << indent(depth) << "\"type\": ";
         leafAttributes_.get(i)->printJson(os, depth);
 
-        if (!defaultValues.empty()) {
-            if (!defaultValues[i].isUnion() && defaultValues[i].type() == AVRO_NULL) {
+        if (!fieldsAliases_.empty() && !fieldsAliases_[i].empty()) {
+            os << ",\n"
+               << indent(depth) << "\"aliases\": [";
+            ++depth;
+            for (size_t j = 0; j < fieldsAliases_[i].size(); ++j) {
+                if (j > 0) {
+                    os << ',';
+                }
+                os << '\n'
+                << indent(depth) << "\"" << fieldsAliases_[i][j] << "\"";
+            }
+            os << '\n'
+            << indent(--depth) << ']';
+        }
+
+        // Serialize "default" field:
+        if (!fieldsDefaultValues_.empty()) {
+            if (!fieldsDefaultValues_[i].isUnion() && fieldsDefaultValues_[i].type() == AVRO_NULL) {
                 // No "default" field.
             } else {
                 os << ",\n"
                    << indent(depth) << "\"default\": ";
-                leafAttributes_.get(i)->printDefaultToJson(defaultValues[i], os,
+                leafAttributes_.get(i)->printDefaultToJson(fieldsDefaultValues_[i], os,
                                                            depth);
             }
         }
+
         if(customAttributes_.size() == fields) {
           printCustomAttributes(customAttributes_.get(i), depth, os);
         }
+
         os << '\n';
         os << indent(--depth) << '}';
     }
@@ -424,16 +459,38 @@ void NodeRecord::printDefaultToJson(const GenericDatum &g, std::ostream &os,
            << indent(--depth) << "}";
     }
 }
-NodeRecord::NodeRecord(const HasName &name,
-                       const MultiLeaves &fields,
-                       const LeafNames &fieldsNames,
-                       std::vector<GenericDatum> dv) : NodeImplRecord(AVRO_RECORD, name, fields, fieldsNames, MultiAttributes(), NoSize()),
-                                                       defaultValues(std::move(dv)) {
+
+NodeRecord::NodeRecord(const HasName &name, const MultiLeaves &fields,
+                       const LeafNames &fieldsNames, std::vector<GenericDatum> dv)
+    : NodeRecord(name, HasDoc(), fields, fieldsNames, {}, std::move(dv), MultiAttributes()) {}
+
+NodeRecord::NodeRecord(const HasName &name, const HasDoc &doc, const MultiLeaves &fields,
+                       const LeafNames &fieldsNames, std::vector<GenericDatum> dv)
+    : NodeRecord(name, doc, fields, fieldsNames, {}, std::move(dv), MultiAttributes()) {}
+
+NodeRecord::NodeRecord(const HasName &name, const MultiLeaves &fields,
+                       const LeafNames &fieldsNames, std::vector<std::vector<std::string>> fieldsAliases,
+                       std::vector<GenericDatum> dv, const MultiAttributes &customAttributes)
+    : NodeRecord(name, HasDoc(), fields, fieldsNames, std::move(fieldsAliases), std::move(dv), customAttributes) {}
+
+NodeRecord::NodeRecord(const HasName &name, const HasDoc &doc, const MultiLeaves &fields,
+                       const LeafNames &fieldsNames, std::vector<std::vector<std::string>> fieldsAliases,
+                       std::vector<GenericDatum> dv, const MultiAttributes &customAttributes)
+    : NodeImplRecord(AVRO_RECORD, name, doc, fields, fieldsNames, customAttributes, NoSize()),
+      fieldsAliases_(std::move(fieldsAliases)),
+      fieldsDefaultValues_(std::move(dv)) {
+
     for (size_t i = 0; i < leafNameAttributes_.size(); ++i) {
         if (!nameIndex_.add(leafNameAttributes_.get(i), i)) {
-            throw Exception(boost::format(
-                                "Cannot add duplicate field: %1%")
-                            % leafNameAttributes_.get(i));
+            throw Exception(boost::format("Cannot add duplicate field: %1%") % leafNameAttributes_.get(i));
+        }
+
+        if (!fieldsAliases_.empty()) {
+            for (const auto &alias : fieldsAliases_[i]) {
+                if (!nameIndex_.add(alias, i)) {
+                    throw Exception(boost::format("Cannot add duplicate field: %1%") % alias);
+                }
+            }
         }
     }
 }
diff --git a/lang/c++/impl/parsing/ResolvingDecoder.cc b/lang/c++/impl/parsing/ResolvingDecoder.cc
index d86f6e582..58e5cdc3d 100644
--- a/lang/c++/impl/parsing/ResolvingDecoder.cc
+++ b/lang/c++/impl/parsing/ResolvingDecoder.cc
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <map>
 #include <memory>
+#include <set>
 #include <string>
 #include <utility>
 
@@ -38,17 +39,14 @@ using std::make_shared;
 
 namespace parsing {
 
-using std::make_shared;
 using std::shared_ptr;
 using std::static_pointer_cast;
 
-using std::find_if;
-using std::istringstream;
 using std::make_pair;
 using std::map;
-using std::ostringstream;
 using std::pair;
 using std::reverse;
+using std::set;
 using std::stack;
 using std::string;
 using std::unique_ptr;
@@ -67,15 +65,6 @@ class ResolvingGrammarGenerator : public ValidatingGrammarGenerator {
                                const NodePtr &reader, map<NodePair, ProductionPtr> &m,
                                map<NodePtr, ProductionPtr> &m2);
 
-    static vector<pair<string, size_t>> fields(const NodePtr &n) {
-        vector<pair<string, size_t>> result;
-        size_t c = n->names();
-        for (size_t i = 0; i < c; ++i) {
-            result.emplace_back(n->nameAt(i), i);
-        }
-        return result;
-    }
-
     static int bestBranch(const NodePtr &writer, const NodePtr &reader);
 
     ProductionPtr getWriterProduction(const NodePtr &n,
@@ -154,15 +143,6 @@ static shared_ptr<vector<uint8_t>> getAvroBinary(
     return snapshot(*os);
 }
 
-template<typename T1, typename T2>
-struct equalsFirst {
-    const T1 &v_;
-    explicit equalsFirst(const T1 &v) : v_(v) {}
-    bool operator()(const pair<T1, T2> &p) {
-        return p.first == v_;
-    }
-};
-
 ProductionPtr ResolvingGrammarGenerator::getWriterProduction(
     const NodePtr &n, map<NodePtr, ProductionPtr> &m2) {
     const NodePtr &nn = (n->type() == AVRO_SYMBOLIC) ? static_cast<const NodeSymbolic &>(*n).getNode() : n;
@@ -182,10 +162,18 @@ ProductionPtr ResolvingGrammarGenerator::resolveRecords(
     map<NodePtr, ProductionPtr> &m2) {
     ProductionPtr result = make_shared<Production>();
 
-    vector<pair<string, size_t>> wf = fields(writer);
-    vector<pair<string, size_t>> rf = fields(reader);
+    vector<string> wf(writer->names());
+    for (size_t i = 0; i < wf.size(); ++i) {
+        wf[i] = writer->nameAt(i);
+    }
+
+    set<size_t> rf;
+    for (size_t i = 0; i < reader->names(); ++i) {
+        rf.emplace(i);
+    }
+
     vector<size_t> fieldOrder;
-    fieldOrder.reserve(reader->names());
+    fieldOrder.reserve(rf.size());
 
     /*
      * We look for all writer fields in the reader. If found, recursively
@@ -193,19 +181,15 @@ ProductionPtr ResolvingGrammarGenerator::resolveRecords(
      * If no matching field is found for reader, arrange to skip the writer
      * field.
      */
-    for (vector<pair<string, size_t>>::const_iterator it = wf.begin();
-         it != wf.end(); ++it) {
-        auto it2 = find_if(rf.begin(), rf.end(),
-                           equalsFirst<string, size_t>(it->first));
-        if (it2 != rf.end()) {
-            ProductionPtr p = doGenerate2(writer->leafAt(it->second),
-                                          reader->leafAt(it2->second), m, m2);
+    for (size_t wi = 0; wi != wf.size(); ++wi) {
+        size_t ri;
+        if (reader->nameIndex(wf[wi], ri)) {
+            ProductionPtr p = doGenerate2(writer->leafAt(wi), reader->leafAt(ri), m, m2);
             copy(p->rbegin(), p->rend(), back_inserter(*result));
-            fieldOrder.push_back(it2->second);
-            rf.erase(it2);
+            fieldOrder.push_back(ri);
+            rf.erase(ri);
         } else {
-            ProductionPtr p = getWriterProduction(
-                writer->leafAt(it->second), m2);
+            ProductionPtr p = getWriterProduction(writer->leafAt(wi), m2);
             result->push_back(Symbol::skipStart());
             if (p->size() == 1) {
                 result->push_back((*p)[0]);
@@ -216,24 +200,21 @@ ProductionPtr ResolvingGrammarGenerator::resolveRecords(
     }
 
     /*
-     * Examine the reader fields left out, (i.e. those didn't have corresponding
+     * Examine the reader fields left out (i.e. those didn't have corresponding
      * writer field).
      */
-    for (vector<pair<string, size_t>>::const_iterator it = rf.begin();
-         it != rf.end(); ++it) {
-
-        NodePtr s = reader->leafAt(it->second);
-        fieldOrder.push_back(it->second);
+    for (const auto ri : rf) {
+        NodePtr s = reader->leafAt(ri);
+        fieldOrder.push_back(ri);
 
         if (s->type() == AVRO_SYMBOLIC) {
             s = resolveSymbol(s);
         }
         shared_ptr<vector<uint8_t>> defaultBinary =
-            getAvroBinary(reader->defaultValueAt(it->second));
+            getAvroBinary(reader->defaultValueAt(ri));
         result->push_back(Symbol::defaultStartAction(defaultBinary));
-        map<NodePair, shared_ptr<Production>>::const_iterator it2 =
-            m.find(NodePair(s, s));
-        ProductionPtr p = (it2 == m.end()) ? doGenerate2(s, s, m, m2) : it2->second;
+        auto it = m.find(NodePair(s, s));
+        ProductionPtr p = it == m.end() ? doGenerate2(s, s, m, m2) : it->second;
         copy(p->rbegin(), p->rend(), back_inserter(*result));
         result->push_back(Symbol::defaultEndAction());
     }
@@ -289,7 +270,7 @@ ProductionPtr ResolvingGrammarGenerator::doGenerate2(
             case AVRO_BYTES:
                 return make_shared<Production>(1, Symbol::bytesSymbol());
             case AVRO_FIXED:
-                if (writer->name() == reader->name() && writer->fixedSize() == reader->fixedSize()) {
+                if (writer->name().equalOrAliasedBy(reader->name()) && writer->fixedSize() == reader->fixedSize()) {
                     ProductionPtr result = make_shared<Production>();
                     result->push_back(Symbol::sizeCheckSymbol(reader->fixedSize()));
                     result->push_back(Symbol::fixedSymbol());
@@ -298,7 +279,7 @@ ProductionPtr ResolvingGrammarGenerator::doGenerate2(
                 }
                 break;
             case AVRO_RECORD:
-                if (writer->name() == reader->name()) {
+                if (writer->name().equalOrAliasedBy(reader->name())) {
                     const pair<NodePtr, NodePtr> key(writer, reader);
                     map<NodePair, ProductionPtr>::const_iterator kp = m.find(key);
                     if (kp != m.end()) {
@@ -312,7 +293,7 @@ ProductionPtr ResolvingGrammarGenerator::doGenerate2(
                 break;
 
             case AVRO_ENUM:
-                if (writer->name() == reader->name()) {
+                if (writer->name().equalOrAliasedBy(reader->name())) {
                     ProductionPtr result = make_shared<Production>();
                     result->push_back(Symbol::enumAdjustSymbol(writer, reader));
                     result->push_back(Symbol::enumSymbol());
diff --git a/lang/c++/test/CodecTests.cc b/lang/c++/test/CodecTests.cc
index 27779bb03..79f546f85 100644
--- a/lang/c++/test/CodecTests.cc
+++ b/lang/c++/test/CodecTests.cc
@@ -1264,6 +1264,46 @@ static const TestData3 data3[] = {
 
     {R"(["boolean", "int"])", "U1I", R"(["boolean", "long"])", "U1L", 1},
     {R"(["boolean", "int"])", "U1I", R"(["long", "boolean"])", "U0L", 1},
+
+    // Aliases
+    {"{\"type\":\"record\", \"name\":\"r\", \"fields\":["
+     "{\"name\":\"f0\", \"type\":\"int\"},"
+     "{\"name\":\"f1\", \"type\":\"boolean\"},"
+     "{\"name\":\"f2\", \"type\":\"double\"}]}",
+     "IBD",
+     "{\"type\":\"record\", \"name\":\"s\", \"aliases\":[\"r\"], \"fields\":["
+     "{\"name\":\"g0\", \"type\":\"int\", \"aliases\":[\"f0\"]},"
+     "{\"name\":\"g1\", \"type\":\"boolean\", \"aliases\":[\"f1\"]},"
+     "{\"name\":\"f2\", \"type\":\"double\", \"aliases\":[\"g2\"]}]}",
+     "IBD",
+     1},
+    {"{\"type\":\"record\", \"name\":\"r\", \"namespace\":\"n\", \"fields\":["
+     "{\"name\":\"f0\", \"type\":\"int\"}]}",
+     "I",
+     "{\"type\":\"record\", \"name\":\"s\", \"namespace\":\"n2\", \"aliases\":[\"t\", \"n.r\"], \"fields\":["
+     " {\"name\":\"f0\", \"type\":\"int\"}]}",
+     "I",
+     1},
+    {"{\"type\":\"enum\", \"name\":\"e\", \"symbols\":[\"a\", \"b\"]}",
+     "e1",
+     "{\"type\":\"enum\", \"name\":\"f\", \"aliases\":[\"e\"], \"symbols\":[\"a\", \"b\", \"c\"]}",
+     "e1",
+     1},
+    {"{\"type\":\"enum\", \"name\":\"e\", \"namespace\":\"n\", \"symbols\":[\"a\", \"b\"]}",
+     "e1",
+     "{\"type\":\"enum\", \"name\":\"f\", \"namespace\":\"n2\", \"aliases\":[\"g\", \"n.e\"], \"symbols\":[\"a\", \"b\"]}",
+     "e1",
+     1},
+    {"{\"type\":\"fixed\", \"name\":\"f\", \"size\":8}",
+     "f8",
+     "{\"type\":\"fixed\", \"name\":\"g\", \"aliases\":[\"f\"], \"size\":8}",
+     "f8",
+     1},
+    {"{\"type\":\"fixed\", \"name\":\"f\", \"namespace\":\"n\", \"size\":8}",
+     "f8",
+     "{\"type\":\"fixed\", \"name\":\"g\", \"namespace\":\"n2\", \"aliases\":[\"h\", \"n.f\"], \"size\":8}",
+     "f8",
+     1},
 };
 
 static const TestData4 data4[] = {
diff --git a/lang/c++/test/unittest.cc b/lang/c++/test/unittest.cc
index 73b92f1f7..962310c10 100644
--- a/lang/c++/test/unittest.cc
+++ b/lang/c++/test/unittest.cc
@@ -437,6 +437,7 @@ struct TestSchema {
         Name recordName("Test");
         HasName nameConcept(recordName);
         concepts::MultiAttribute<std::string> fieldNames;
+        std::vector<std::vector<std::string>> fieldAliases;
         concepts::MultiAttribute<NodePtr> fieldValues;
         std::vector<GenericDatum> defaultValues;
         concepts::MultiAttribute<CustomAttributes> customAttributes;
@@ -453,7 +454,7 @@ struct TestSchema {
         customAttributes.add(cf);
 
         NodeRecord nodeRecordWithCustomAttribute(nameConcept, fieldValues,
-                                            fieldNames, defaultValues,
+                                            fieldNames, fieldAliases, defaultValues,
                                             customAttributes);
         std::string expectedJsonWithCustomAttribute =
         "{\"type\": \"record\", \"name\": \"Test\",\"fields\": "