Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add binding mode option to database and database_binder #168

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions hdr/sqlite_modern_cpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ namespace sqlite {
typedef std::shared_ptr<sqlite3> connection_type;

class row_iterator;

class database_binder {

public:
Expand All @@ -38,7 +39,8 @@ namespace sqlite {
database_binder(database_binder&& other) :
_db(std::move(other._db)),
_stmt(std::move(other._stmt)),
_inx(other._inx), execution_started(other.execution_started) { }
_inx(other._inx), execution_started(other.execution_started),
bind_mode(std::move(other.bind_mode)){ }

void execute();

Expand Down Expand Up @@ -69,6 +71,7 @@ namespace sqlite {
row_iterator end();

private:
binding_mode bind_mode;
std::shared_ptr<sqlite3> _db;
std::unique_ptr<sqlite3_stmt, decltype(&sqlite3_finalize)> _stmt;
utility::UncaughtExceptionDetector _has_uncaught_exception;
Expand Down Expand Up @@ -105,17 +108,17 @@ namespace sqlite {

public:

database_binder(std::shared_ptr<sqlite3> db, u16str_ref sql):
database_binder(std::shared_ptr<sqlite3> db, u16str_ref sql, binding_mode bmode):
_db(db),
_stmt(_prepare(sql), sqlite3_finalize),
_inx(0) {
}
_inx(0),
bind_mode(bmode){}

database_binder(std::shared_ptr<sqlite3> db, str_ref sql):
database_binder(std::shared_ptr<sqlite3> db, str_ref sql, binding_mode bmode):
_db(db),
_stmt(_prepare(sql), sqlite3_finalize),
_inx(0) {
}
_inx(0),
bind_mode(bmode){}

~database_binder() noexcept(false) {
/* Will be executed if no >>op is found, but not if an exception
Expand All @@ -124,7 +127,10 @@ namespace sqlite {
execute();
}
}

database_binder & operator << (binding_mode bmode)
{
bind_mode = bmode;
}
friend class row_iterator;
};

Expand Down Expand Up @@ -355,14 +361,16 @@ namespace sqlite {
OpenFlags flags = OpenFlags::READWRITE | OpenFlags::CREATE;
const char *zVfs = nullptr;
Encoding encoding = Encoding::ANY;
binding_mode bind_mode = binding_mode::make_copies;
};

class database {
protected:
std::shared_ptr<sqlite3> _db;

public:
database(const std::string &db_name, const sqlite_config &config = {}): _db(nullptr) {
binding_mode Default_Bind_Mode;
database(const std::string &db_name, const sqlite_config &config = {}): _db(nullptr), Default_Bind_Mode(config.bind_mode){
sqlite3* tmp = nullptr;
auto ret = sqlite3_open_v2(db_name.data(), &tmp, static_cast<int>(config.flags), config.zVfs);
_db = std::shared_ptr<sqlite3>(tmp, [=](sqlite3* ptr) { sqlite3_close_v2(ptr); }); // this will close the connection eventually when no longer needed.
Expand All @@ -381,11 +389,11 @@ namespace sqlite {
_db(db) {}

database_binder operator<<(str_ref sql) {
return database_binder(_db, sql);
return database_binder(_db, sql, Default_Bind_Mode);
}

database_binder operator<<(u16str_ref sql) {
return database_binder(_db, sql);
return database_binder(_db, sql, Default_Bind_Mode);
}

connection_type connection() const { return _db; }
Expand Down Expand Up @@ -481,7 +489,7 @@ namespace sqlite {
void inline operator++(database_binder& db, int) { db.execute(); }

template<typename T> database_binder &operator<<(database_binder& db, T&& val) {
int result = bind_col_in_db(db._stmt.get(), db._next_index(), std::forward<T>(val));
int result = bind_col_in_db(db._stmt.get(), db._next_index(), std::forward<T>(val), db.bind_mode);
if(result != SQLITE_OK)
exceptions::throw_sqlite_error(result, db.sql());
return db;
Expand Down
66 changes: 43 additions & 23 deletions hdr/sqlite_modern_cpp/type_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ namespace sqlite
#include "errors.h"

namespace sqlite {
// Whether or not SQlite should make a copy of the string or blob when binding to statements. Only use dont_make_copies when you are sure the reference passed will be valid at least until after the query gets executed
enum class binding_mode : bool
{
make_copies, dont_make_copies
};
template<class T, int Type, class = void>
struct has_sqlite_type : std::false_type {};

Expand Down Expand Up @@ -84,7 +89,7 @@ namespace sqlite {
template<>
struct has_sqlite_type<int, SQLITE_INTEGER> : std::true_type {};

inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const int& val) {
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const int& val, binding_mode) {
return sqlite3_bind_int(stmt, inx, val);
}
inline void store_result_in_db(sqlite3_context* db, const int& val) {
Expand All @@ -103,7 +108,7 @@ namespace sqlite {
template<>
struct has_sqlite_type<sqlite_int64, SQLITE_INTEGER, void> : std::true_type {};

inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const sqlite_int64& val) {
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const sqlite_int64& val, binding_mode) {
return sqlite3_bind_int64(stmt, inx, val);
}
inline void store_result_in_db(sqlite3_context* db, const sqlite_int64& val) {
Expand All @@ -122,7 +127,7 @@ namespace sqlite {
template<>
struct has_sqlite_type<float, SQLITE_FLOAT, void> : std::true_type {};

inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const float& val) {
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const float& val, binding_mode) {
return sqlite3_bind_double(stmt, inx, double(val));
}
inline void store_result_in_db(sqlite3_context* db, const float& val) {
Expand All @@ -141,7 +146,7 @@ namespace sqlite {
template<>
struct has_sqlite_type<double, SQLITE_FLOAT, void> : std::true_type {};

inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const double& val) {
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const double& val, binding_mode) {
return sqlite3_bind_double(stmt, inx, val);
}
inline void store_result_in_db(sqlite3_context* db, const double& val) {
Expand All @@ -160,7 +165,7 @@ namespace sqlite {
template<>
struct has_sqlite_type<std::nullptr_t, SQLITE_NULL, void> : std::true_type {};

inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, std::nullptr_t) {
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, std::nullptr_t, binding_mode) {
return sqlite3_bind_null(stmt, inx);
}
inline void store_result_in_db(sqlite3_context* db, std::nullptr_t) {
Expand All @@ -170,13 +175,19 @@ namespace sqlite {
// str_ref
template<>
struct has_sqlite_type<std::string, SQLITE3_TEXT, void> : std::true_type {};
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, str_ref val) {
return sqlite3_bind_text(stmt, inx, val.data(), val.length(), SQLITE_TRANSIENT);
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, str_ref val, binding_mode bmode) {
if(bmode == binding_mode::make_copies)
return sqlite3_bind_text(stmt, inx, val.data(), val.length(), SQLITE_TRANSIENT);
else
return sqlite3_bind_text(stmt, inx, val.data(), val.length(), SQLITE_STATIC);
}

// Convert char* to string_view to trigger op<<(..., const str_ref )
template<std::size_t N> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char(&STR)[N]) {
return sqlite3_bind_text(stmt, inx, &STR[0], N-1, SQLITE_TRANSIENT);
template<std::size_t N> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char(&STR)[N], binding_mode bmode) {
if (bmode == binding_mode::make_copies)
return sqlite3_bind_text(stmt, inx, &STR[0], N-1, SQLITE_TRANSIENT);
else
return sqlite3_bind_text(stmt, inx, &STR[0], N - 1, SQLITE_STATIC);
}

inline std::string get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<std::string>) {
Expand All @@ -194,13 +205,19 @@ namespace sqlite {
// u16str_ref
template<>
struct has_sqlite_type<std::u16string, SQLITE3_TEXT, void> : std::true_type {};
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, u16str_ref val) {
return sqlite3_bind_text16(stmt, inx, val.data(), sizeof(char16_t) * val.length(), SQLITE_TRANSIENT);
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, u16str_ref val, binding_mode bmode) {
if (bmode == binding_mode::make_copies)
return sqlite3_bind_text16(stmt, inx, val.data(), sizeof(char16_t) * val.length(), SQLITE_TRANSIENT);
else
return sqlite3_bind_text16(stmt, inx, val.data(), sizeof(char16_t) * val.length(), SQLITE_STATIC);
}

// Convert char* to string_view to trigger op<<(..., const str_ref )
template<std::size_t N> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char16_t(&STR)[N]) {
return sqlite3_bind_text16(stmt, inx, &STR[0], sizeof(char16_t) * (N-1), SQLITE_TRANSIENT);
template<std::size_t N> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char16_t(&STR)[N], binding_mode bmode) {
if(bmode == binding_mode::make_copies)
return sqlite3_bind_text16(stmt, inx, &STR[0],sizeof(char16_t) * (N-1), SQLITE_TRANSIENT);
else
return sqlite3_bind_text16(stmt, inx, &STR[0], sizeof(char16_t) * (N-1), SQLITE_STATIC);
}

inline std::u16string get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<std::u16string>) {
Expand All @@ -221,8 +238,8 @@ namespace sqlite {
struct has_sqlite_type<Integral, SQLITE_INTEGER, typename std::enable_if<std::is_integral<Integral>::value>::type> : std::true_type {};

template<class Integral, class = typename std::enable_if<std::is_integral<Integral>::value>::type>
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const Integral& val) {
return bind_col_in_db(stmt, inx, static_cast<sqlite3_int64>(val));
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const Integral& val, binding_mode bmode) {
return bind_col_in_db(stmt, inx, static_cast<sqlite3_int64>(val), bmode);
}
template<class Integral, class = std::enable_if<std::is_integral<Integral>::type>>
inline void store_result_in_db(sqlite3_context* db, const Integral& val) {
Expand All @@ -241,10 +258,13 @@ namespace sqlite {
template<typename T, typename A>
struct has_sqlite_type<std::vector<T, A>, SQLITE_BLOB, void> : std::true_type {};

template<typename T, typename A> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::vector<T, A>& vec) {
template<typename T, typename A> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::vector<T, A>& vec, binding_mode bmode) {
void const* buf = reinterpret_cast<void const *>(vec.data());
int bytes = vec.size() * sizeof(T);
return sqlite3_bind_blob(stmt, inx, buf, bytes, SQLITE_TRANSIENT);
if(bmode == binding_mode::make_copies)
return sqlite3_bind_blob(stmt, inx, buf, bytes, SQLITE_TRANSIENT);
else
return sqlite3_bind_blob(stmt, inx, buf, bytes, SQLITE_STATIC);
}
template<typename T, typename A> inline void store_result_in_db(sqlite3_context* db, const std::vector<T, A>& vec) {
void const* buf = reinterpret_cast<void const *>(vec.data());
Expand Down Expand Up @@ -274,8 +294,8 @@ namespace sqlite {
template<typename T>
struct has_sqlite_type<std::unique_ptr<T>, SQLITE_NULL, void> : std::true_type {};

template<typename T> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::unique_ptr<T>& val) {
return val ? bind_col_in_db(stmt, inx, *val) : bind_col_in_db(stmt, inx, nullptr);
template<typename T> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::unique_ptr<T>& val, binding_mode bmode) {
return val ? bind_col_in_db(stmt, inx, *val, bmode) : bind_col_in_db(stmt, inx, nullptr, bmode);
}
template<typename T> inline std::unique_ptr<T> get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<std::unique_ptr<T>>) {
if(sqlite3_column_type(stmt, inx) == SQLITE_NULL) {
Expand Down Expand Up @@ -305,8 +325,8 @@ namespace sqlite {
template<typename T>
struct has_sqlite_type<optional<T>, SQLITE_NULL, void> : std::true_type {};

template <typename OptionalT> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const optional<OptionalT>& val) {
return val ? bind_col_in_db(stmt, inx, *val) : bind_col_in_db(stmt, inx, nullptr);
template <typename OptionalT> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const optional<OptionalT>& val, binding_mode bmode) {
return val ? bind_col_in_db(stmt, inx, *val, bmode) : bind_col_in_db(stmt, inx, nullptr, bmode);
}
template <typename OptionalT> inline void store_result_in_db(sqlite3_context* db, const optional<OptionalT>& val) {
if(val)
Expand Down Expand Up @@ -380,8 +400,8 @@ namespace sqlite {
#endif
}
}
template <typename ...Args> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::variant<Args...>& val) {
return std::visit([&](auto &&opt) {return bind_col_in_db(stmt, inx, std::forward<decltype(opt)>(opt));}, val);
template <typename ...Args> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::variant<Args...>& val, binding_mode bmode) {
return std::visit([&](auto &&opt) {return bind_col_in_db(stmt, inx, std::forward<decltype(opt)>(opt), bmode);}, val);
}
template <typename ...Args> inline void store_result_in_db(sqlite3_context* db, const std::variant<Args...>& val) {
std::visit([&](auto &&opt) {store_result_in_db(db, std::forward<decltype(opt)>(opt));}, val);
Expand Down