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\": "