Skip to content

Commit

Permalink
Add row-based iterator over query result
Browse files Browse the repository at this point in the history
  • Loading branch information
Mytherin committed Dec 25, 2019
1 parent ba82470 commit f0351bf
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 2 deletions.
59 changes: 59 additions & 0 deletions src/common/types/value.cpp
Expand Up @@ -9,6 +9,7 @@
#include "duckdb/common/printer.hpp"
#include "duckdb/common/serializer.hpp"
#include "duckdb/common/types/date.hpp"
#include "duckdb/common/types/null_value.hpp"
#include "duckdb/common/types/time.hpp"
#include "duckdb/common/types/timestamp.hpp"
#include "duckdb/common/types/vector.hpp"
Expand Down Expand Up @@ -172,6 +173,9 @@ Value Value::TIMESTAMP(int32_t year, int32_t month, int32_t day, int32_t hour, i
return Value::TIMESTAMP(Date::FromDate(year, month, day), Time::FromTime(hour, min, sec, msec));
}

//===--------------------------------------------------------------------===//
// CreateValue
//===--------------------------------------------------------------------===//
template <> Value Value::CreateValue(bool value) {
return Value::BOOLEAN(value);
}
Expand Down Expand Up @@ -208,6 +212,61 @@ template <> Value Value::CreateValue(double value) {
return Value::DOUBLE(value);
}

//===--------------------------------------------------------------------===//
// GetValue
//===--------------------------------------------------------------------===//
template<class T> T Value::GetValueInternal() {
if (is_null) {
return NullValue<T>();
}
switch (type) {
case TypeId::BOOLEAN:
return Cast::Operation<bool, T>(value_.boolean);
case TypeId::TINYINT:
return Cast::Operation<int8_t, T>(value_.tinyint);
case TypeId::SMALLINT:
return Cast::Operation<int16_t, T>(value_.smallint);
case TypeId::INTEGER:
return Cast::Operation<int32_t, T>(value_.integer);
case TypeId::BIGINT:
return Cast::Operation<int64_t, T>(value_.bigint);
case TypeId::FLOAT:
return Cast::Operation<float, T>(value_.float_);
case TypeId::DOUBLE:
return Cast::Operation<double, T>(value_.double_);
case TypeId::VARCHAR:
return Cast::Operation<const char*, T>(str_value.c_str());
default:
throw NotImplementedException("Unimplemented type for GetValue()");
}
}

template <> bool Value::GetValue() {
return GetValueInternal<bool>();
}
template <> int8_t Value::GetValue() {
return GetValueInternal<int8_t>();
}
template <> int16_t Value::GetValue() {
return GetValueInternal<int16_t>();
}
template <> int32_t Value::GetValue() {
return GetValueInternal<int32_t>();
}
template <> int64_t Value::GetValue() {
return GetValueInternal<int64_t>();
}
template <> string Value::GetValue() {
return GetValueInternal<string>();
}
template <> float Value::GetValue() {
return GetValueInternal<float>();
}
template <> double Value::GetValue() {
return GetValueInternal<double>();
}


Value Value::Numeric(TypeId type, int64_t value) {
assert(!TypeIsIntegral(type) ||
(value >= duckdb::MinimumValue(type) && (value < 0 || (uint64_t)value <= duckdb::MaximumValue(type))));
Expand Down
16 changes: 15 additions & 1 deletion src/include/duckdb/common/types/value.hpp
Expand Up @@ -90,8 +90,11 @@ class Value {
//! Create a double Value from a specified value
static Value DOUBLE(double value);

template<class T> T GetValue() {
throw NotImplementedException("Unimplemented template type for Value::GetValue");
}
template <class T> static Value CreateValue(T value) {
throw NotImplementedException("Unimplemented template type for value creation");
throw NotImplementedException("Unimplemented template type for Value::CreateValue");
}

int64_t GetNumericValue();
Expand Down Expand Up @@ -175,6 +178,8 @@ class Value {
void Print();

private:
template<class T>
T GetValueInternal();
//! Templated helper function for casting
template <class DST, class OP> static DST _cast(const Value &v);

Expand All @@ -196,4 +201,13 @@ template <> Value Value::CreateValue(string value);
template <> Value Value::CreateValue(float value);
template <> Value Value::CreateValue(double value);

template <> bool Value::GetValue();
template <> int8_t Value::GetValue();
template <> int16_t Value::GetValue();
template <> int32_t Value::GetValue();
template <> int64_t Value::GetValue();
template <> string Value::GetValue();
template <> float Value::GetValue();
template <> double Value::GetValue();

} // namespace duckdb
54 changes: 53 additions & 1 deletion src/include/duckdb/main/query_result.hpp
Expand Up @@ -24,7 +24,7 @@ class QueryResult {
QueryResult(QueryResultType type, StatementType statement_type);
//! Creates a successful query result with the specified names and types
QueryResult(QueryResultType type, StatementType statement_type, vector<SQLType> sql_types, vector<TypeId> types,
vector<string> names);
vector<string> names);
//! Creates an unsuccessful query result with error condition
QueryResult(QueryResultType type, string error);
virtual ~QueryResult() {
Expand Down Expand Up @@ -57,7 +57,59 @@ class QueryResult {
//! Returns true if the two results are identical; false otherwise. Note that this method is destructive; it calls
//! Fetch() until both results are exhausted. The data in the results will be lost.
bool Equals(QueryResult &other);
private:
//! The current chunk used by the iterator
unique_ptr<DataChunk> iterator_chunk;

class QueryResultIterator;

class QueryResultRow {
public:
QueryResultRow(QueryResultIterator &iterator) :
iterator(iterator), row(0) {
}

QueryResultIterator &iterator;
index_t row;

template<class T>
T GetValue(index_t col_idx) const {
return iterator.result->iterator_chunk->data[col_idx].GetValue(iterator.row_idx).GetValue<T>();
}
};
//! The row-based query result iterator. Invoking the
class QueryResultIterator {
public:
QueryResultIterator(QueryResult *result) :
current_row(*this), result(result), row_idx(0){
if (result) {
result->iterator_chunk = result->Fetch();
}
}

QueryResultRow current_row;
QueryResult *result;
index_t row_idx;
public:
void Next() {
if (!result->iterator_chunk) {
return;
}
current_row.row++;
row_idx++;
if (row_idx >= result->iterator_chunk->size()) {
result->iterator_chunk = result->Fetch();
row_idx = 0;
}
}

QueryResultIterator& operator++() { Next(); return *this; }
bool operator!=(const QueryResultIterator &other) const { return !result->iterator_chunk; }
const QueryResultRow& operator*() const { return current_row; }
};
public:
QueryResultIterator begin() { return QueryResultIterator(this); }
QueryResultIterator end() { return QueryResultIterator(nullptr); }
protected:
string HeaderToString();

Expand Down
17 changes: 17 additions & 0 deletions test/api/test_results.cpp
Expand Up @@ -34,6 +34,23 @@ TEST_CASE("Test results API", "[api]") {
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')"));

vector<int> i_values = {1, 2};
vector<string> j_values = {"hello", "test"};

auto result = con.Query("SELECT * FROM data;");
for(auto &row : *result) {
REQUIRE(row.GetValue<int>(0) == i_values[row.row]);
REQUIRE(row.GetValue<string>(1) == j_values[row.row]);
}
}

TEST_CASE("Error in streaming result after initial query", "[api]") {
DuckDB db(nullptr);
Connection con(db);
Expand Down

0 comments on commit f0351bf

Please sign in to comment.