Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lang/c++/impl/Compiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,13 @@ static NodePtr makeNode(const Entity &e, const Object &m,
result = makeMapNode(e, m, st, ns);
} else {
result = makePrimitive(type);
if (result) {
CustomAttributes customAttributes;
getCustomAttributes(m, customAttributes);
if (!customAttributes.attributes().empty()) {
result->addCustomAttributesForField(customAttributes);
}
}
}

if (result) {
Expand Down
9 changes: 8 additions & 1 deletion lang/c++/impl/NodeImpl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,10 @@ NodeSymbolic::resolve(const Node &reader) const {

void NodePrimitive::printJson(std::ostream &os, size_t depth) const {
bool hasLogicalType = logicalType().type() != LogicalType::NONE;
bool hasCustomAttributes = customAttributes_.size() != 0;
bool printAsObject = hasLogicalType || hasCustomAttributes;

if (hasLogicalType) {
if (printAsObject) {
os << "{\n"
<< indent(depth) << "\"type\": ";
}
Expand All @@ -232,6 +234,11 @@ void NodePrimitive::printJson(std::ostream &os, size_t depth) const {
os << ",\n"
<< indent(depth);
logicalType().printJson(os);
}
for (size_t i = 0; i != customAttributes_.size(); ++i) {
printCustomAttributes(customAttributes_.get(i), depth, os);
}
if (printAsObject) {
os << "\n}";
}
if (!getDoc().empty()) {
Expand Down
25 changes: 25 additions & 0 deletions lang/c++/test/SchemaTests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,30 @@ static void testCustomAttributesJson2Schema2Json() {
BOOST_CHECK_EQUAL(removeWhitespaceFromSchema(json), removeWhitespaceFromSchema(schema));
}

static void testPrimitiveCustomAttributesJsonRoundTrip() {
const std::vector<std::string> schemas = {
R"({
"type": "long",
"logicalType": "timestamp-micros",
"adjust-to-utc": true
})",
R"({
"type": "long",
"custom-property": "value"
})",
};

// Iceberg relies on adjust-to-utc to distinguish timestamp from timestamptz.
// Primitive schema properties must also round-trip without a logical type.
for (const auto &schema : schemas) {
ValidSchema compiledSchema = compileJsonSchemaFromString(schema);
BOOST_REQUIRE_EQUAL(compiledSchema.root()->customAttributes(), 1);

std::string json = compiledSchema.toJson();
BOOST_CHECK_EQUAL(removeWhitespaceFromSchema(json), removeWhitespaceFromSchema(schema));
}
}

static void testCustomAttributesSchema2Json2Schema() {
const std::string expected = R"({
"type": "record",
Expand Down Expand Up @@ -951,6 +975,7 @@ init_unit_test_suite(int /*argc*/, char * /*argv*/[]) {
ts->add(BOOST_TEST_CASE(&avro::schema::testParseCustomAttributes));
ts->add(BOOST_TEST_CASE(&avro::schema::testAddCustomAttributes));
ts->add(BOOST_TEST_CASE(&avro::schema::testCustomAttributesJson2Schema2Json));
ts->add(BOOST_TEST_CASE(&avro::schema::testPrimitiveCustomAttributesJsonRoundTrip));
ts->add(BOOST_TEST_CASE(&avro::schema::testCustomAttributesSchema2Json2Schema));
return ts;
}
Loading