Revision 143a60e7
Added by David Sorber over 9 years ago
| software/sqlite_poc/DBManager.cc | ||
|---|---|---|
|
|
||
|
#include "DBManager.h"
|
||
|
|
||
|
// Helper function for int64_t type
|
||
|
void get_value(int64_t& val, int idx, sqlite3_stmt* stmt)
|
||
|
{
|
||
|
if (sqlite3_column_type(stmt, idx) != SQLITE_INTEGER)
|
||
|
{
|
||
|
assert(false);
|
||
|
}
|
||
|
|
||
|
val = sqlite3_column_int64(stmt, idx);
|
||
|
}
|
||
|
|
||
|
// Helper function for double type
|
||
|
void get_value(double& val, int idx, sqlite3_stmt* stmt)
|
||
|
{
|
||
|
if (sqlite3_column_type(stmt, idx) != SQLITE_FLOAT)
|
||
|
{
|
||
|
assert(false);
|
||
|
}
|
||
|
|
||
|
val = sqlite3_column_double(stmt, idx);
|
||
|
}
|
||
|
|
||
|
// Helper function for string type
|
||
|
void get_value(std::string& val, int idx, sqlite3_stmt* stmt)
|
||
|
{
|
||
|
if (sqlite3_column_type(stmt, idx) == SQLITE_TEXT)
|
||
|
{
|
||
|
val.assign(reinterpret_cast<const char*>(sqlite3_column_text(stmt, idx)));
|
||
|
}
|
||
|
else if (sqlite3_column_type(stmt, idx) == SQLITE_NULL)
|
||
|
{
|
||
|
val.assign("");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
assert(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DBManager::DBManager(const std::string& db_path)
|
||
|
{
|
||
|
|
||
|
|
||
|
int rc = sqlite3_open(db_path.c_str(), &this->db);
|
||
|
if (rc)
|
||
|
{
|
||
| software/sqlite_poc/DBManager.h | ||
|---|---|---|
|
|
||
|
#include <cassert>
|
||
|
#include <string>
|
||
|
#include <tuple>
|
||
|
#include <type_traits>
|
||
|
#include <vector>
|
||
|
|
||
|
#include <sqlite3.h>
|
||
|
#include <sqlite3.h>
|
||
|
|
||
|
// Helper function prototypes
|
||
|
void get_value(int64_t& val, int idx, sqlite3_stmt* stmt);
|
||
|
void get_value(double& val, int idx, sqlite3_stmt* stmt);
|
||
|
void get_value(std::string& val, int idx, sqlite3_stmt* stmt);
|
||
|
|
||
|
template<size_t I>
|
||
|
struct builder_impl
|
||
|
{
|
||
|
template<typename T, typename G>
|
||
|
static void set_value(T& tup, size_t idx, G& val)
|
||
|
template<typename T>
|
||
|
static void set_values_helper(T& tup, sqlite3_stmt* stmt)
|
||
|
{
|
||
|
if (idx == I - 1)
|
||
|
{
|
||
|
std::get<I - 1>(tup) = val;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
builder_impl<I - 1>::set_value(tup, idx, val);
|
||
|
}
|
||
|
// Assign the "current" value
|
||
|
get_value(std::get<I - 1>(tup), I - 1, stmt);
|
||
|
|
||
|
// Recurse to the next value in the tuple
|
||
|
builder_impl<I - 1>::set_values_helper(tup, stmt);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <>
|
||
|
|
||
|
template<>
|
||
|
struct builder_impl<0>
|
||
|
{
|
||
|
template<typename T, typename G>
|
||
|
static void set_value(T& tup, size_t idx, G& val)
|
||
|
{
|
||
|
assert(false);
|
||
|
template<typename T>
|
||
|
static void set_values_helper(T& tup, sqlite3_stmt* stmt)
|
||
|
{
|
||
|
// Stop recursing
|
||
|
return;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <typename G, typename... Ts>
|
||
|
void set_value_at(std::tuple<Ts...>& tup, size_t idx, G const& val)
|
||
|
template <typename... Ts>
|
||
|
void set_values(std::tuple<Ts...>& tup, sqlite3_stmt* stmt)
|
||
|
{
|
||
|
builder_impl<sizeof...(Ts)>::set_value(tup, idx, val);
|
||
|
}
|
||
|
|
||
|
template <typename G, typename... Ts>
|
||
|
void set_value_at(std::tuple<Ts...>& tup, size_t idx, G& val)
|
||
|
{
|
||
|
builder_impl<sizeof...(Ts)>::set_value(tup, idx, val);
|
||
|
builder_impl<sizeof...(Ts)>::set_values_helper(tup, stmt);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
class DBManager
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
|
||
|
// Constructor
|
||
|
DBManager(const std::string& db_path);
|
||
|
|
||
|
|
||
|
// Destructor
|
||
|
~DBManager();
|
||
|
|
||
|
// execute query
|
||
|
|
||
|
// Execute query template function
|
||
|
template<typename... T>
|
||
|
std::vector<std::tuple<T...>*>* execute_query(
|
||
|
const std::string& sql_statement,
|
||
|
int& return_code)
|
||
|
{
|
||
|
sqlite3_stmt* sqlite_statement;
|
||
|
|
||
|
|
||
|
// Prepare the sqlite statement
|
||
|
return_code = sqlite3_prepare_v2(db, sql_statement.c_str(), -1,
|
||
|
return_code = sqlite3_prepare_v2(db, sql_statement.c_str(), -1,
|
||
|
&sqlite_statement, nullptr);
|
||
|
if (return_code != SQLITE_OK)
|
||
|
{
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
auto output_vector = new std::vector<std::tuple<T...>*>();
|
||
|
|
||
|
}
|
||
|
|
||
|
auto output_vector = new std::vector<std::tuple<T...>*>();
|
||
|
|
||
|
// Iterate until we've collected all the output
|
||
|
int step_return = 0;
|
||
|
while ((step_return = sqlite3_step(sqlite_statement)) != SQLITE_DONE)
|
||
|
{
|
||
|
// Create a new result row to be populated below
|
||
|
// Create a new result row
|
||
|
auto result_row = new std::tuple<T...>();
|
||
|
|
||
|
// Iterate over the columns in the row
|
||
|
int num_cols = sqlite3_data_count(sqlite_statement);
|
||
|
for (int col_idx = 0; col_idx < num_cols; col_idx++)
|
||
|
{
|
||
|
switch(sqlite3_column_type(sqlite_statement, col_idx))
|
||
|
{
|
||
|
case SQLITE_INTEGER:
|
||
|
set_value_at(*result_row, col_idx,
|
||
|
sqlite3_column_int64(sqlite_statement,
|
||
|
col_idx));
|
||
|
break;
|
||
|
|
||
|
case SQLITE_FLOAT:
|
||
|
set_value_at(*result_row, col_idx,
|
||
|
sqlite3_column_double(sqlite_statement,
|
||
|
col_idx));
|
||
|
break;
|
||
|
|
||
|
case SQLITE_TEXT:
|
||
|
//~ set_value_at(*result_row, col_idx,
|
||
|
//~ std::string(reinterpret_cast<const char*>(sqlite3_column_text(sqlite_statement, col_idx))));
|
||
|
break;
|
||
|
|
||
|
case SQLITE_BLOB:
|
||
|
break;
|
||
|
|
||
|
case SQLITE_NULL:
|
||
|
//~ set_value_at(*result_row, col_idx, std::string(""));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Now populate the result row using some neato variadic
|
||
|
// template function magic NOTE: a small amount of black magic
|
||
|
// is in use here; animal sacrifices not necessary
|
||
|
set_values(*result_row, sqlite_statement);
|
||
|
|
||
|
// Add the result row to our vector of output rows
|
||
|
output_vector->push_back(result_row);
|
||
|
}
|
||
|
|
||
|
|
||
|
return output_vector;
|
||
|
}
|
||
|
|
||
|
|
||
|
private:
|
||
|
|
||
|
|
||
|
sqlite3 *db;
|
||
|
};
|
||
| software/sqlite_poc/Makefile | ||
|---|---|---|
|
main: $(OBJECTS)
|
||
|
g++ $(CXXFLAGS) $(OBJECTS) -lsqlite3 -o poc_test
|
||
|
|
||
|
%.o : %.c
|
||
|
%.o : %.c %.h
|
||
|
g++ $(CXXFLAGS) -c $<
|
||
|
|
||
|
clean:
|
||
| software/sqlite_poc/main.cc | ||
|---|---|---|
|
// Make a DBManager instance which will connect to our test DB automagically
|
||
|
DBManager db_mgr("database.db");
|
||
|
|
||
|
|
||
|
// Make a query
|
||
|
std::string query("SELECT Number, Float FROM sample;");
|
||
|
std::string query("SELECT Number, Words, Float FROM sample;");
|
||
|
|
||
|
// Execute the query making sure the template params match up with the
|
||
|
// query params
|
||
|
int rc = 0;
|
||
|
auto results = db_mgr.execute_query<int64_t, double>(query, rc);
|
||
|
auto results = db_mgr.execute_query<int64_t, std::string, double>(query, rc);
|
||
|
std::cout << "Return code: " << rc << std::endl;
|
||
|
|
||
|
// If we returned successfully, print out the result rows
|
||
After much contemplation I realized that my previous approach to solving this execute query template function problem was more wrong than right. Several days of staring at and reading about variadic templates later I figured out the correct way to accomplish my goal.