Skip to content
Closed
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
12 changes: 12 additions & 0 deletions clickhouse/columns/array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ void ColumnArray::AppendAsColumn(ColumnRef array) {
data_->Append(array);
}

void ColumnArray::AppendAsColumnWithMove(ColumnRef array)
{
if (!data_->Type()->IsEqual(array->Type())) {
throw ValidationError(
"can't append column of type " + array->Type()->GetName() + " "
"to column type " + data_->Type()->GetName());
}

AddOffset(array->Size());
data_->AppendWithMove(array);
}

ColumnRef ColumnArray::GetAsColumn(size_t n) const {
if (n >= Size())
throw ValidationError("Index is out ouf bounds: " + std::to_string(n));
Expand Down
4 changes: 4 additions & 0 deletions clickhouse/columns/array.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ class ColumnArray : public Column {
/// Converts input column to array and appends as one row to the current column.
void AppendAsColumn(ColumnRef array);

/// Converts input column to array and appends as one row to the current column.
/// Move the column data if possible, without memory copy.
void AppendAsColumnWithMove(ColumnRef array);

/// Convets array at pos n to column.
/// Type of element of result column same as type of array element.
ColumnRef GetAsColumn(size_t n) const;
Expand Down
6 changes: 6 additions & 0 deletions clickhouse/columns/column.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ class Column : public std::enable_shared_from_this<Column> {
/// Appends content of given column to the end of current one.
virtual void Append(ColumnRef column) = 0;

/// Appends content of given column to the end of current one.
/// Move the column data if possible, without memory copy.
virtual void AppendWithMove(ColumnRef column) {
Append(column);
}

/// Template method to load column data from input stream. It'll call LoadPrefix and LoadBody.
/// Should be called only once from the client. Derived classes should not call it.
bool Load(InputStream* input, size_t rows);
Expand Down
30 changes: 24 additions & 6 deletions clickhouse/columns/string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,12 +203,7 @@ void ColumnString::Append(std::string_view str) {
}

void ColumnString::Append(const char* str) {
auto len = strlen(str);
if (blocks_.size() == 0 || blocks_.back().GetAvailable() < len) {
blocks_.emplace_back(std::max(DEFAULT_BLOCK_SIZE, len));
}

items_.emplace_back(blocks_.back().AppendUnsafe(str));
Append(std::string_view{ str ,strlen(str) });
}

void ColumnString::Append(std::string&& steal_value) {
Expand Down Expand Up @@ -255,6 +250,29 @@ void ColumnString::Append(ColumnRef column) {
}
}

void ColumnString::AppendWithMove(ColumnRef column) {
if (auto col = column->As<ColumnString>()) {
for (auto&& block : col->blocks_) {
blocks_.emplace_back(std::move(block));
}
col->blocks_.clear();
col->blocks_.shrink_to_fit();

for (auto&& ad : col->append_data_) {
append_data_.emplace_back(std::move(ad));
}
col->append_data_.clear();
col->append_data_.shrink_to_fit();

items_.reserve(items_.size() + col->Size());
for (auto&& item : col->items_) {
items_.emplace_back(std::move(item));
}
col->items_.clear();
col->items_.shrink_to_fit();
}
}

bool ColumnString::LoadBody(InputStream* input, size_t rows) {
items_.clear();
blocks_.clear();
Expand Down
4 changes: 4 additions & 0 deletions clickhouse/columns/string.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ class ColumnString : public Column {
/// Appends content of given column to the end of current one.
void Append(ColumnRef column) override;

/// Appends content of given column to the end of current one.
/// Move the column data if possible, without memory copy.
void AppendWithMove(ColumnRef column) override;

/// Loads column data from input stream.
bool LoadBody(InputStream* input, size_t rows) override;

Expand Down
45 changes: 45 additions & 0 deletions tests/simple/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,50 @@ inline void ArrayExample(Client& client) {
client.Execute("DROP TEMPORARY TABLE test_array");
}

inline void StringArrayExample(Client& client) {
{
Block b;

/// Create a table.
client.Execute("CREATE TEMPORARY TABLE IF NOT EXISTS test_string_array (arr Array(String))");

auto arr = std::make_shared<ColumnArray>(std::make_shared<ColumnString>());

auto id = std::make_shared<ColumnString>();
std::string long_time = "1234567890qwertyui";
id->AppendNoManagedLifetime(long_time);
id->Append("hellohellohellohellohello");
arr->AppendAsColumnWithMove(id);

id->Append("worldworldworldworldworldworld");
arr->AppendAsColumnWithMove(id);

id->Append("heiheiheiheiheiheiheiheiheihei");
arr->AppendAsColumnWithMove(id);

id->Append("hahahahahahahahahahahahahahahaha");
arr->AppendAsColumnWithMove(id);

b.AppendColumn("arr", arr);
client.Insert("test_string_array", b);

client.Select("SELECT arr FROM test_string_array", [](const Block& block)
{
for (size_t c = 0; c < block.GetRowCount(); ++c) {
auto col = block[0]->As<ColumnArray>()->GetAsColumn(c);
for (size_t i = 0; i < col->Size(); ++i) {
std::cerr << (*col->As<ColumnString>())[i] << " ";
}
std::cerr << std::endl;
}
}
);

/// Delete table.
client.Execute("DROP TABLE test_string_array");
}
}

inline void MultiArrayExample(Client& client) {
Block b;

Expand Down Expand Up @@ -477,6 +521,7 @@ inline void IPExample(Client &client) {
}

static void RunTests(Client& client) {
StringArrayExample(client);
ArrayExample(client);
CancelableExample(client);
DateExample(client);
Expand Down
26 changes: 26 additions & 0 deletions ut/column_array_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,32 @@ TEST(ColumnArray, Append) {
ASSERT_EQ(col->As<ColumnUInt64>()->At(1), 3u);
}

TEST(ColumnArray, AppendWithMove) {
auto arr = std::make_shared<ColumnArray>(std::make_shared<ColumnString>());

std::string str1 = "hello clickhouse-server";
std::string str2 = "hello clickhouse-client";

auto id = std::make_shared<ColumnString>();
std::string expect1 = str1;
id->Append(std::move(str1));
arr->AppendAsColumnWithMove(id);

std::string expect2 = str2;
id->Append(std::move(str2));
arr->AppendAsColumnWithMove(id);

ASSERT_EQ(arr->Size(), 2u);

auto col = arr->GetAsColumn(0);
ASSERT_EQ(col->Size(), 1u);
ASSERT_EQ(col->As<ColumnString>()->At(0), expect1);

col = arr->GetAsColumn(1);
ASSERT_EQ(col->Size(), 1u);
ASSERT_EQ(col->As<ColumnString>()->At(0), expect2);
}

TEST(ColumnArray, ArrayOfDecimal) {
auto column = std::make_shared<clickhouse::ColumnDecimal>(18, 10);
auto array = std::make_shared<clickhouse::ColumnArray>(column->CloneEmpty());
Expand Down