Skip to content

Commit dfe8f2f

Browse files
committed
Initial support for named and indexed parameters
1 parent 1f0bb47 commit dfe8f2f

File tree

3 files changed

+65
-0
lines changed

3 files changed

+65
-0
lines changed

hdr/sqlite_modern_cpp.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,29 @@ namespace sqlite {
2626

2727
typedef std::shared_ptr<sqlite3> connection_type;
2828

29+
template<class T, bool Name = false>
30+
struct index_binding_helper {
31+
index_binding_helper(const index_binding_helper &) = delete;
32+
#if __cplusplus < 201703
33+
index_binding_helper(index_binding_helper &&) = default;
34+
#endif
35+
typename std::conditional<Name, const char *, int>::type index;
36+
T value;
37+
};
38+
39+
inline namespace literals {
40+
inline auto operator ""_sqlparam(const char *name, std::size_t) {
41+
return [name](auto &&arg) {
42+
return index_binding_helper<decltype(arg), true>{name, std::forward<decltype(arg)>(arg)};
43+
};
44+
}
45+
inline auto operator ""_sqlparam(unsigned long long index) {
46+
return [index](auto &&arg) {
47+
return index_binding_helper<decltype(arg)>{index, std::forward<decltype(arg)>(arg)};
48+
};
49+
}
50+
}
51+
2952
class row_iterator;
3053
class database_binder {
3154

@@ -101,6 +124,8 @@ namespace sqlite {
101124
}
102125

103126
template<typename T> friend database_binder& operator<<(database_binder& db, T&&);
127+
template<typename T> friend database_binder& operator<<(database_binder& db, index_binding_helper<T>);
128+
template<typename T> friend database_binder& operator<<(database_binder& db, index_binding_helper<T, true>);
104129
friend void operator++(database_binder& db, int);
105130

106131
public:
@@ -480,6 +505,25 @@ namespace sqlite {
480505
// Some ppl are lazy so we have a operator for proper prep. statemant handling.
481506
void inline operator++(database_binder& db, int) { db.execute(); }
482507

508+
template<typename T> database_binder &operator<<(database_binder& db, index_binding_helper<T> val) {
509+
db._next_index(); --db._inx;
510+
int result = bind_col_in_db(db._stmt.get(), val.index, std::forward<T>(val.value));
511+
if(result != SQLITE_OK)
512+
exceptions::throw_sqlite_error(result, db.sql());
513+
return db;
514+
}
515+
516+
template<typename T> database_binder &operator<<(database_binder& db, index_binding_helper<T, true> val) {
517+
db._next_index(); --db._inx;
518+
int index = sqlite3_bind_parameter_index(db._stmt.get(), val.index);
519+
if(!index)
520+
throw errors::unknown_binding("The given binding name is not valid for this statement", db.sql());
521+
int result = bind_col_in_db(db._stmt.get(), index, std::forward<T>(val.value));
522+
if(result != SQLITE_OK)
523+
exceptions::throw_sqlite_error(result, db.sql());
524+
return db;
525+
}
526+
483527
template<typename T> database_binder &operator<<(database_binder& db, T&& val) {
484528
int result = bind_col_in_db(db._stmt.get(), db._next_index(), std::forward<T>(val));
485529
if(result != SQLITE_OK)
@@ -488,6 +532,7 @@ namespace sqlite {
488532
}
489533
// Convert the rValue binder to a reference and call first op<<, its needed for the call that creates the binder (be carefull of recursion here!)
490534
template<typename T> database_binder operator << (database_binder&& db, const T& val) { db << val; return std::move(db); }
535+
template<typename T, bool Name> database_binder operator << (database_binder&& db, index_binding_helper<T, Name> val) { db << index_binding_helper<T, Name>{val.index, std::forward<T>(val.value)}; return std::move(db); }
491536

492537
namespace sql_function_binder {
493538
template<class T>

hdr/sqlite_modern_cpp/errors.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ namespace sqlite {
3939
class no_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; };
4040
class more_statements: public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements can only contain one statement
4141
class invalid_utf16: public sqlite_exception { using sqlite_exception::sqlite_exception; };
42+
class unknown_binding: public sqlite_exception { using sqlite_exception::sqlite_exception; };
4243

4344
static void throw_sqlite_error(const int& error_code, str_ref sql = "") {
4445
switch(error_code & 0xFF) {

tests/named.cc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <iostream>
2+
#include <cstdlib>
3+
#include <sqlite_modern_cpp.h>
4+
#include <catch.hpp>
5+
using namespace sqlite;
6+
using namespace std;
7+
8+
TEST_CASE("binding named parameters works", "[named]") {
9+
database db(":memory:");
10+
11+
db << "CREATE TABLE foo (a,b);";
12+
13+
int a = 1;
14+
db << "INSERT INTO foo VALUES (:first,:second)" << ":second"_sqlparam(2) << ":first"_sqlparam(a);
15+
16+
db << "SELECT b FROM foo WHERE a=?;" << 1 >> a;
17+
18+
REQUIRE(a == 2);
19+
}

0 commit comments

Comments
 (0)