Current section
Files
Jump to
Current section
Files
c_src/duckdb/src/main/appender.cpp
#include "duckdb/main/appender.hpp"
#include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp"
#include "duckdb/common/exception.hpp"
#include "duckdb/main/connection.hpp"
#include "duckdb/main/client_context.hpp"
#include "duckdb/main/database.hpp"
#include "duckdb/storage/data_table.hpp"
#include "duckdb/common/string_util.hpp"
#include "duckdb/common/operator/cast_operators.hpp"
#include "duckdb/common/operator/string_cast.hpp"
namespace duckdb {
Appender::Appender(Connection &con, const string &schema_name, const string &table_name)
: context(con.context), column(0) {
description = con.TableInfo(schema_name, table_name);
if (!description) {
// table could not be found
throw CatalogException(StringUtil::Format("Table \"%s.%s\" could not be found", schema_name, table_name));
}
for (auto &column : description->columns) {
types.push_back(column.type);
}
InitializeChunk();
}
Appender::Appender(Connection &con, const string &table_name) : Appender(con, DEFAULT_SCHEMA, table_name) {
}
Appender::~Appender() {
if (std::uncaught_exception()) {
return;
}
// flush any remaining chunks, but only if we are not cleaning up the appender as part of an exception stack unwind
// wrapped in a try/catch because Close() can throw if the table was dropped in the meantime
try {
Close();
} catch (...) {
}
}
void Appender::InitializeChunk() {
chunk = make_unique<DataChunk>();
chunk->Initialize(types);
}
void Appender::BeginRow() {
}
void Appender::EndRow() {
// check that all rows have been appended to
if (column != chunk->ColumnCount()) {
throw InvalidInputException("Call to EndRow before all rows have been appended to!");
}
column = 0;
chunk->SetCardinality(chunk->size() + 1);
if (chunk->size() >= STANDARD_VECTOR_SIZE) {
FlushChunk();
}
}
template <class SRC, class DST>
void Appender::AppendValueInternal(Vector &col, SRC input) {
FlatVector::GetData<DST>(col)[chunk->size()] = Cast::Operation<SRC, DST>(input);
}
template <class T>
void Appender::AppendValueInternal(T input) {
if (column >= types.size()) {
throw InvalidInputException("Too many appends for chunk!");
}
auto &col = chunk->data[column];
switch (col.GetType().InternalType()) {
case PhysicalType::BOOL:
AppendValueInternal<T, bool>(col, input);
break;
case PhysicalType::UINT8:
AppendValueInternal<T, uint8_t>(col, input);
break;
case PhysicalType::INT8:
AppendValueInternal<T, int8_t>(col, input);
break;
case PhysicalType::UINT16:
AppendValueInternal<T, uint16_t>(col, input);
break;
case PhysicalType::INT16:
AppendValueInternal<T, int16_t>(col, input);
break;
case PhysicalType::UINT32:
AppendValueInternal<T, uint32_t>(col, input);
break;
case PhysicalType::INT32:
AppendValueInternal<T, int32_t>(col, input);
break;
case PhysicalType::UINT64:
AppendValueInternal<T, uint64_t>(col, input);
break;
case PhysicalType::INT64:
AppendValueInternal<T, int64_t>(col, input);
break;
case PhysicalType::INT128:
AppendValueInternal<T, hugeint_t>(col, input);
break;
case PhysicalType::FLOAT:
AppendValueInternal<T, float>(col, input);
break;
case PhysicalType::DOUBLE:
AppendValueInternal<T, double>(col, input);
break;
case PhysicalType::VARCHAR:
FlatVector::GetData<string_t>(col)[chunk->size()] = StringCast::Operation<T>(input, col);
break;
default:
AppendValue(Value::CreateValue<T>(input));
return;
}
column++;
}
template <>
void Appender::Append(bool value) {
AppendValueInternal<bool>(value);
}
template <>
void Appender::Append(int8_t value) {
AppendValueInternal<int8_t>(value);
}
template <>
void Appender::Append(int16_t value) {
AppendValueInternal<int16_t>(value);
}
template <>
void Appender::Append(int32_t value) {
AppendValueInternal<int32_t>(value);
}
template <>
void Appender::Append(int64_t value) {
AppendValueInternal<int64_t>(value);
}
template <>
void Appender::Append(hugeint_t value) {
AppendValueInternal<hugeint_t>(value);
}
template <>
void Appender::Append(uint8_t value) {
AppendValueInternal<uint8_t>(value);
}
template <>
void Appender::Append(uint16_t value) {
AppendValueInternal<uint16_t>(value);
}
template <>
void Appender::Append(uint32_t value) {
AppendValueInternal<uint32_t>(value);
}
template <>
void Appender::Append(uint64_t value) {
AppendValueInternal<uint64_t>(value);
}
template <>
void Appender::Append(const char *value) {
AppendValueInternal<string_t>(string_t(value));
}
void Appender::Append(const char *value, uint32_t length) {
AppendValueInternal<string_t>(string_t(value, length));
}
template <>
void Appender::Append(string_t value) {
AppendValueInternal<string_t>(value);
}
template <>
void Appender::Append(float value) {
if (!Value::FloatIsValid(value)) {
throw InvalidInputException("Float value is out of range!");
}
AppendValueInternal<float>(value);
}
template <>
void Appender::Append(double value) {
if (!Value::DoubleIsValid(value)) {
throw InvalidInputException("Double value is out of range!");
}
AppendValueInternal<double>(value);
}
template <>
void Appender::Append(date_t value) {
AppendValueInternal<int32_t>(value.days);
}
template <>
void Appender::Append(dtime_t value) {
AppendValueInternal<int64_t>(value.micros);
}
template <>
void Appender::Append(timestamp_t value) {
AppendValueInternal<int64_t>(value.value);
}
template <>
void Appender::Append(interval_t value) {
AppendValueInternal<interval_t>(value);
}
template <>
void Appender::Append(Value value) { // NOLINT: template shtuff
if (column >= chunk->ColumnCount()) {
throw InvalidInputException("Too many appends for chunk!");
}
AppendValue(value);
}
template <>
void Appender::Append(std::nullptr_t value) {
if (column >= chunk->ColumnCount()) {
throw InvalidInputException("Too many appends for chunk!");
}
auto &col = chunk->data[column++];
FlatVector::SetNull(col, chunk->size(), true);
}
void Appender::AppendValue(const Value &value) {
chunk->SetValue(column, chunk->size(), value);
column++;
}
void Appender::FlushChunk() {
if (chunk->size() == 0) {
return;
}
collection.Append(move(chunk));
InitializeChunk();
if (collection.ChunkCount() >= FLUSH_COUNT) {
Flush();
}
}
void Appender::Flush() {
// check that all vectors have the same length before appending
if (column != 0) {
throw InvalidInputException("Failed to Flush appender: incomplete append to row!");
}
FlushChunk();
if (collection.Count() == 0) {
return;
}
context->Append(*description, collection);
collection.Reset();
column = 0;
}
void Appender::Close() {
if (column == 0 || column == types.size()) {
Flush();
}
}
} // namespace duckdb