#include "catch.hpp" #include "test_helpers.hpp" #include "duckdb/common/types/date.hpp" #include "duckdb/common/types/time.hpp" #include "duckdb/common/types/timestamp.hpp" using namespace duckdb; using namespace std; TEST_CASE("Test results API", "[api]") { DuckDB db(nullptr); Connection con(db); // result equality auto result = con.Query("SELECT 42"); auto result2 = con.Query("SELECT 42"); REQUIRE(result->Equals(*result2)); // result inequality result = con.Query("SELECT 42"); result2 = con.Query("SELECT 43"); REQUIRE(!result->Equals(*result2)); // stream query to string auto stream_result = con.SendQuery("SELECT 42"); auto str = stream_result->ToString(); REQUIRE(!str.empty()); // materialized query to string result = con.Query("SELECT 42"); str = result->ToString(); REQUIRE(!str.empty()); // error to string result = con.Query("SELEC 42"); str = result->ToString(); REQUIRE(!str.empty()); } TEST_CASE("Test iterating over results", "[api]") { DuckDB db(nullptr); Connection con(db); REQUIRE_NO_FAIL(con.Query("CREATE TABLE data(i INTEGER, j VARCHAR)")); REQUIRE_NO_FAIL(con.Query("INSERT INTO data VALUES (1, 'hello'), (2, 'test')")); duckdb::vector i_values = {1, 2}; duckdb::vector j_values = {"hello", "test"}; idx_t row_count = 0; auto result = con.Query("SELECT * FROM data;"); for (auto &row : *result) { REQUIRE(row.GetValue(0) == i_values[row.row]); REQUIRE(row.GetValue(1) == j_values[row.row]); row_count++; } REQUIRE(row_count == 2); } TEST_CASE("Test different result types", "[api]") { DuckDB db(nullptr); Connection con(db); REQUIRE_NO_FAIL( con.Query("CREATE TABLE data(i INTEGER, j VARCHAR, k DECIMAL(38,1), l DECIMAL(18,3), m HUGEINT, n DOUBLE)")); REQUIRE_NO_FAIL(con.Query("INSERT INTO data VALUES (23, '17.1', 94289, 9842, 4982412, 17.3)")); idx_t row_count = 0; auto result = con.Query("SELECT * FROM data;"); for (auto &row : *result) { REQUIRE(row.GetValue(0) == 23); REQUIRE(row.GetValue(0) == 23); REQUIRE(row.GetValue(0) == 23); REQUIRE(row.GetValue(0) == "23"); REQUIRE(row.GetValue(1) == 17); REQUIRE(row.GetValue(1) == 17); REQUIRE(row.GetValue(1) == 17.1); REQUIRE(row.GetValue(1) == "17.1"); REQUIRE(row.GetValue(2) == 94289); REQUIRE(row.GetValue(2) == 94289); REQUIRE(row.GetValue(2) == 94289); REQUIRE(row.GetValue(3) == 9842); REQUIRE(row.GetValue(3) == 9842); REQUIRE(row.GetValue(3) == 9842); REQUIRE(row.GetValue(4) == 4982412); REQUIRE(row.GetValue(4) == 4982412); REQUIRE(row.GetValue(4) == 4982412); REQUIRE(row.GetValue(4) == "4982412"); REQUIRE(row.GetValue(5) == 17); REQUIRE(row.GetValue(5) == 17); REQUIRE(row.GetValue(5) == 17.3); row_count++; } REQUIRE(row_count == 1); } TEST_CASE("Test dates/times/timestamps", "[api]") { DuckDB db(nullptr); Connection con(db); REQUIRE_NO_FAIL(con.Query("CREATE TABLE data(i DATE, j TIME, k TIMESTAMP)")); REQUIRE_NO_FAIL( con.Query("INSERT INTO data VALUES (DATE '1992-01-01', TIME '13:00:17', TIMESTAMP '1993-01-01 14:00:17')")); idx_t row_count = 0; auto result = con.Query("SELECT * FROM data;"); for (auto &row : *result) { int32_t year, month, day; int32_t hour, minute, second, millisecond; auto date = row.GetValue(0); auto time = row.GetValue(1); auto timestamp = row.GetValue(2); Date::Convert(date, year, month, day); REQUIRE(year == 1992); REQUIRE(month == 1); REQUIRE(day == 1); Time::Convert(time, hour, minute, second, millisecond); REQUIRE(hour == 13); REQUIRE(minute == 0); REQUIRE(second == 17); REQUIRE(millisecond == 0); Timestamp::Convert(timestamp, date, time); Date::Convert(date, year, month, day); Time::Convert(time, hour, minute, second, millisecond); REQUIRE(year == 1993); REQUIRE(month == 1); REQUIRE(day == 1); REQUIRE(hour == 14); REQUIRE(minute == 0); REQUIRE(second == 17); REQUIRE(millisecond == 0); row_count++; } REQUIRE(row_count == 1); } TEST_CASE("Error in streaming result after initial query", "[api][.]") { DuckDB db(nullptr); Connection con(db); // create a big table with strings that are numbers REQUIRE_NO_FAIL(con.Query("CREATE TABLE strings(v VARCHAR)")); for (size_t i = 0; i < STANDARD_VECTOR_SIZE * 2 - 1; i++) { REQUIRE_NO_FAIL(con.Query("INSERT INTO strings VALUES ('" + to_string(i) + "')")); } // now insert one non-numeric value REQUIRE_NO_FAIL(con.Query("INSERT INTO strings VALUES ('hello')")); // now create a streaming result auto result = con.SendQuery("SELECT CAST(v AS INTEGER) FROM strings"); REQUIRE_FAIL(result); } TEST_CASE("Test UUID", "[api][uuid]") { DuckDB db(nullptr); Connection con(db); REQUIRE_NO_FAIL(con.Query("CREATE TABLE uuids (u uuid)")); REQUIRE_NO_FAIL(con.Query("INSERT INTO uuids VALUES ('A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11'), " "('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11');")); REQUIRE_FAIL(con.Query("INSERT INTO uuids VALUES ('');")); REQUIRE_FAIL(con.Query("INSERT INTO uuids VALUES ('a0eebc99');")); REQUIRE_FAIL(con.Query("INSERT INTO uuids VALUES ('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380z11');")); idx_t row_count = 0; auto result = con.Query("SELECT * FROM uuids"); for (auto &row : *result) { auto uuid = row.GetValue(0); REQUIRE(uuid == "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"); row_count++; } REQUIRE(row_count == 2); } TEST_CASE("Test ARRAY_AGG with ORDER BY", "[api][array_agg]") { DuckDB db(nullptr); Connection con(db); REQUIRE_NO_FAIL(con.Query("CREATE TABLE t2 (a INT, b INT, c INT)")); REQUIRE_NO_FAIL(con.Query("INSERT INTO t2 VALUES (1,1,1), (1,2,2), (2,1,3), (2,2,4)")); auto result = con.Query("select a, array_agg(c ORDER BY b) from t2 GROUP BY a"); REQUIRE(!result->HasError()); REQUIRE(result->names[1] == "array_agg(c ORDER BY b)"); } TEST_CASE("Issue #9417", "[api][.]") { DBConfig config; config.options.allow_unsigned_extensions = true; DuckDB db(TestCreatePath("issue_replication.db"), &config); Connection con(db); auto result = con.SendQuery("with max_period as (" " select max(reporting_date) as max_record\n" " from \"data/parquet-testing/issue9417.parquet\"\n" " )\n" " select\n" " *\n" " from \"data/parquet-testing/issue9417.parquet\" e\n" " inner join max_period\n" " on e.reporting_date = max_period.max_record\n" " where e.record_date between '2012-01-31' and '2023-06-30'"); idx_t count = 0; while (true) { auto chunk = result->Fetch(); if (chunk) { REQUIRE(count + chunk->size() <= 46); count += chunk->size(); } else { break; } } REQUIRE(count == 46); }