Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[C-API] Add duckdb_appender_column_type #10401

Merged
merged 6 commits into from Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/include/duckdb.h
Expand Up @@ -2483,6 +2483,23 @@ 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);

/*!
Creates an array of DUCKDB_TYPEs that represent the types of the columns of the table that the appender appends to.

The resulting array should be freed with `duckdb_appender_destroy_column_types`.

* appender: The appender to get the column types from.
* out_types: The resulting array of types.
*/
DUCKDB_API duckdb_state duckdb_appender_get_column_types(duckdb_appender appender, enum DUCKDB_TYPE **out_types);
maiadegraaf marked this conversation as resolved.
Show resolved Hide resolved

/*!
Destroys the array of DUCKDB_TYPEs that was created by `duckdb_appender_get_column_types`.

* types: The array of types to destroy.
*/
DUCKDB_API duckdb_state duckdb_appender_destroy_column_types(enum DUCKDB_TYPE *types);

/*!
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
19 changes: 19 additions & 0 deletions src/main/capi/appender-c.cpp
Expand Up @@ -208,6 +208,25 @@ duckdb_state duckdb_appender_close(duckdb_appender appender) {
return duckdb_appender_run_function(appender, [&](Appender &appender) { appender.Close(); });
}

duckdb_state duckdb_appender_get_column_types(duckdb_appender appender, DUCKDB_TYPE **out_types) {
maiadegraaf marked this conversation as resolved.
Show resolved Hide resolved
maiadegraaf marked this conversation as resolved.
Show resolved Hide resolved
if (!appender) {
return DuckDBError;
}
return duckdb_appender_run_function(appender, [&](Appender &appender) {
auto types = appender.GetTypes();
*out_types = new DUCKDB_TYPE[types.size()];
for (idx_t i = 0; i < types.size(); i++) {
(*out_types)[i] = ConvertCPPTypeToC(types[i]);
}
return DuckDBSuccess;
});
}

duckdb_state duckdb_appender_destroy_column_types(DUCKDB_TYPE *types) {
delete[] types;
return DuckDBSuccess;
}

duckdb_state duckdb_append_data_chunk(duckdb_appender appender, duckdb_data_chunk chunk) {
if (!chunk) {
return DuckDBError;
Expand Down
48 changes: 47 additions & 1 deletion test/api/capi/test_capi_data_chunk.cpp
Expand Up @@ -127,11 +127,17 @@ 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
DUCKDB_TYPE *column_types;
REQUIRE(duckdb_appender_get_column_types(appender, &column_types) == DuckDBSuccess);
REQUIRE(column_types[0] == DUCKDB_TYPE_BIGINT);
REQUIRE(column_types[1] == DUCKDB_TYPE_SMALLINT);
REQUIRE(duckdb_appender_destroy_column_types(column_types) == DuckDBSuccess);

// 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 +199,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