Skip to content

Commit

Permalink
Merge pull request #10401 from maiadegraaf/c_api_additions
Browse files Browse the repository at this point in the history
[C-API] Add duckdb_appender_column_type
  • Loading branch information
Mytherin committed Feb 1, 2024
2 parents 7456cef + 9ace475 commit 221a408
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 3 deletions.
19 changes: 19 additions & 0 deletions src/include/duckdb.h
Expand Up @@ -2483,6 +2483,25 @@ Note that the object must be destroyed with `duckdb_appender_destroy`.
DUCKDB_API duckdb_state duckdb_appender_create(duckdb_connection connection, const char *schema, const char *table,
duckdb_appender *out_appender);

/*!
Returns the number of columns in the table that belongs to the appender.
* appender The appender to get the column count from.
* returns: The number of columns in the table.
*/
DUCKDB_API idx_t duckdb_appender_column_count(duckdb_appender appender);

/*!
Returns the type of the column at the specified index.
Note: The resulting type should be destroyed with `duckdb_destroy_logical_type`.
* appender The appender to get the column type from.
* col_idx The index of the column to get the type of.
* returns: The duckdb_logical_type of the column.
*/
DUCKDB_API duckdb_logical_type duckdb_appender_column_type(duckdb_appender appender, idx_t col_idx);

/*!
Returns the error message associated with the given appender.
If the appender has no error message, this returns `nullptr` instead.
Expand Down
11 changes: 9 additions & 2 deletions src/main/appender.cpp
Expand Up @@ -327,8 +327,15 @@ void BaseAppender::AppendValue(const Value &value) {
}

void BaseAppender::AppendDataChunk(DataChunk &chunk) {
if (chunk.GetTypes() != types) {
throw InvalidInputException("Type mismatch in Append DataChunk and the types required for appender");
auto chunk_types = chunk.GetTypes();
if (chunk_types != types) {
for (idx_t i = 0; i < chunk.ColumnCount(); i++) {
if (chunk.data[i].GetType() != types[i]) {
throw InvalidInputException("Type mismatch in Append DataChunk and the types required for appender, "
"expected %s but got %s for column %d",
types[i].ToString(), chunk.data[i].GetType().ToString(), i + 1);
}
}
}
collection->Append(chunk);
if (collection->Count() >= FLUSH_COUNT) {
Expand Down
26 changes: 26 additions & 0 deletions src/main/capi/appender-c.cpp
Expand Up @@ -212,6 +212,32 @@ duckdb_state duckdb_appender_close(duckdb_appender appender) {
return duckdb_appender_run_function(appender, [&](Appender &appender) { appender.Close(); });
}

idx_t duckdb_appender_column_count(duckdb_appender appender) {
if (!appender) {
return 0;
}

auto wrapper = reinterpret_cast<AppenderWrapper *>(appender);
if (!wrapper->appender) {
return 0;
}

return wrapper->appender->GetTypes().size();
}

duckdb_logical_type duckdb_appender_column_type(duckdb_appender appender, idx_t col_idx) {
if (!appender || col_idx >= duckdb_appender_column_count(appender)) {
return nullptr;
}

auto wrapper = reinterpret_cast<AppenderWrapper *>(appender);
if (!wrapper->appender) {
return nullptr;
}

return reinterpret_cast<duckdb_logical_type>(new duckdb::LogicalType(wrapper->appender->GetTypes()[col_idx]));
}

duckdb_state duckdb_append_data_chunk(duckdb_appender appender, duckdb_data_chunk chunk) {
if (!chunk) {
return DuckDBError;
Expand Down
52 changes: 51 additions & 1 deletion test/api/capi/test_capi_data_chunk.cpp
Expand Up @@ -127,11 +127,21 @@ TEST_CASE("Test DataChunk C API", "[capi]") {
REQUIRE(duckdb_data_chunk_get_size(nullptr) == 0);

// use the appender to insert a value using the data chunk API

duckdb_appender appender;
status = duckdb_appender_create(tester.connection, nullptr, "test", &appender);
REQUIRE(status == DuckDBSuccess);

// get the column types from the appender
REQUIRE(duckdb_appender_column_count(appender) == 2);

auto appender_first_type = duckdb_appender_column_type(appender, 0);
REQUIRE(duckdb_get_type_id(appender_first_type) == DUCKDB_TYPE_BIGINT);
duckdb_destroy_logical_type(&appender_first_type);

auto appender_second_type = duckdb_appender_column_type(appender, 1);
REQUIRE(duckdb_get_type_id(appender_second_type) == DUCKDB_TYPE_SMALLINT);
duckdb_destroy_logical_type(&appender_second_type);

// append standard primitive values
auto col1_ptr = (int64_t *)duckdb_vector_get_data(duckdb_data_chunk_get_vector(data_chunk, 0));
*col1_ptr = 42;
Expand Down Expand Up @@ -193,6 +203,46 @@ TEST_CASE("Test DataChunk C API", "[capi]") {
duckdb_destroy_logical_type(&types[1]);
}

TEST_CASE("Test DataChunk appending incorrect types in C API", "[capi]") {
CAPITester tester;
duckdb::unique_ptr<CAPIResult> result;
duckdb_state status;

REQUIRE(tester.OpenDatabase(nullptr));

REQUIRE(duckdb_vector_size() == STANDARD_VECTOR_SIZE);

tester.Query("CREATE TABLE test(i BIGINT, j SMALLINT)");

duckdb_logical_type types[2];
types[0] = duckdb_create_logical_type(DUCKDB_TYPE_BIGINT);
types[1] = duckdb_create_logical_type(DUCKDB_TYPE_BOOLEAN);

auto data_chunk = duckdb_create_data_chunk(types, 2);
REQUIRE(data_chunk);

auto col1_ptr = (int64_t *)duckdb_vector_get_data(duckdb_data_chunk_get_vector(data_chunk, 0));
*col1_ptr = 42;
auto col2_ptr = (bool *)duckdb_vector_get_data(duckdb_data_chunk_get_vector(data_chunk, 1));
*col2_ptr = false;

duckdb_appender appender;
status = duckdb_appender_create(tester.connection, nullptr, "test", &appender);
REQUIRE(status == DuckDBSuccess);

REQUIRE(duckdb_append_data_chunk(appender, data_chunk) == DuckDBError);

auto error = duckdb_appender_error(appender);
REQUIRE(duckdb::StringUtil::Contains(error, "expected SMALLINT but got BOOLEAN for column 2"));

duckdb_appender_destroy(&appender);

duckdb_destroy_data_chunk(&data_chunk);

duckdb_destroy_logical_type(&types[0]);
duckdb_destroy_logical_type(&types[1]);
}

TEST_CASE("Test DataChunk varchar result fetch in C API", "[capi]") {
if (duckdb_vector_size() < 64) {
return;
Expand Down

0 comments on commit 221a408

Please sign in to comment.