#pragma once
#include <algorithm>
#include <cctype>
#include <string>
#include <functional>
#include <ctime>
#include <tuple>
#include <memory>
#include <vector>
#include <locale>
#include <codecvt>
#ifdef __has_include
#if __cplusplus > 201402 && __has_include(<optional>)
#define MODERN_SQLITE_STD_OPTIONAL_SUPPORT
#endif
#endif
#ifdef __has_include
#if __cplusplus > 201402 && __has_include(<variant>)
#define MODERN_SQLITE_STD_VARIANT_SUPPORT
#endif
#endif
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
#include <optional>
#endif
#ifdef _MODERN_SQLITE_BOOST_OPTIONAL_SUPPORT
#include <boost/optional.hpp>
#endif
#include <sqlite3.h>
#include "sqlite_modern_cpp/errors.h"
#include "sqlite_modern_cpp/utility/function_traits.h"
#include "sqlite_modern_cpp/utility/uncaught_exceptions.h"
#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
#include "sqlite_modern_cpp/utility/variant.h"
#endif
namespace sqlite {
class database;
class database_binder;
template<std::size_t> class binder;
typedef std::shared_ptr<sqlite3> connection_type;
template<typename Tuple, int Element = 0, bool Last = (std::tuple_size<Tuple>::value == Element)> struct tuple_iterate {
static void iterate(Tuple& t, database_binder& db) {
get_col_from_db(db, Element, std::get<Element>(t));
tuple_iterate<Tuple, Element + 1>::iterate(t, db);
}
};
template<typename Tuple, int Element> struct tuple_iterate<Tuple, Element, true> {
static void iterate(Tuple&, database_binder&) {}
};
class database_binder {
public:
// database_binder is not copyable
database_binder() = delete;
database_binder(const database_binder& other) = delete;
database_binder& operator=(const database_binder&) = delete;
database_binder(database_binder&& other) :
_db(std::move(other._db)),
_stmt(std::move(other._stmt)),
_inx(other._inx), execution_started(other.execution_started) { }
void reset() {
sqlite3_reset(_stmt.get());
sqlite3_clear_bindings(_stmt.get());
_inx = 1;
used(false);
}
void execute() {
int hresult;
used(true); /* prevent from executing again when goes out of scope */
while((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {}
if(hresult != SQLITE_DONE) {
errors::throw_sqlite_error(hresult, sql());
}
}
std::string sql() {
#if SQLITE_VERSION_NUMBER >= 3014000
auto sqlite_deleter = [](void *ptr) {sqlite3_free(ptr);};
std::unique_ptr<char, decltype(sqlite_deleter)> str(sqlite3_expanded_sql(_stmt.get()), sqlite_deleter);
return str ? str.get() : original_sql();
#else
return original_sql();
#endif
}
std::string original_sql() {
return sqlite3_sql(_stmt.get());
}
void used(bool state) {
if(execution_started == true && state == true) {
throw errors::reexecution("Already used statement executed again! Please reset() first!",sql());
}
execution_started = state;
}
bool used() const { return execution_started; }
private:
std::shared_ptr<sqlite3> _db;
std::unique_ptr<sqlite3_stmt, decltype(&sqlite3_finalize)> _stmt;
utility::UncaughtExceptionDetector _has_uncaught_exception;
int _inx;
bool execution_started = false;
void _extract(std::function<void(void)> call_back) {
int hresult;
used(true);
while((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {
call_back();
}
if(hresult != SQLITE_DONE) {
errors::throw_sqlite_error(hresult, sql());
}
}