185 lines
5.6 KiB
C++
185 lines
5.6 KiB
C++
#include "capi_tester.hpp"
|
|
#include "duckdb.h"
|
|
|
|
using namespace duckdb;
|
|
using namespace std;
|
|
|
|
TEST_CASE("Test streaming results in C API", "[capi]") {
|
|
CAPITester tester;
|
|
CAPIPrepared prepared;
|
|
CAPIPending pending;
|
|
duckdb::unique_ptr<CAPIResult> result;
|
|
|
|
// open the database in in-memory mode
|
|
REQUIRE(tester.OpenDatabase(nullptr));
|
|
REQUIRE(prepared.Prepare(tester, "SELECT i::UINT32 FROM range(1000000) tbl(i)"));
|
|
REQUIRE(pending.PendingStreaming(prepared));
|
|
|
|
while (true) {
|
|
auto state = pending.ExecuteTask();
|
|
REQUIRE(state != DUCKDB_PENDING_ERROR);
|
|
if (state == DUCKDB_PENDING_RESULT_READY) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
result = pending.Execute();
|
|
REQUIRE(result);
|
|
REQUIRE(!result->HasError());
|
|
auto chunk = result->StreamChunk();
|
|
|
|
idx_t value = duckdb::DConstants::INVALID_INDEX;
|
|
idx_t result_count = 0;
|
|
while (chunk) {
|
|
auto old_value = value;
|
|
|
|
auto vector = chunk->GetVector(0);
|
|
uint32_t *data = (uint32_t *)duckdb_vector_get_data(vector);
|
|
value = data[0];
|
|
if (old_value != duckdb::DConstants::INVALID_INDEX) {
|
|
// We select from a range, so we can expect every starting value of a new chunk to be higher than the last
|
|
// one.
|
|
REQUIRE(value > old_value);
|
|
}
|
|
REQUIRE(chunk->size() > 0);
|
|
result_count += chunk->size();
|
|
REQUIRE(result_count <= 1000000);
|
|
chunk = result->StreamChunk();
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Test other methods on streaming results in C API", "[capi]") {
|
|
CAPITester tester;
|
|
CAPIPrepared prepared;
|
|
CAPIPending pending;
|
|
duckdb::unique_ptr<CAPIResult> result;
|
|
|
|
// open the database in in-memory mode
|
|
REQUIRE(tester.OpenDatabase(nullptr));
|
|
REQUIRE(prepared.Prepare(tester, "SELECT i::UINT32 FROM range(1000000) tbl(i)"));
|
|
REQUIRE(pending.PendingStreaming(prepared));
|
|
|
|
while (true) {
|
|
auto state = pending.ExecuteTask();
|
|
REQUIRE(state != DUCKDB_PENDING_ERROR);
|
|
if (state == DUCKDB_PENDING_RESULT_READY) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Once we've done this, the StreamQueryResult is made
|
|
result = pending.Execute();
|
|
REQUIRE(result);
|
|
REQUIRE(!result->HasError());
|
|
REQUIRE(result->IsStreaming());
|
|
|
|
// interrogate the result with various methods
|
|
auto chunk_count = result->ChunkCount();
|
|
REQUIRE(chunk_count == 0);
|
|
auto column_count = result->ColumnCount();
|
|
(void)column_count;
|
|
auto column_name = result->ColumnName(0);
|
|
(void)column_name;
|
|
auto column_type = result->ColumnType(0);
|
|
(void)column_type;
|
|
auto error_message = result->ErrorMessage();
|
|
REQUIRE(error_message == nullptr);
|
|
auto fetched_chunk = result->FetchChunk(0);
|
|
REQUIRE(fetched_chunk == nullptr);
|
|
auto has_error = result->HasError();
|
|
REQUIRE(has_error == false);
|
|
auto row_count = result->row_count();
|
|
REQUIRE(row_count == 0);
|
|
auto rows_changed = result->rows_changed();
|
|
REQUIRE(rows_changed == 0);
|
|
|
|
// this succeeds because the result is materialized if a stream-result method hasn't being used yet
|
|
auto column_data = result->ColumnData<uint32_t>(0);
|
|
REQUIRE(column_data != nullptr);
|
|
|
|
// this materializes the result
|
|
auto is_null = result->IsNull(0, 0);
|
|
REQUIRE(is_null == false);
|
|
}
|
|
|
|
TEST_CASE("Test streaming arrow results in C API", "[capi][arrow]") {
|
|
CAPITester tester;
|
|
CAPIPrepared prepared;
|
|
CAPIPending pending;
|
|
duckdb::unique_ptr<CAPIResult> result;
|
|
|
|
// open the database in in-memory mode
|
|
REQUIRE(tester.OpenDatabase(nullptr));
|
|
REQUIRE(prepared.Prepare(tester, "SELECT i::UINT32 FROM range(1000000) tbl(i)"));
|
|
REQUIRE(pending.PendingStreaming(prepared));
|
|
|
|
while (true) {
|
|
auto state = pending.ExecuteTask();
|
|
REQUIRE(state != DUCKDB_PENDING_ERROR);
|
|
if (state == DUCKDB_PENDING_RESULT_READY) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
result = pending.Execute();
|
|
REQUIRE(result);
|
|
REQUIRE(!result->HasError());
|
|
auto chunk = result->StreamChunk();
|
|
|
|
// Check handle null out_array
|
|
duckdb_result_arrow_array(result->InternalResult(), chunk->GetChunk(), nullptr);
|
|
|
|
int nb_row = 0;
|
|
while (chunk) {
|
|
ArrowArray *arrow_array = new ArrowArray();
|
|
duckdb_result_arrow_array(result->InternalResult(), chunk->GetChunk(), (duckdb_arrow_array *)&arrow_array);
|
|
nb_row += arrow_array->length;
|
|
chunk = result->StreamChunk();
|
|
arrow_array->release(arrow_array);
|
|
delete arrow_array;
|
|
}
|
|
REQUIRE(nb_row == 1000000);
|
|
}
|
|
|
|
TEST_CASE("Test query progress and interrupt in C API", "[capi]") {
|
|
CAPITester tester;
|
|
CAPIPrepared prepared;
|
|
CAPIPending pending;
|
|
duckdb::unique_ptr<CAPIResult> result;
|
|
|
|
// test null handling
|
|
REQUIRE(duckdb_query_progress(nullptr).percentage == -1.0);
|
|
duckdb_interrupt(nullptr);
|
|
|
|
// open the database in in-memory mode
|
|
REQUIRE(tester.OpenDatabase(nullptr));
|
|
REQUIRE_NO_FAIL(tester.Query("SET threads=1"));
|
|
REQUIRE_NO_FAIL(tester.Query("create table tbl as select range a, mod(range,10) b from range(10000);"));
|
|
REQUIRE_NO_FAIL(tester.Query("create table tbl_2 as select range a from range(10000);"));
|
|
REQUIRE_NO_FAIL(tester.Query("set enable_progress_bar=true;"));
|
|
REQUIRE_NO_FAIL(tester.Query("set enable_progress_bar_print=false;"));
|
|
// test no progress before query
|
|
REQUIRE(duckdb_query_progress(tester.connection).percentage == -1.0);
|
|
// test zero progress with query
|
|
REQUIRE(prepared.Prepare(tester, "select count(*) from tbl where a = (select min(a) from tbl_2)"));
|
|
REQUIRE(pending.PendingStreaming(prepared));
|
|
REQUIRE(duckdb_query_progress(tester.connection).percentage == 0.0);
|
|
|
|
// test progress
|
|
while (duckdb_query_progress(tester.connection).percentage == 0.0) {
|
|
auto state = pending.ExecuteTask();
|
|
REQUIRE(state == DUCKDB_PENDING_RESULT_NOT_READY);
|
|
}
|
|
REQUIRE(duckdb_query_progress(tester.connection).percentage >= 0.0);
|
|
|
|
// test interrupt
|
|
duckdb_interrupt(tester.connection);
|
|
while (true) {
|
|
auto state = pending.ExecuteTask();
|
|
REQUIRE(state != DUCKDB_PENDING_RESULT_READY);
|
|
if (state == DUCKDB_PENDING_ERROR) {
|
|
break;
|
|
}
|
|
}
|
|
}
|