Skip to content

Commit dca7c0a

Browse files
authored
[BugFix] Support inserting array type to hive table with CSV format (backport #67355) (#69089)
Signed-off-by: GavinMar <yangguansuo@starrocks.com>
1 parent 4fc5018 commit dca7c0a

7 files changed

Lines changed: 180 additions & 4 deletions

File tree

be/src/formats/csv/array_converter.cpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,33 @@ Status ArrayConverter::write_string(OutputStream* os, const Column& column, size
2727

2828
auto begin = offsets.get_data()[row_num];
2929
auto end = offsets.get_data()[row_num + 1];
30+
char collection_delim = ',';
31+
bool is_hive_format = false;
32+
33+
if (options.array_format_type == ArrayFormatType::kHive) {
34+
collection_delim = HiveTextArrayReader::get_collection_delimiter(options.array_hive_collection_delimiter,
35+
options.array_hive_mapkey_delimiter,
36+
options.array_hive_nested_level);
37+
is_hive_format = true;
38+
} else {
39+
RETURN_IF_ERROR(os->write('['));
40+
}
3041

31-
RETURN_IF_ERROR(os->write('['));
3242
for (auto i = begin; i < end; i++) {
33-
RETURN_IF_ERROR(_element_converter->write_quoted_string(os, elements, i, options));
43+
if (is_hive_format) {
44+
Options sub_options = options;
45+
sub_options.array_hive_nested_level++;
46+
// In Hive, we should use write_string() instead of write_quoted_string
47+
RETURN_IF_ERROR(_element_converter->write_string(os, elements, i, sub_options));
48+
} else {
49+
RETURN_IF_ERROR(_element_converter->write_quoted_string(os, elements, i, options));
50+
}
3451
if (i + 1 < end) {
35-
RETURN_IF_ERROR(os->write(','));
52+
RETURN_IF_ERROR(os->write(collection_delim));
3653
}
3754
}
55+
RETURN_IF(is_hive_format, Status::OK());
56+
3857
return os->write(']');
3958
}
4059

be/src/formats/csv/csv_file_writer.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "common/http/content_type.h"
2121
#include "csv_escape.h"
22+
#include "exec/hdfs_scanner_text.h"
2223
#include "formats/utils.h"
2324
#include "output_stream_file.h"
2425
#include "runtime/current_thread.h"
@@ -55,6 +56,17 @@ Status CSVFileWriter::init() {
5556
}
5657
_column_converters.emplace_back(std::move(nullable_conv), csv::get_converter(type, false));
5758
}
59+
_converter_options = std::make_shared<csv::Converter::Options>();
60+
if (_writer_options->is_hive) {
61+
_converter_options->is_hive = true;
62+
_converter_options->array_format_type = csv::ArrayFormatType::kHive;
63+
_converter_options->array_hive_collection_delimiter = _writer_options->collection_delim.empty()
64+
? DEFAULT_COLLECTION_DELIM.front()
65+
: _writer_options->collection_delim.front();
66+
_converter_options->array_hive_mapkey_delimiter = _writer_options->mapkey_delim.empty()
67+
? DEFAULT_MAPKEY_DELIM.front()
68+
: _writer_options->mapkey_delim.front();
69+
}
5870
return Status::OK();
5971
}
6072

@@ -115,7 +127,7 @@ Status CSVFileWriter::write(Chunk* chunk) {
115127
} else {
116128
converter = _column_converters[c].second.get();
117129
}
118-
RETURN_IF_ERROR(converter->write_string(_output_stream.get(), *columns[c], r, {}));
130+
RETURN_IF_ERROR(converter->write_string(_output_stream.get(), *columns[c], r, *_converter_options));
119131
if (c + 1 != columns.size()) {
120132
RETURN_IF_ERROR(_output_stream->write(_writer_options->column_terminated_by));
121133
}
@@ -174,6 +186,15 @@ Status CSVFileWriterFactory::init() {
174186
if (_options.contains(CSVWriterOptions::INCLUDE_HEADER)) {
175187
_parsed_options->include_header = boost::iequals(_options[CSVWriterOptions::INCLUDE_HEADER], "true");
176188
}
189+
if (_options.contains(CSVWriterOptions::COLLECTION_DELIM)) {
190+
_parsed_options->collection_delim = _options[CSVWriterOptions::COLLECTION_DELIM];
191+
}
192+
if (_options.contains(CSVWriterOptions::MAPKEY_DELIM)) {
193+
_parsed_options->mapkey_delim = _options[CSVWriterOptions::MAPKEY_DELIM];
194+
}
195+
if (_options.contains(CSVWriterOptions::IS_HIVE)) {
196+
_parsed_options->is_hive = _options[CSVWriterOptions::IS_HIVE] == "true";
197+
}
177198
return Status::OK();
178199
}
179200

