This is an automated email from the ASF dual-hosted git repository.
pitrou pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/main by this push:
new c8240bd63a0 GH-50830: [C++][Parquet] Use JsonWriter for
LogicalType::ToJSON() (#50877)
c8240bd63a0 is described below
commit c8240bd63a0a6977a94a708999219512c4400389
Author: Nathan Chung <[email protected]>
AuthorDate: Thu Aug 27 03:07:49 2026 -0500
GH-50830: [C++][Parquet] Use JsonWriter for LogicalType::ToJSON() (#50877)
### Rationale for this change
This change was made because it refactored ToJSON() to build JSON through a
proper writer instead of hand-built strings, which removes a correctness risk.
It also aligns the codebase's ongoing transition away from manual JSON
construction.
### What changes are included in this PR?
This PR focused on changing the 8 ToJSON() functions from hand built
strings to using JsonWriter. In addition, the test files (schema_test.cc,
reader_test.cc) were also updated to reflect these changes. Finally,
WriteCrsKeyAndValue were no longer needed so it was deleted.
### Are these changes tested?
These changes are tested and verified. There was a parquet-schema-test in
which 41/41 testcases passed. There was a parquet-reader-test in which 149/154
passed and 5 were skipped as they were unrelated. A Full ctest -R "^parquet-":
11/11 suites passed. And finally the pre-commit (C++ Format + C++ Lint) came
out clean.
### Are there any user-facing changes?
Yes, there are user-facing changes. ToJSON() now produces compact JSON
instead of spaced JSON. It's the same valid JSON, but different exact bytes:
- Before: {"Type": "Decimal", "precision": 10, "scale": 4}
- After: {"Type":"Decimal","precision":10,"scale":4}
### AI Disclosure
Per the AI-generated code guidance: the 8 ToJSON() transitions to
JsonWriter, test file changes, the merge conflict resolutions, and the test
were produced with Claude Code, and reviewed and verified by me. Correctness
was checked by rebuilding and running the relevant test after every single
function conversion, not just once at the end. In addition, catching two
mistakes early via failing tests (the Decimal attempt that dropped fields, the
Int attempt that did the same).
* GitHub Issue: #50830
Authored-by: Nathan Chung <[email protected]>
Signed-off-by: Antoine Pitrou <[email protected]>
---
cpp/src/arrow/json/json_writer_internal.cc | 11 ++
cpp/src/arrow/json/json_writer_internal.h | 4 +
cpp/src/arrow/json/json_writer_internal_test.cc | 43 +++++
cpp/src/parquet/printer.cc | 215 ++++++++++++------------
cpp/src/parquet/reader_test.cc | 201 +++++++++++++---------
cpp/src/parquet/schema_test.cc | 106 ++++++------
cpp/src/parquet/types.cc | 114 +++++++------
7 files changed, 395 insertions(+), 299 deletions(-)
diff --git a/cpp/src/arrow/json/json_writer_internal.cc
b/cpp/src/arrow/json/json_writer_internal.cc
index 6d3e9ff9d85..e49841fda65 100644
--- a/cpp/src/arrow/json/json_writer_internal.cc
+++ b/cpp/src/arrow/json/json_writer_internal.cc
@@ -197,6 +197,12 @@ Result<std::string_view> JsonWriter::GetString() const {
return view;
}
+Result<std::string> JsonWriter::GetPrettyString(
+ const simdjson::fractured_json_options& options) const {
+ ARROW_ASSIGN_OR_RAISE(std::string_view json, GetString());
+ return simdjson::fractured_json_string(json, options);
+}
+
void JsonWriter::Clear() {
builder_.clear();
needs_comma_ = false;
@@ -218,4 +224,9 @@ void JsonWriter::BoolField(std::string_view key, bool
value) {
Bool(value);
}
+void JsonWriter::IntField(std::string_view key, int32_t value) {
+ Key(key);
+ Int(value);
+}
+
} // namespace arrow::json
diff --git a/cpp/src/arrow/json/json_writer_internal.h
b/cpp/src/arrow/json/json_writer_internal.h
index 4a8200f1df3..e407695daa9 100644
--- a/cpp/src/arrow/json/json_writer_internal.h
+++ b/cpp/src/arrow/json/json_writer_internal.h
@@ -58,9 +58,13 @@ class ARROW_EXPORT JsonWriter {
void StringField(std::string_view key, std::string_view value);
void BoolField(std::string_view key, bool value);
+ void IntField(std::string_view key, int32_t value);
Result<std::string_view> GetString() const;
+ Result<std::string> GetPrettyString(
+ const simdjson::fractured_json_options& options = {}) const;
+
void Clear();
private:
diff --git a/cpp/src/arrow/json/json_writer_internal_test.cc
b/cpp/src/arrow/json/json_writer_internal_test.cc
index 7fc12e8adbe..a0123e80836 100644
--- a/cpp/src/arrow/json/json_writer_internal_test.cc
+++ b/cpp/src/arrow/json/json_writer_internal_test.cc
@@ -290,4 +290,47 @@ TEST(JsonWriter, WriteValueAllNumberTypes) {
R"({"signed":-42,"unsigned":18446744073709551615,"double":2.5,"big":184467440737095516161234567890})");
}
+TEST(JsonWriter, IntField) {
+ JsonWriter writer;
+
+ writer.StartObject();
+ writer.IntField("a", 42);
+ writer.EndObject();
+
+ ASSERT_OK_AND_ASSIGN(std::string_view json, writer.GetString());
+
+ EXPECT_EQ(json, R"({"a":42})");
+}
+
+TEST(JsonWriter, GetPrettyString) {
+ JsonWriter writer;
+
+ writer.StartObject();
+ writer.Key("a");
+ writer.Int(42);
+ writer.Key("b");
+ writer.String("hello");
+ writer.EndObject();
+
+ ASSERT_OK_AND_ASSIGN(std::string pretty, writer.GetPrettyString());
+
+ // Pretty output should differ from the compact form (padded spacing, at
minimum),
+ // even though a small object like this may still be rendered on one line.
+ EXPECT_NE(pretty, R"({"a":42,"b":"hello"})");
+
+ // But it should still parse back to the same values.
+ sj::parser parser;
+ simdjson::padded_string padded(pretty);
+ sj::document doc;
+ ASSERT_EQ(parser.iterate(padded).get(doc), simdjson::SUCCESS);
+
+ int64_t a_value;
+ ASSERT_EQ(doc["a"].get(a_value), simdjson::SUCCESS);
+ EXPECT_EQ(a_value, 42);
+
+ std::string_view b_value;
+ ASSERT_EQ(doc["b"].get(b_value), simdjson::SUCCESS);
+ EXPECT_EQ(b_value, "hello");
+}
+
} // namespace arrow::json
diff --git a/cpp/src/parquet/printer.cc b/cpp/src/parquet/printer.cc
index dfce57a00fc..dd6a21913bf 100644
--- a/cpp/src/parquet/printer.cc
+++ b/cpp/src/parquet/printer.cc
@@ -21,9 +21,11 @@
#include <cstdio>
#include <memory>
#include <ostream>
+#include <sstream>
#include <string>
#include <vector>
+#include "arrow/json/json_writer_internal.h"
#include "arrow/util/key_value_metadata.h"
#include "arrow/util/string.h"
@@ -254,16 +256,18 @@ void ParquetFilePrinter::DebugPrint(std::ostream& stream,
std::list<int> selecte
void ParquetFilePrinter::JSONPrint(std::ostream& stream, std::list<int>
selected_columns,
const char* filename) {
const FileMetaData* file_metadata = fileReader->metadata().get();
- stream << "{\n";
- stream << " \"FileName\": \"" << filename << "\",\n";
- stream << " \"Version\": \"" <<
ParquetVersionToString(file_metadata->version())
- << "\",\n";
- stream << " \"CreatedBy\": \"" << file_metadata->created_by() << "\",\n";
- stream << " \"TotalRows\": \"" << file_metadata->num_rows() << "\",\n";
- stream << " \"NumberOfRowGroups\": \"" << file_metadata->num_row_groups()
<< "\",\n";
- stream << " \"NumberOfRealColumns\": \""
- << file_metadata->schema()->group_node()->field_count() << "\",\n";
- stream << " \"NumberOfColumns\": \"" << file_metadata->num_columns() <<
"\",\n";
+ ::arrow::json::JsonWriter writer;
+ writer.StartObject();
+ writer.StringField("FileName", filename);
+ writer.StringField("Version",
ParquetVersionToString(file_metadata->version()));
+ writer.StringField("CreatedBy", file_metadata->created_by());
+ writer.StringField("TotalRows", std::to_string(file_metadata->num_rows()));
+ writer.StringField("NumberOfRowGroups",
+ std::to_string(file_metadata->num_row_groups()));
+ writer.StringField(
+ "NumberOfRealColumns",
+ std::to_string(file_metadata->schema()->group_node()->field_count()));
+ writer.StringField("NumberOfColumns",
std::to_string(file_metadata->num_columns()));
if (selected_columns.empty()) {
for (int i = 0; i < file_metadata->num_columns(); i++) {
@@ -277,161 +281,152 @@ void ParquetFilePrinter::JSONPrint(std::ostream&
stream, std::list<int> selected
}
}
- stream << " \"Columns\": [\n";
- int c = 0;
+ writer.Key("Columns");
+ writer.StartArray();
for (auto i : selected_columns) {
const ColumnDescriptor* descr = file_metadata->schema()->Column(i);
- stream << " { \"Id\": \"" << i << "\","
- << " \"Name\": \"" << descr->path()->ToDotString() << "\","
- << " \"PhysicalType\": \""
- << TypeToString(descr->physical_type(), descr->type_length()) <<
"\","
- << " \"ConvertedType\": \"" <<
ConvertedTypeToString(descr->converted_type())
- << "\","
- << " \"LogicalType\": " << (descr->logical_type())->ToJSON() << "
}";
- c++;
- if (c != static_cast<int>(selected_columns.size())) {
- stream << ",\n";
- }
+ writer.StartObject();
+ writer.StringField("Id", std::to_string(i));
+ writer.StringField("Name", descr->path()->ToDotString());
+ writer.StringField("PhysicalType",
+ TypeToString(descr->physical_type(),
descr->type_length()));
+ writer.StringField("ConvertedType",
ConvertedTypeToString(descr->converted_type()));
+ writer.Key("LogicalType");
+ writer.RawValue(descr->logical_type()->ToJSON());
+ writer.EndObject();
}
+ writer.EndArray();
- stream << "\n ],\n \"RowGroups\": [\n";
+ writer.Key("RowGroups");
+ writer.StartArray();
for (int r = 0; r < file_metadata->num_row_groups(); ++r) {
- stream << " {\n \"Id\": \"" << r << "\", ";
+ writer.StartObject();
+ writer.StringField("Id", std::to_string(r));
auto group_reader = fileReader->RowGroup(r);
std::unique_ptr<RowGroupMetaData> group_metadata =
file_metadata->RowGroup(r);
- stream << " \"TotalBytes\": \"" << group_metadata->total_byte_size() <<
"\", ";
- stream << " \"TotalCompressedBytes\": \"" <<
group_metadata->total_compressed_size()
- << "\", ";
+ writer.StringField("TotalBytes",
std::to_string(group_metadata->total_byte_size()));
+ writer.StringField("TotalCompressedBytes",
+
std::to_string(group_metadata->total_compressed_size()));
auto row_group_sorting_columns = group_metadata->sorting_columns();
if (!row_group_sorting_columns.empty()) {
- stream << " \"SortColumns\": [\n";
- for (size_t i = 0; i < row_group_sorting_columns.size(); i++) {
- stream << " {\"column_idx\": " <<
row_group_sorting_columns[i].column_idx
- << ", \"descending\": " <<
row_group_sorting_columns[i].descending
- << ", \"nulls_first\": " <<
row_group_sorting_columns[i].nulls_first
- << "}";
- if (i + 1 != row_group_sorting_columns.size()) {
- stream << ",";
- }
- stream << '\n';
+ writer.Key("SortColumns");
+ writer.StartArray();
+ for (const auto& sorting_column : row_group_sorting_columns) {
+ writer.StartObject();
+ writer.IntField("column_idx", sorting_column.column_idx);
+ writer.IntField("descending", sorting_column.descending);
+ writer.IntField("nulls_first", sorting_column.nulls_first);
+ writer.EndObject();
}
- stream << " ], ";
+ writer.EndArray();
}
- stream << " \"Rows\": \"" << group_metadata->num_rows() << "\",\n";
+ writer.StringField("Rows", std::to_string(group_metadata->num_rows()));
- // Print column metadata
- stream << " \"ColumnChunks\": [\n";
- int c1 = 0;
+ writer.Key("ColumnChunks");
+ writer.StartArray();
for (auto i : selected_columns) {
auto column_chunk = group_metadata->ColumnChunk(i);
std::shared_ptr<Statistics> stats = column_chunk->statistics();
-
const ColumnDescriptor* descr = file_metadata->schema()->Column(i);
- stream << " {\"Id\": \"" << i << "\", \"Values\": \""
- << column_chunk->num_values() << "\", "
- << "\"StatsSet\": ";
+
+ writer.StartObject();
+ writer.StringField("Id", std::to_string(i));
+ writer.StringField("Values", std::to_string(column_chunk->num_values()));
if (column_chunk->is_stats_set()) {
- stream << R"("True", "Stats": {)";
+ writer.StringField("StatsSet", "True");
+ writer.Key("Stats");
+ writer.StartObject();
if (stats->HasNullCount()) {
- stream << R"("NumNulls": ")" << stats->null_count() << "\"";
+ writer.StringField("NumNulls", std::to_string(stats->null_count()));
}
if (stats->HasDistinctCount()) {
- stream << ", "
- << R"("DistinctValues": ")" << stats->distinct_count() <<
"\"";
+ writer.StringField("DistinctValues",
std::to_string(stats->distinct_count()));
}
if (stats->HasMinMax()) {
std::string min = stats->EncodeMin(), max = stats->EncodeMax();
- stream << ", "
- << R"("Max": ")"
- << FormatStatValue(descr->physical_type(), max,
descr->logical_type())
- << "\", "
- << R"("Min": ")"
- << FormatStatValue(descr->physical_type(), min,
descr->logical_type())
- << "\"";
+ writer.StringField(
+ "Max", FormatStatValue(descr->physical_type(), max,
descr->logical_type()));
+ writer.StringField(
+ "Min", FormatStatValue(descr->physical_type(), min,
descr->logical_type()));
if (stats->is_max_value_exact().has_value()) {
- stream << ", "
- << R"("IsMaxValueExact": ")"
- << (stats->is_max_value_exact().value() ? "True" : "False")
<< "\"";
+ writer.StringField("IsMaxValueExact",
+ stats->is_max_value_exact().value() ? "True" :
"False");
} else {
- stream << ", "
- << R"("IsMaxValueExact": "unknown")";
+ writer.StringField("IsMaxValueExact", "unknown");
}
if (stats->is_min_value_exact().has_value()) {
- stream << ", "
- << R"("IsMinValueExact": ")"
- << (stats->is_min_value_exact().value() ? "True" : "False")
<< "\"";
+ writer.StringField("IsMinValueExact",
+ stats->is_min_value_exact().value() ? "True" :
"False");
} else {
- stream << ", "
- << R"("IsMinValueExact": "unknown")";
+ writer.StringField("IsMinValueExact", "unknown");
}
}
- stream << " },";
+ writer.EndObject();
} else {
- stream << "\"False\",";
+ writer.StringField("StatsSet", "False");
}
- stream << "\n \"Compression\": \""
- << ::arrow::internal::AsciiToUpper(
- Codec::GetCodecAsString(column_chunk->compression()))
- << R"(", "Encodings": )";
- stream << "\"";
+
+ writer.StringField("Compression",
+ ::arrow::internal::AsciiToUpper(
+
Codec::GetCodecAsString(column_chunk->compression())));
+
+ std::ostringstream encodings_stream;
if (column_chunk->encoding_stats().empty()) {
for (auto encoding : column_chunk->encodings()) {
- stream << EncodingToString(encoding) << " ";
+ encodings_stream << EncodingToString(encoding) << " ";
}
} else {
- PrintPageEncodingStats(stream, column_chunk->encoding_stats());
+ PrintPageEncodingStats(encodings_stream,
column_chunk->encoding_stats());
}
- stream << "\"";
- stream << ", "
- << R"("UncompressedSize": ")" <<
column_chunk->total_uncompressed_size()
- << R"(", "CompressedSize": ")" <<
column_chunk->total_compressed_size()
- << "\"";
+ writer.StringField("Encodings", encodings_stream.str());
+
+ writer.StringField("UncompressedSize",
+
std::to_string(column_chunk->total_uncompressed_size()));
+ writer.StringField("CompressedSize",
+
std::to_string(column_chunk->total_compressed_size()));
if (column_chunk->bloom_filter_offset()) {
- // Output BloomFilter {offset, length}
- stream << ", \"BloomFilter\": {"
- << R"("offset": ")" <<
column_chunk->bloom_filter_offset().value() << "\"";
+ writer.Key("BloomFilter");
+ writer.StartObject();
+ writer.StringField("offset",
+
std::to_string(column_chunk->bloom_filter_offset().value()));
if (column_chunk->bloom_filter_length()) {
- stream << R"(, "length": ")" <<
column_chunk->bloom_filter_length().value()
- << "\"";
+ writer.StringField("length",
+
std::to_string(column_chunk->bloom_filter_length().value()));
}
- stream << "}";
+ writer.EndObject();
}
if (column_chunk->GetColumnIndexLocation()) {
auto location = column_chunk->GetColumnIndexLocation().value();
- // Output ColumnIndex {offset, length}
- stream << ", \"ColumnIndex\": {"
- << R"("offset": ")" << location.offset;
- stream << R"(", "length": ")" << location.length;
- stream << "\"}";
+ writer.Key("ColumnIndex");
+ writer.StartObject();
+ writer.StringField("offset", std::to_string(location.offset));
+ writer.StringField("length", std::to_string(location.length));
+ writer.EndObject();
}
if (column_chunk->GetOffsetIndexLocation()) {
auto location = column_chunk->GetOffsetIndexLocation().value();
- // Output OffsetIndex {offset, length}
- stream << ", \"OffsetIndex\": {"
- << R"("offset": ")" << location.offset << "\"";
- stream << R"(, "length": ")" << location.length << "\"";
- stream << "}";
+ writer.Key("OffsetIndex");
+ writer.StartObject();
+ writer.StringField("offset", std::to_string(location.offset));
+ writer.StringField("length", std::to_string(location.length));
+ writer.EndObject();
}
- // end of a ColumnChunk
- stream << " }";
- c1++;
- if (c1 != static_cast<int>(selected_columns.size())) {
- stream << ",\n";
- }
- }
-
- stream << "\n ]\n }";
- if ((r + 1) != static_cast<int>(file_metadata->num_row_groups())) {
- stream << ",\n";
+ writer.EndObject();
}
+ writer.EndArray();
+ writer.EndObject();
}
- stream << "\n ]\n}\n";
+ writer.EndArray();
+ writer.EndObject();
+
+ PARQUET_ASSIGN_OR_THROW(std::string pretty_json, writer.GetPrettyString());
+ stream << pretty_json << "\n";
}
} // namespace parquet
diff --git a/cpp/src/parquet/reader_test.cc b/cpp/src/parquet/reader_test.cc
index cdeee116fbc..eeb839e71fe 100644
--- a/cpp/src/parquet/reader_test.cc
+++ b/cpp/src/parquet/reader_test.cc
@@ -1103,28 +1103,64 @@ class TestJSONWithLocalFile : public ::testing::Test {
TEST_F(TestJSONWithLocalFile, JSONOutputWithStatistics) {
std::string json_output = R"###({
- "FileName": "nested_lists.snappy.parquet",
- "Version": "1.0",
- "CreatedBy": "parquet-mr version 1.8.2 (build
c6522788629e590a53eb79874b95f6c3ff11f16c)",
- "TotalRows": "3",
- "NumberOfRowGroups": "1",
- "NumberOfRealColumns": "2",
- "NumberOfColumns": "2",
- "Columns": [
- { "Id": "0", "Name": "a.list.element.list.element.list.element",
"PhysicalType": "BYTE_ARRAY", "ConvertedType": "UTF8", "LogicalType": {"Type":
"String"} },
- { "Id": "1", "Name": "b", "PhysicalType": "INT32", "ConvertedType":
"NONE", "LogicalType": {"Type": "None"} }
- ],
- "RowGroups": [
- {
- "Id": "0", "TotalBytes": "155", "TotalCompressedBytes": "0", "Rows":
"3",
- "ColumnChunks": [
- {"Id": "0", "Values": "18", "StatsSet": "False",
- "Compression": "SNAPPY", "Encodings": "PLAIN_DICTIONARY(DICT_PAGE)
PLAIN_DICTIONARY", "UncompressedSize": "103", "CompressedSize": "104" },
- {"Id": "1", "Values": "3", "StatsSet": "True", "Stats": {"NumNulls":
"0", "Max": "1", "Min": "1", "IsMaxValueExact": "unknown", "IsMinValueExact":
"unknown" },
- "Compression": "SNAPPY", "Encodings": "PLAIN_DICTIONARY(DICT_PAGE)
PLAIN_DICTIONARY", "UncompressedSize": "52", "CompressedSize": "56" }
- ]
- }
- ]
+ "FileName": "nested_lists.snappy.parquet",
+ "Version": "1.0",
+ "CreatedBy": "parquet-mr version 1.8.2 (build
c6522788629e590a53eb79874b95f6c3ff11f16c)",
+ "TotalRows": "3",
+ "NumberOfRowGroups": "1",
+ "NumberOfRealColumns": "2",
+ "NumberOfColumns": "2",
+ "Columns": [
+ {
+ "Id": "0",
+ "Name": "a.list.element.list.element.list.element",
+ "PhysicalType": "BYTE_ARRAY",
+ "ConvertedType": "UTF8",
+ "LogicalType": { "Type": "String" }
+ },
+ {
+ "Id": "1",
+ "Name": "b",
+ "PhysicalType": "INT32",
+ "ConvertedType": "NONE",
+ "LogicalType": { "Type": "None" }
+ }
+ ],
+ "RowGroups": [
+ {
+ "Id": "0",
+ "TotalBytes": "155",
+ "TotalCompressedBytes": "0",
+ "Rows": "3",
+ "ColumnChunks": [
+ {
+ "Id": "0",
+ "Values": "18",
+ "StatsSet": "False",
+ "Compression": "SNAPPY",
+ "Encodings": "PLAIN_DICTIONARY(DICT_PAGE)
PLAIN_DICTIONARY",
+ "UncompressedSize": "103",
+ "CompressedSize": "104"
+ },
+ {
+ "Id": "1",
+ "Values": "3",
+ "StatsSet": "True",
+ "Stats": {
+ "NumNulls": "0",
+ "Max": "1",
+ "Min": "1",
+ "IsMaxValueExact": "unknown",
+ "IsMinValueExact": "unknown"
+ },
+ "Compression": "SNAPPY",
+ "Encodings": "PLAIN_DICTIONARY(DICT_PAGE)
PLAIN_DICTIONARY",
+ "UncompressedSize": "52",
+ "CompressedSize": "56"
+ }
+ ]
+ }
+ ]
}
)###";
@@ -1134,55 +1170,47 @@ TEST_F(TestJSONWithLocalFile, JSONOutputWithStatistics)
{
TEST_F(TestJSONWithLocalFile, JSONOutput) {
std::string json_output = R"###({
- "FileName": "alltypes_plain.parquet",
- "Version": "1.0",
- "CreatedBy": "impala version 1.3.0-INTERNAL (build
8a48ddb1eff84592b3fc06bc6f51ec120e1fffc9)",
- "TotalRows": "8",
- "NumberOfRowGroups": "1",
- "NumberOfRealColumns": "11",
- "NumberOfColumns": "11",
- "Columns": [
- { "Id": "0", "Name": "id", "PhysicalType": "INT32", "ConvertedType":
"NONE", "LogicalType": {"Type": "None"} },
- { "Id": "1", "Name": "bool_col", "PhysicalType": "BOOLEAN",
"ConvertedType": "NONE", "LogicalType": {"Type": "None"} },
- { "Id": "2", "Name": "tinyint_col", "PhysicalType": "INT32",
"ConvertedType": "NONE", "LogicalType": {"Type": "None"} },
- { "Id": "3", "Name": "smallint_col", "PhysicalType": "INT32",
"ConvertedType": "NONE", "LogicalType": {"Type": "None"} },
- { "Id": "4", "Name": "int_col", "PhysicalType": "INT32", "ConvertedType":
"NONE", "LogicalType": {"Type": "None"} },
- { "Id": "5", "Name": "bigint_col", "PhysicalType": "INT64",
"ConvertedType": "NONE", "LogicalType": {"Type": "None"} },
- { "Id": "6", "Name": "float_col", "PhysicalType": "FLOAT",
"ConvertedType": "NONE", "LogicalType": {"Type": "None"} },
- { "Id": "7", "Name": "double_col", "PhysicalType": "DOUBLE",
"ConvertedType": "NONE", "LogicalType": {"Type": "None"} },
- { "Id": "8", "Name": "date_string_col", "PhysicalType": "BYTE_ARRAY",
"ConvertedType": "NONE", "LogicalType": {"Type": "None"} },
- { "Id": "9", "Name": "string_col", "PhysicalType": "BYTE_ARRAY",
"ConvertedType": "NONE", "LogicalType": {"Type": "None"} },
- { "Id": "10", "Name": "timestamp_col", "PhysicalType": "INT96",
"ConvertedType": "NONE", "LogicalType": {"Type": "None"} }
- ],
- "RowGroups": [
- {
- "Id": "0", "TotalBytes": "671", "TotalCompressedBytes": "0", "Rows":
"8",
- "ColumnChunks": [
- {"Id": "0", "Values": "8", "StatsSet": "False",
- "Compression": "UNCOMPRESSED", "Encodings": "RLE PLAIN_DICTIONARY
PLAIN ", "UncompressedSize": "73", "CompressedSize": "73" },
- {"Id": "1", "Values": "8", "StatsSet": "False",
- "Compression": "UNCOMPRESSED", "Encodings": "RLE PLAIN_DICTIONARY
PLAIN ", "UncompressedSize": "24", "CompressedSize": "24" },
- {"Id": "2", "Values": "8", "StatsSet": "False",
- "Compression": "UNCOMPRESSED", "Encodings": "RLE PLAIN_DICTIONARY
PLAIN ", "UncompressedSize": "47", "CompressedSize": "47" },
- {"Id": "3", "Values": "8", "StatsSet": "False",
- "Compression": "UNCOMPRESSED", "Encodings": "RLE PLAIN_DICTIONARY
PLAIN ", "UncompressedSize": "47", "CompressedSize": "47" },
- {"Id": "4", "Values": "8", "StatsSet": "False",
- "Compression": "UNCOMPRESSED", "Encodings": "RLE PLAIN_DICTIONARY
PLAIN ", "UncompressedSize": "47", "CompressedSize": "47" },
- {"Id": "5", "Values": "8", "StatsSet": "False",
- "Compression": "UNCOMPRESSED", "Encodings": "RLE PLAIN_DICTIONARY
PLAIN ", "UncompressedSize": "55", "CompressedSize": "55" },
- {"Id": "6", "Values": "8", "StatsSet": "False",
- "Compression": "UNCOMPRESSED", "Encodings": "RLE PLAIN_DICTIONARY
PLAIN ", "UncompressedSize": "47", "CompressedSize": "47" },
- {"Id": "7", "Values": "8", "StatsSet": "False",
- "Compression": "UNCOMPRESSED", "Encodings": "RLE PLAIN_DICTIONARY
PLAIN ", "UncompressedSize": "55", "CompressedSize": "55" },
- {"Id": "8", "Values": "8", "StatsSet": "False",
- "Compression": "UNCOMPRESSED", "Encodings": "RLE PLAIN_DICTIONARY
PLAIN ", "UncompressedSize": "88", "CompressedSize": "88" },
- {"Id": "9", "Values": "8", "StatsSet": "False",
- "Compression": "UNCOMPRESSED", "Encodings": "RLE PLAIN_DICTIONARY
PLAIN ", "UncompressedSize": "49", "CompressedSize": "49" },
- {"Id": "10", "Values": "8", "StatsSet": "False",
- "Compression": "UNCOMPRESSED", "Encodings": "RLE PLAIN_DICTIONARY
PLAIN ", "UncompressedSize": "139", "CompressedSize": "139" }
- ]
- }
- ]
+ "FileName": "alltypes_plain.parquet",
+ "Version": "1.0",
+ "CreatedBy": "impala version 1.3.0-INTERNAL (build
8a48ddb1eff84592b3fc06bc6f51ec120e1fffc9)",
+ "TotalRows": "8",
+ "NumberOfRowGroups": "1",
+ "NumberOfRealColumns": "11",
+ "NumberOfColumns": "11",
+ "Columns": [
+ { "ConvertedType": "NONE", "Id": "0" , "LogicalType": { "Type": "None"
}, "Name": "id" , "PhysicalType": "INT32" },
+ { "ConvertedType": "NONE", "Id": "1" , "LogicalType": { "Type": "None"
}, "Name": "bool_col" , "PhysicalType": "BOOLEAN" },
+ { "ConvertedType": "NONE", "Id": "2" , "LogicalType": { "Type": "None"
}, "Name": "tinyint_col" , "PhysicalType": "INT32" },
+ { "ConvertedType": "NONE", "Id": "3" , "LogicalType": { "Type": "None"
}, "Name": "smallint_col" , "PhysicalType": "INT32" },
+ { "ConvertedType": "NONE", "Id": "4" , "LogicalType": { "Type": "None"
}, "Name": "int_col" , "PhysicalType": "INT32" },
+ { "ConvertedType": "NONE", "Id": "5" , "LogicalType": { "Type": "None"
}, "Name": "bigint_col" , "PhysicalType": "INT64" },
+ { "ConvertedType": "NONE", "Id": "6" , "LogicalType": { "Type": "None"
}, "Name": "float_col" , "PhysicalType": "FLOAT" },
+ { "ConvertedType": "NONE", "Id": "7" , "LogicalType": { "Type": "None"
}, "Name": "double_col" , "PhysicalType": "DOUBLE" },
+ { "ConvertedType": "NONE", "Id": "8" , "LogicalType": { "Type": "None"
}, "Name": "date_string_col", "PhysicalType": "BYTE_ARRAY" },
+ { "ConvertedType": "NONE", "Id": "9" , "LogicalType": { "Type": "None"
}, "Name": "string_col" , "PhysicalType": "BYTE_ARRAY" },
+ { "ConvertedType": "NONE", "Id": "10", "LogicalType": { "Type": "None"
}, "Name": "timestamp_col" , "PhysicalType": "INT96" }
+ ],
+ "RowGroups": [
+ {
+ "Id": "0",
+ "TotalBytes": "671",
+ "TotalCompressedBytes": "0",
+ "Rows": "8",
+ "ColumnChunks": [
+ { "CompressedSize": "73" , "Compression": "UNCOMPRESSED",
"Encodings": "RLE PLAIN_DICTIONARY PLAIN ", "Id": "0" , "StatsSet": "False",
"UncompressedSize": "73" , "Values": "8" },
+ { "CompressedSize": "24" , "Compression": "UNCOMPRESSED",
"Encodings": "RLE PLAIN_DICTIONARY PLAIN ", "Id": "1" , "StatsSet": "False",
"UncompressedSize": "24" , "Values": "8" },
+ { "CompressedSize": "47" , "Compression": "UNCOMPRESSED",
"Encodings": "RLE PLAIN_DICTIONARY PLAIN ", "Id": "2" , "StatsSet": "False",
"UncompressedSize": "47" , "Values": "8" },
+ { "CompressedSize": "47" , "Compression": "UNCOMPRESSED",
"Encodings": "RLE PLAIN_DICTIONARY PLAIN ", "Id": "3" , "StatsSet": "False",
"UncompressedSize": "47" , "Values": "8" },
+ { "CompressedSize": "47" , "Compression": "UNCOMPRESSED",
"Encodings": "RLE PLAIN_DICTIONARY PLAIN ", "Id": "4" , "StatsSet": "False",
"UncompressedSize": "47" , "Values": "8" },
+ { "CompressedSize": "55" , "Compression": "UNCOMPRESSED",
"Encodings": "RLE PLAIN_DICTIONARY PLAIN ", "Id": "5" , "StatsSet": "False",
"UncompressedSize": "55" , "Values": "8" },
+ { "CompressedSize": "47" , "Compression": "UNCOMPRESSED",
"Encodings": "RLE PLAIN_DICTIONARY PLAIN ", "Id": "6" , "StatsSet": "False",
"UncompressedSize": "47" , "Values": "8" },
+ { "CompressedSize": "55" , "Compression": "UNCOMPRESSED",
"Encodings": "RLE PLAIN_DICTIONARY PLAIN ", "Id": "7" , "StatsSet": "False",
"UncompressedSize": "55" , "Values": "8" },
+ { "CompressedSize": "88" , "Compression": "UNCOMPRESSED",
"Encodings": "RLE PLAIN_DICTIONARY PLAIN ", "Id": "8" , "StatsSet": "False",
"UncompressedSize": "88" , "Values": "8" },
+ { "CompressedSize": "49" , "Compression": "UNCOMPRESSED",
"Encodings": "RLE PLAIN_DICTIONARY PLAIN ", "Id": "9" , "StatsSet": "False",
"UncompressedSize": "49" , "Values": "8" },
+ { "CompressedSize": "139", "Compression": "UNCOMPRESSED",
"Encodings": "RLE PLAIN_DICTIONARY PLAIN ", "Id": "10", "StatsSet": "False",
"UncompressedSize": "139", "Values": "8" }
+ ]
+ }
+ ]
}
)###";
@@ -1196,16 +1224,22 @@ TEST_F(TestJSONWithLocalFile, JSONOutputFLBA) {
std::string json_content =
ReadFromLocalFile("fixed_length_byte_array.parquet");
std::string json_contains = R"###({
- "FileName": "fixed_length_byte_array.parquet",
- "Version": "1.0",
- "CreatedBy": "parquet-mr version 1.13.0-SNAPSHOT (build
d057b39d93014fe40f5067ee4a33621e65c91552)",
- "TotalRows": "1000",
- "NumberOfRowGroups": "1",
- "NumberOfRealColumns": "1",
- "NumberOfColumns": "1",
- "Columns": [
- { "Id": "0", "Name": "flba_field", "PhysicalType":
"FIXED_LEN_BYTE_ARRAY(4)", "ConvertedType": "NONE", "LogicalType": {"Type":
"None"} }
- ])###";
+ "FileName": "fixed_length_byte_array.parquet",
+ "Version": "1.0",
+ "CreatedBy": "parquet-mr version 1.13.0-SNAPSHOT (build
d057b39d93014fe40f5067ee4a33621e65c91552)",
+ "TotalRows": "1000",
+ "NumberOfRowGroups": "1",
+ "NumberOfRealColumns": "1",
+ "NumberOfColumns": "1",
+ "Columns": [
+ {
+ "Id": "0",
+ "Name": "flba_field",
+ "PhysicalType": "FIXED_LEN_BYTE_ARRAY(4)",
+ "ConvertedType": "NONE",
+ "LogicalType": { "Type": "None" }
+ }
+ ],)###";
EXPECT_THAT(json_content, testing::HasSubstr(json_contains));
}
@@ -1214,9 +1248,8 @@ TEST_F(TestJSONWithLocalFile, JSONOutputSortColumns) {
std::string json_content = ReadFromLocalFile("sort_columns.parquet");
std::string json_contains = R"###("SortColumns": [
- {"column_idx": 0, "descending": 1, "nulls_first": 1},
- {"column_idx": 1, "descending": 0, "nulls_first": 0}
- ])###";
+ { "column_idx": 0, "descending": 1, "nulls_first": 1 }, {
"column_idx": 1, "descending": 0, "nulls_first": 0 }
+ ],)###";
EXPECT_THAT(json_content, testing::HasSubstr(json_contains));
}
diff --git a/cpp/src/parquet/schema_test.cc b/cpp/src/parquet/schema_test.cc
index 704b2da79c1..3888e4f8d95 100644
--- a/cpp/src/parquet/schema_test.cc
+++ b/cpp/src/parquet/schema_test.cc
@@ -1495,120 +1495,120 @@ TEST(TestLogicalTypeOperation,
LogicalTypeRepresentation) {
};
std::vector<ExpectedRepresentation> cases = {
- {UndefinedLogicalType::Make(), "Undefined", R"({"Type": "Undefined"})"},
- {LogicalType::String(), "String", R"({"Type": "String"})"},
- {LogicalType::Map(), "Map", R"({"Type": "Map"})"},
- {LogicalType::List(), "List", R"({"Type": "List"})"},
- {LogicalType::Enum(), "Enum", R"({"Type": "Enum"})"},
+ {UndefinedLogicalType::Make(), "Undefined", R"({"Type":"Undefined"})"},
+ {LogicalType::String(), "String", R"({"Type":"String"})"},
+ {LogicalType::Map(), "Map", R"({"Type":"Map"})"},
+ {LogicalType::List(), "List", R"({"Type":"List"})"},
+ {LogicalType::Enum(), "Enum", R"({"Type":"Enum"})"},
{LogicalType::Decimal(10, 4), "Decimal(precision=10, scale=4)",
- R"({"Type": "Decimal", "precision": 10, "scale": 4})"},
+ R"({"Type":"Decimal","precision":10,"scale":4})"},
{LogicalType::Decimal(10), "Decimal(precision=10, scale=0)",
- R"({"Type": "Decimal", "precision": 10, "scale": 0})"},
- {LogicalType::Date(), "Date", R"({"Type": "Date"})"},
+ R"({"Type":"Decimal","precision":10,"scale":0})"},
+ {LogicalType::Date(), "Date", R"({"Type":"Date"})"},
{LogicalType::Time(true, LogicalType::TimeUnit::MILLIS),
"Time(isAdjustedToUTC=true, timeUnit=milliseconds)",
- R"({"Type": "Time", "isAdjustedToUTC": true, "timeUnit":
"milliseconds"})"},
+ R"({"Type":"Time","isAdjustedToUTC":true,"timeUnit":"milliseconds"})"},
{LogicalType::Time(true, LogicalType::TimeUnit::MICROS),
"Time(isAdjustedToUTC=true, timeUnit=microseconds)",
- R"({"Type": "Time", "isAdjustedToUTC": true, "timeUnit":
"microseconds"})"},
+ R"({"Type":"Time","isAdjustedToUTC":true,"timeUnit":"microseconds"})"},
{LogicalType::Time(true, LogicalType::TimeUnit::NANOS),
"Time(isAdjustedToUTC=true, timeUnit=nanoseconds)",
- R"({"Type": "Time", "isAdjustedToUTC": true, "timeUnit":
"nanoseconds"})"},
+ R"({"Type":"Time","isAdjustedToUTC":true,"timeUnit":"nanoseconds"})"},
{LogicalType::Time(false, LogicalType::TimeUnit::MILLIS),
"Time(isAdjustedToUTC=false, timeUnit=milliseconds)",
- R"({"Type": "Time", "isAdjustedToUTC": false, "timeUnit":
"milliseconds"})"},
+ R"({"Type":"Time","isAdjustedToUTC":false,"timeUnit":"milliseconds"})"},
{LogicalType::Time(false, LogicalType::TimeUnit::MICROS),
"Time(isAdjustedToUTC=false, timeUnit=microseconds)",
- R"({"Type": "Time", "isAdjustedToUTC": false, "timeUnit":
"microseconds"})"},
+ R"({"Type":"Time","isAdjustedToUTC":false,"timeUnit":"microseconds"})"},
{LogicalType::Time(false, LogicalType::TimeUnit::NANOS),
"Time(isAdjustedToUTC=false, timeUnit=nanoseconds)",
- R"({"Type": "Time", "isAdjustedToUTC": false, "timeUnit":
"nanoseconds"})"},
+ R"({"Type":"Time","isAdjustedToUTC":false,"timeUnit":"nanoseconds"})"},
{LogicalType::Timestamp(true, LogicalType::TimeUnit::MILLIS),
"Timestamp(isAdjustedToUTC=true, timeUnit=milliseconds, "
"is_from_converted_type=false, force_set_converted_type=false)",
- R"({"Type": "Timestamp", "isAdjustedToUTC": true, "timeUnit":
"milliseconds", )"
- R"("is_from_converted_type": false, "force_set_converted_type":
false})"},
+
R"({"Type":"Timestamp","isAdjustedToUTC":true,"timeUnit":"milliseconds",)"
+ R"("is_from_converted_type":false,"force_set_converted_type":false})"},
{LogicalType::Timestamp(true, LogicalType::TimeUnit::MICROS),
"Timestamp(isAdjustedToUTC=true, timeUnit=microseconds, "
"is_from_converted_type=false, force_set_converted_type=false)",
- R"({"Type": "Timestamp", "isAdjustedToUTC": true, "timeUnit":
"microseconds", )"
- R"("is_from_converted_type": false, "force_set_converted_type":
false})"},
+
R"({"Type":"Timestamp","isAdjustedToUTC":true,"timeUnit":"microseconds",)"
+ R"("is_from_converted_type":false,"force_set_converted_type":false})"},
{LogicalType::Timestamp(true, LogicalType::TimeUnit::NANOS),
"Timestamp(isAdjustedToUTC=true, timeUnit=nanoseconds, "
"is_from_converted_type=false, force_set_converted_type=false)",
- R"({"Type": "Timestamp", "isAdjustedToUTC": true, "timeUnit":
"nanoseconds", )"
- R"("is_from_converted_type": false, "force_set_converted_type":
false})"},
+
R"({"Type":"Timestamp","isAdjustedToUTC":true,"timeUnit":"nanoseconds",)"
+ R"("is_from_converted_type":false,"force_set_converted_type":false})"},
{LogicalType::Timestamp(false, LogicalType::TimeUnit::MILLIS, true,
true),
"Timestamp(isAdjustedToUTC=false, timeUnit=milliseconds, "
"is_from_converted_type=true, force_set_converted_type=true)",
- R"({"Type": "Timestamp", "isAdjustedToUTC": false, "timeUnit":
"milliseconds", )"
- R"("is_from_converted_type": true, "force_set_converted_type": true})"},
+
R"({"Type":"Timestamp","isAdjustedToUTC":false,"timeUnit":"milliseconds",)"
+ R"("is_from_converted_type":true,"force_set_converted_type":true})"},
{LogicalType::Timestamp(false, LogicalType::TimeUnit::MICROS),
"Timestamp(isAdjustedToUTC=false, timeUnit=microseconds, "
"is_from_converted_type=false, force_set_converted_type=false)",
- R"({"Type": "Timestamp", "isAdjustedToUTC": false, "timeUnit":
"microseconds", )"
- R"("is_from_converted_type": false, "force_set_converted_type":
false})"},
+
R"({"Type":"Timestamp","isAdjustedToUTC":false,"timeUnit":"microseconds",)"
+ R"("is_from_converted_type":false,"force_set_converted_type":false})"},
{LogicalType::Timestamp(false, LogicalType::TimeUnit::NANOS),
"Timestamp(isAdjustedToUTC=false, timeUnit=nanoseconds, "
"is_from_converted_type=false, force_set_converted_type=false)",
- R"({"Type": "Timestamp", "isAdjustedToUTC": false, "timeUnit":
"nanoseconds", )"
- R"("is_from_converted_type": false, "force_set_converted_type":
false})"},
- {LogicalType::Interval(), "Interval", R"({"Type": "Interval"})"},
+
R"({"Type":"Timestamp","isAdjustedToUTC":false,"timeUnit":"nanoseconds",)"
+ R"("is_from_converted_type":false,"force_set_converted_type":false})"},
+ {LogicalType::Interval(), "Interval", R"({"Type":"Interval"})"},
{LogicalType::Int(8, false), "Int(bitWidth=8, isSigned=false)",
- R"({"Type": "Int", "bitWidth": 8, "isSigned": false})"},
+ R"({"Type":"Int","bitWidth":8,"isSigned":false})"},
{LogicalType::Int(16, false), "Int(bitWidth=16, isSigned=false)",
- R"({"Type": "Int", "bitWidth": 16, "isSigned": false})"},
+ R"({"Type":"Int","bitWidth":16,"isSigned":false})"},
{LogicalType::Int(32, false), "Int(bitWidth=32, isSigned=false)",
- R"({"Type": "Int", "bitWidth": 32, "isSigned": false})"},
+ R"({"Type":"Int","bitWidth":32,"isSigned":false})"},
{LogicalType::Int(64, false), "Int(bitWidth=64, isSigned=false)",
- R"({"Type": "Int", "bitWidth": 64, "isSigned": false})"},
+ R"({"Type":"Int","bitWidth":64,"isSigned":false})"},
{LogicalType::Int(8, true), "Int(bitWidth=8, isSigned=true)",
- R"({"Type": "Int", "bitWidth": 8, "isSigned": true})"},
+ R"({"Type":"Int","bitWidth":8,"isSigned":true})"},
{LogicalType::Int(16, true), "Int(bitWidth=16, isSigned=true)",
- R"({"Type": "Int", "bitWidth": 16, "isSigned": true})"},
+ R"({"Type":"Int","bitWidth":16,"isSigned":true})"},
{LogicalType::Int(32, true), "Int(bitWidth=32, isSigned=true)",
- R"({"Type": "Int", "bitWidth": 32, "isSigned": true})"},
+ R"({"Type":"Int","bitWidth":32,"isSigned":true})"},
{LogicalType::Int(64, true), "Int(bitWidth=64, isSigned=true)",
- R"({"Type": "Int", "bitWidth": 64, "isSigned": true})"},
- {LogicalType::Null(), "Null", R"({"Type": "Null"})"},
- {LogicalType::JSON(), "JSON", R"({"Type": "JSON"})"},
- {LogicalType::BSON(), "BSON", R"({"Type": "BSON"})"},
- {LogicalType::UUID(), "UUID", R"({"Type": "UUID"})"},
- {LogicalType::Float16(), "Float16", R"({"Type": "Float16"})"},
- {LogicalType::Geometry(), "Geometry(crs=)", R"({"Type": "Geometry"})"},
+ R"({"Type":"Int","bitWidth":64,"isSigned":true})"},
+ {LogicalType::Null(), "Null", R"({"Type":"Null"})"},
+ {LogicalType::JSON(), "JSON", R"({"Type":"JSON"})"},
+ {LogicalType::BSON(), "BSON", R"({"Type":"BSON"})"},
+ {LogicalType::UUID(), "UUID", R"({"Type":"UUID"})"},
+ {LogicalType::Float16(), "Float16", R"({"Type":"Float16"})"},
+ {LogicalType::Geometry(), "Geometry(crs=)", R"({"Type":"Geometry"})"},
{LogicalType::Geometry("srid:1234"), "Geometry(crs=srid:1234)",
- R"({"Type": "Geometry", "crs": "srid:1234"})"},
+ R"({"Type":"Geometry","crs":"srid:1234"})"},
{LogicalType::Geometry(R"(crs with "quotes" and \backslashes\)"),
R"(Geometry(crs=crs with "quotes" and \backslashes\))",
- R"({"Type": "Geometry", "crs": "crs with \"quotes\" and
\\backslashes\\"})"},
+ R"({"Type":"Geometry","crs":"crs with \"quotes\" and
\\backslashes\\"})"},
{LogicalType::Geometry("crs with control characters \u0001 and \u001f"),
"Geometry(crs=crs with control characters \u0001 and \u001f)",
- R"({"Type": "Geometry", "crs": "crs with control characters \u0001 and
\u001f"})"},
+ R"({"Type":"Geometry","crs":"crs with control characters \u0001 and
\u001f"})"},
{LogicalType::Geography(), "Geography(crs=, algorithm=spherical)",
- R"({"Type": "Geography"})"},
+ R"({"Type":"Geography"})"},
{LogicalType::Geography("srid:1234",
LogicalType::EdgeInterpolationAlgorithm::SPHERICAL),
"Geography(crs=srid:1234, algorithm=spherical)",
- R"({"Type": "Geography", "crs": "srid:1234"})"},
+ R"({"Type":"Geography","crs":"srid:1234"})"},
{LogicalType::Geography("srid:1234",
LogicalType::EdgeInterpolationAlgorithm::VINCENTY),
"Geography(crs=srid:1234, algorithm=vincenty)",
- R"({"Type": "Geography", "crs": "srid:1234", "algorithm":
"vincenty"})"},
+ R"({"Type":"Geography","crs":"srid:1234","algorithm":"vincenty"})"},
{LogicalType::Geography("srid:1234",
LogicalType::EdgeInterpolationAlgorithm::THOMAS),
"Geography(crs=srid:1234, algorithm=thomas)",
- R"({"Type": "Geography", "crs": "srid:1234", "algorithm": "thomas"})"},
+ R"({"Type":"Geography","crs":"srid:1234","algorithm":"thomas"})"},
{LogicalType::Geography("srid:1234",
LogicalType::EdgeInterpolationAlgorithm::ANDOYER),
"Geography(crs=srid:1234, algorithm=andoyer)",
- R"({"Type": "Geography", "crs": "srid:1234", "algorithm": "andoyer"})"},
+ R"({"Type":"Geography","crs":"srid:1234","algorithm":"andoyer"})"},
{LogicalType::Geography("srid:1234",
LogicalType::EdgeInterpolationAlgorithm::KARNEY),
"Geography(crs=srid:1234, algorithm=karney)",
- R"({"Type": "Geography", "crs": "srid:1234", "algorithm": "karney"})"},
- {LogicalType::Variant(), "Variant(1)", R"({"Type": "Variant",
"SpecVersion": 1})"},
- {LogicalType::Variant(2), "Variant(2)", R"({"Type": "Variant",
"SpecVersion": 2})"},
- {LogicalType::None(), "None", R"({"Type": "None"})"},
+ R"({"Type":"Geography","crs":"srid:1234","algorithm":"karney"})"},
+ {LogicalType::Variant(), "Variant(1)",
R"({"Type":"Variant","SpecVersion":1})"},
+ {LogicalType::Variant(2), "Variant(2)",
R"({"Type":"Variant","SpecVersion":2})"},
+ {LogicalType::None(), "None", R"({"Type":"None"})"},
};
for (const ExpectedRepresentation& c : cases) {
diff --git a/cpp/src/parquet/types.cc b/cpp/src/parquet/types.cc
index cc3199f367a..534c79201c0 100644
--- a/cpp/src/parquet/types.cc
+++ b/cpp/src/parquet/types.cc
@@ -717,9 +717,12 @@ class LogicalType::Impl {
}
virtual std::string ToJSON() const {
- std::stringstream json;
- json << R"({"Type": ")" << ToString() << R"("})";
- return json.str();
+ ::arrow::json::JsonWriter writer;
+ writer.StartObject();
+ writer.StringField("Type", ToString());
+ writer.EndObject();
+ PARQUET_ASSIGN_OR_THROW(std::string_view json, writer.GetString());
+ return std::string(json);
}
virtual format::LogicalType ToThrift() const {
@@ -1171,10 +1174,14 @@ std::string LogicalType::Impl::Decimal::ToString()
const {
}
std::string LogicalType::Impl::Decimal::ToJSON() const {
- std::stringstream json;
- json << R"({"Type": "Decimal", "precision": )" << precision_ << R"(,
"scale": )"
- << scale_ << "}";
- return json.str();
+ ::arrow::json::JsonWriter writer;
+ writer.StartObject();
+ writer.StringField("Type", "Decimal");
+ writer.IntField("precision", precision_);
+ writer.IntField("scale", scale_);
+ writer.EndObject();
+ PARQUET_ASSIGN_OR_THROW(std::string_view json, writer.GetString());
+ return std::string(json);
}
format::LogicalType LogicalType::Impl::Decimal::ToThrift() const {
@@ -1316,10 +1323,14 @@ std::string LogicalType::Impl::Time::ToString() const {
}
std::string LogicalType::Impl::Time::ToJSON() const {
- std::stringstream json;
- json << R"({"Type": "Time", "isAdjustedToUTC": )" << std::boolalpha <<
adjusted_
- << R"(, "timeUnit": ")" << time_unit_string(unit_) << R"("})";
- return json.str();
+ ::arrow::json::JsonWriter writer;
+ writer.StartObject();
+ writer.StringField("Type", "Time");
+ writer.BoolField("isAdjustedToUTC", adjusted_);
+ writer.StringField("timeUnit", time_unit_string(unit_));
+ writer.EndObject();
+ PARQUET_ASSIGN_OR_THROW(std::string_view json, writer.GetString());
+ return std::string(json);
}
format::LogicalType LogicalType::Impl::Time::ToThrift() const {
@@ -1463,12 +1474,16 @@ std::string LogicalType::Impl::Timestamp::ToString()
const {
}
std::string LogicalType::Impl::Timestamp::ToJSON() const {
- std::stringstream json;
- json << R"({"Type": "Timestamp", "isAdjustedToUTC": )" << std::boolalpha <<
adjusted_
- << R"(, "timeUnit": ")" << time_unit_string(unit_) << R"(")"
- << R"(, "is_from_converted_type": )" << is_from_converted_type_
- << R"(, "force_set_converted_type": )" << force_set_converted_type_ <<
R"(})";
- return json.str();
+ ::arrow::json::JsonWriter writer;
+ writer.StartObject();
+ writer.StringField("Type", "Timestamp");
+ writer.BoolField("isAdjustedToUTC", adjusted_);
+ writer.StringField("timeUnit", time_unit_string(unit_));
+ writer.BoolField("is_from_converted_type", is_from_converted_type_);
+ writer.BoolField("force_set_converted_type", force_set_converted_type_);
+ writer.EndObject();
+ PARQUET_ASSIGN_OR_THROW(std::string_view json, writer.GetString());
+ return std::string(json);
}
format::LogicalType LogicalType::Impl::Timestamp::ToThrift() const {
@@ -1653,10 +1668,14 @@ std::string LogicalType::Impl::Int::ToString() const {
}
std::string LogicalType::Impl::Int::ToJSON() const {
- std::stringstream json;
- json << R"({"Type": "Int", "bitWidth": )" << width_ << R"(, "isSigned": )"
- << std::boolalpha << signed_ << "}";
- return json.str();
+ ::arrow::json::JsonWriter writer;
+ writer.StartObject();
+ writer.StringField("Type", "Int");
+ writer.IntField("bitWidth", width_);
+ writer.BoolField("isSigned", signed_);
+ writer.EndObject();
+ PARQUET_ASSIGN_OR_THROW(std::string_view json, writer.GetString());
+ return std::string(json);
}
format::LogicalType LogicalType::Impl::Int::ToThrift() const {
@@ -1778,16 +1797,6 @@ class LogicalType::Impl::Float16 final : public
LogicalType::Impl::Incompatible,
GENERATE_MAKE(Float16)
-namespace {
-void WriteCrsKeyAndValue(const std::string_view crs, std::ostream& json) {
- // There is no restriction on the crs value here, and it may contain quotes
- // or backslashes that would result in invalid JSON if unescaped.
- ::arrow::json::JsonWriter writer;
- writer.String(crs);
- json << R"(, "crs": )" << writer.GetString().ValueUnsafe();
-}
-} // namespace
-
class LogicalType::Impl::Geometry final : public
LogicalType::Impl::Incompatible,
public
LogicalType::Impl::SimpleApplicable {
public:
@@ -1816,15 +1825,15 @@ std::string LogicalType::Impl::Geometry::ToString()
const {
}
std::string LogicalType::Impl::Geometry::ToJSON() const {
- std::stringstream json;
- json << R"({"Type": "Geometry")";
-
+ ::arrow::json::JsonWriter writer;
+ writer.StartObject();
+ writer.StringField("Type", "Geometry");
if (!crs_.empty()) {
- WriteCrsKeyAndValue(crs_, json);
+ writer.StringField("crs", crs_);
}
-
- json << "}";
- return json.str();
+ writer.EndObject();
+ PARQUET_ASSIGN_OR_THROW(std::string_view json, writer.GetString());
+ return std::string(json);
}
format::LogicalType LogicalType::Impl::Geometry::ToThrift() const {
@@ -1907,19 +1916,18 @@ std::string LogicalType::Impl::Geography::ToString()
const {
}
std::string LogicalType::Impl::Geography::ToJSON() const {
- std::stringstream json;
- json << R"({"Type": "Geography")";
-
+ ::arrow::json::JsonWriter writer;
+ writer.StartObject();
+ writer.StringField("Type", "Geography");
if (!crs_.empty()) {
- WriteCrsKeyAndValue(crs_, json);
+ writer.StringField("crs", crs_);
}
-
if (algorithm_ != LogicalType::EdgeInterpolationAlgorithm::SPHERICAL) {
- json << R"(, "algorithm": ")" << algorithm_name() << R"(")";
+ writer.StringField("algorithm", algorithm_name());
}
-
- json << "}";
- return json.str();
+ writer.EndObject();
+ PARQUET_ASSIGN_OR_THROW(std::string_view json, writer.GetString());
+ return std::string(json);
}
format::LogicalType LogicalType::Impl::Geography::ToThrift() const {
@@ -2000,11 +2008,13 @@ std::string LogicalType::Impl::Variant::ToString()
const {
}
std::string LogicalType::Impl::Variant::ToJSON() const {
- std::stringstream json;
- json << R"({"Type": "Variant", "SpecVersion": )" <<
static_cast<int>(spec_version_)
- << "}";
-
- return json.str();
+ ::arrow::json::JsonWriter writer;
+ writer.StartObject();
+ writer.StringField("Type", "Variant");
+ writer.IntField("SpecVersion", static_cast<int>(spec_version_));
+ writer.EndObject();
+ PARQUET_ASSIGN_OR_THROW(std::string_view json, writer.GetString());
+ return std::string(json);
}
format::LogicalType LogicalType::Impl::Variant::ToThrift() const {