be/src/formats/csv/csv_file_writer.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,16 @@ struct CSVWriterOptions : FileWriterOptions {
2424
std::string column_terminated_by = ",";
2525
std::string line_terminated_by = "\n";
2626
bool include_header = false;
27+
std::string collection_delim = ",";
28+
std::string mapkey_delim = ",";
29+
bool is_hive = false;
2730

2831
inline static std::string COLUMN_TERMINATED_BY = "column_terminated_by";
2932
inline static std::string LINE_TERMINATED_BY = "line_terminated_by";
3033
inline static std::string INCLUDE_HEADER = "include_header";
34+
inline static std::string COLLECTION_DELIM = "collection_delim";
35+
inline static std::string MAPKEY_DELIM = "mapkey_delim";
36+
inline static std::string IS_HIVE = "is_hive";
3137
};
3238

3339
// The primary purpose of this class is to support hive + csv. Use with caution in other cases.
@@ -59,6 +65,7 @@ class CSVFileWriter final : public FileWriter {
5965
std::vector<std::unique_ptr<ColumnEvaluator>> _column_evaluators;
6066
std::shared_ptr<CSVWriterOptions> _writer_options;
6167
const std::function<void()> _rollback_action;
68+
std::shared_ptr<csv::Converter::Options> _converter_options;
6269

6370
int64_t _num_rows = 0;
6471
bool _header_written = false;

be/src/runtime/hive_table_sink.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ Status HiveTableSink::decompose_to_pipeline(pipeline::OpFactories prev_operators
8282
DCHECK(boost::iequals(t_hive_sink.file_format, formats::TEXTFILE));
8383
sink_ctx->options[formats::CSVWriterOptions::COLUMN_TERMINATED_BY] = DEFAULT_FIELD_DELIM;
8484
sink_ctx->options[formats::CSVWriterOptions::LINE_TERMINATED_BY] = DEFAULT_LINE_DELIM;
85+
sink_ctx->options[formats::CSVWriterOptions::COLLECTION_DELIM] = DEFAULT_COLLECTION_DELIM;
86+
sink_ctx->options[formats::CSVWriterOptions::MAPKEY_DELIM] = DEFAULT_MAPKEY_DELIM;
87+
sink_ctx->options[formats::CSVWriterOptions::IS_HIVE] = "true";
8588

8689
// use customized value if specified
8790
if (t_hive_sink.text_file_desc.__isset.field_delim) {
@@ -90,6 +93,13 @@ Status HiveTableSink::decompose_to_pipeline(pipeline::OpFactories prev_operators
9093
if (t_hive_sink.text_file_desc.__isset.line_delim) {
9194
sink_ctx->options[formats::CSVWriterOptions::LINE_TERMINATED_BY] = t_hive_sink.text_file_desc.line_delim;
9295
}
96+
if (t_hive_sink.text_file_desc.__isset.collection_delim) {
97+
sink_ctx->options[formats::CSVWriterOptions::COLLECTION_DELIM] =
98+
t_hive_sink.text_file_desc.collection_delim;
99+
}
100+
if (t_hive_sink.text_file_desc.__isset.mapkey_delim) {
101+
sink_ctx->options[formats::CSVWriterOptions::MAPKEY_DELIM] = t_hive_sink.text_file_desc.mapkey_delim;
102+
}
93103
}
94104
sink_ctx->fragment_context = fragment_ctx;
95105

be/test/formats/csv/csv_file_writer_test.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,74 @@ TEST_F(CSVFileWriterTest, TestWriteArrayWithNull) {
569569
ASSERT_EQ(content, expect);
570570
}
571571

572+
TEST_F(CSVFileWriterTest, TestWriteHiveArray) {
573+
// type_descs: ARRAY<INT>
574+
std::vector<TypeDescriptor> type_descs;
575+
auto type_int = TypeDescriptor::from_logical_type(TYPE_INT);
576+
auto type_int_array = TypeDescriptor::from_logical_type(TYPE_ARRAY);
577+
type_int_array.children.push_back(type_int);
578+
type_descs.push_back(type_int_array);
579+
580+
auto column_names = _make_type_names(type_descs);
581+
auto maybe_output_file = _fs.new_writable_file(_file_path);
582+
EXPECT_OK(maybe_output_file.status());
583+
auto output_file = std::move(maybe_output_file.value());
584+
auto output_stream = std::make_unique<csv::OutputStreamFile>(std::move(output_file), 1024);
585+
auto column_evaluators = ColumnSlotIdEvaluator::from_types(type_descs);
586+
auto writer_options = std::make_shared<formats::CSVWriterOptions>();
587+
writer_options->is_hive = true;
588+
writer_options->collection_delim = '\002';
589+
auto writer =
590+
std::make_unique<formats::CSVFileWriter>(_file_path, std::move(output_stream), column_names, type_descs,
591+
std::move(column_evaluators), writer_options, []() {});
592+
ASSERT_OK(writer->init());
593+
594+
auto chunk = std::make_shared<Chunk>();
595+
{
596+
// Create ARRAY<INT> column with data: [1,2,3], [4,5], [], [6]
597+
auto elements_data = Int32Column::create();
598+
auto offsets = UInt32Column::create();
599+
auto null_column = UInt8Column::create();
600+
601+
// row 0: [1,2,3]
602+
elements_data->append(1);
603+
elements_data->append(2);
604+
elements_data->append(3);
605+
offsets->append(0);
606+
607+
// row 1: [4,5]
608+
elements_data->append(4);
609+
elements_data->append(5);
610+
offsets->append(3);
611+
612+
// row 2: [] (empty array)
613+
offsets->append(5);
614+
615+
// row 3: [6]
616+
elements_data->append(6);
617+
offsets->append(5);
618+
offsets->append(6);
619+
620+
auto array_column =
621+
ArrayColumn::create(NullableColumn::create(elements_data, UInt8Column::create(6, 0)), offsets);
622+
null_column->append_numbers(std::vector<uint8_t>{0, 0, 0, 0}.data(), 4);
623+
auto nullable_column = NullableColumn::create(std::move(array_column), std::move(null_column));
624+
chunk->append_column(std::move(nullable_column), chunk->num_columns());
625+
}
626+
627+
// write chunk
628+
ASSERT_OK(writer->write(chunk.get()));
629+
auto result = writer->commit();
630+
ASSERT_OK(result.io_status);
631+
ASSERT_EQ(result.file_statistics.record_count, 4);
632+
633+
// verify correctness
634+
std::string content;
635+
ASSERT_OK(_fs.read_file(_file_path, &content));
636+
std::string expect = "1\0022\0023\n4\0025\n\n6\n";
637+
ASSERT_EQ(content, expect);
638+
}
639+
572640
TEST_F(CSVFileWriterTest, TestWriteMap) {
573641
// type_descs
574642
std::vector<TypeDescriptor> type_descs;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
-- name: test_write_csv_array
2+
create external catalog hive_sink_test_${uuid0} PROPERTIES ("type"="hive", "hive.metastore.uris"="${hive_metastore_uris}");
3+
-- result:
4+
-- !result
5+
set catalog hive_sink_test_${uuid0};
6+
-- result:
7+
-- !result
8+
create database hive_db_${uuid0};
9+
-- result:
10+
-- !result
11+
use hive_db_${uuid0};
12+
-- result:
13+
-- !result
14+
create table t1(c1 string, c2 array<integer>) properties("file_format"="textfile");
15+
-- result:
16+
-- !result
17+
insert into t1(c1,c2) values('hello', [123456,0,-128,0]);
18+
-- result:
19+
-- !result
20+
select * from t1;
21+
-- result:
22+
hello [123456,0,-128,0]
23+
-- !result
24+
drop table t1 force;
25+
-- result:
26+
-- !result
27+
drop database hive_db_${uuid0};
28+
-- result:
29+
-- !result
30+
drop catalog hive_sink_test_${uuid0};
31+
-- result:
32+
-- !result
33+
set catalog default_catalog;
34+
-- result:
35+
-- !result
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
-- name: test_write_csv_array
2+
3+
create external catalog hive_sink_test_${uuid0} PROPERTIES ("type"="hive", "hive.metastore.uris"="${hive_metastore_uris}");
4+
set catalog hive_sink_test_${uuid0};
5+
create database hive_db_${uuid0};
6+
use hive_db_${uuid0};
7+
8+
create table t1(c1 string, c2 array<integer>) properties("file_format"="textfile");
9+
insert into t1(c1,c2) values('hello', [123456,0,-128,0]);
10+
11+
select * from t1;
12+
drop table t1 force;
13+
14+
drop database hive_db_${uuid0};
15+
drop catalog hive_sink_test_${uuid0};
16+
set catalog default_catalog;

0 commit comments

Comments
 (0)