Skip to content

Commit d6f4070

Browse files
authored
Merge pull request SqliteModernCpp#170 from zauguin/named
Support for named and indexed parameters
2 parents 1f0bb47 + c989bf2 commit d6f4070

File tree

3 files changed

+61
-0
lines changed

3 files changed

+61
-0
lines changed

hdr/sqlite_modern_cpp.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,25 @@ 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+
template<class T>
40+
auto named_parameter(const char *name, T &&arg) {
41+
return index_binding_helper<decltype(arg), true>{name, std::forward<decltype(arg)>(arg)};
42+
}
43+
template<class T>
44+
auto indexed_parameter(int index, T &&arg) {
45+
return index_binding_helper<decltype(arg)>{index, std::forward<decltype(arg)>(arg)};
46+
}
47+
2948
class row_iterator;
3049
class database_binder {
3150

@@ -101,6 +120,8 @@ namespace sqlite {
101120
}
102121

103122
template<typename T> friend database_binder& operator<<(database_binder& db, T&&);
123+
template<typename T> friend database_binder& operator<<(database_binder& db, index_binding_helper<T>);
124+
template<typename T> friend database_binder& operator<<(database_binder& db, index_binding_helper<T, true>);
104125
friend void operator++(database_binder& db, int);
105126

106127
public:
@@ -480,6 +501,25 @@ namespace sqlite {
480501
// Some ppl are lazy so we have a operator for proper prep. statemant handling.
481502
void inline operator++(database_binder& db, int) { db.execute(); }
482503

504+
template<typename T> database_binder &operator<<(database_binder& db, index_binding_helper<T> val) {
505+
db._next_index(); --db._inx;
506+
int result = bind_col_in_db(db._stmt.get(), val.index, std::forward<T>(val.value));
507+
if(result != SQLITE_OK)
508+
exceptions::throw_sqlite_error(result, db.sql());
509+
return db;
510+
}
511+
512+
template<typename T> database_binder &operator<<(database_binder& db, index_binding_helper<T, true> val) {
513+
db._next_index(); --db._inx;
514+
int index = sqlite3_bind_parameter_index(db._stmt.get(), val.index);
515+
if(!index)
516+
throw errors::unknown_binding("The given binding name is not valid for this statement", db.sql());
517+
int result = bind_col_in_db(db._stmt.get(), index, std::forward<T>(val.value));
518+
if(result != SQLITE_OK)
519+
exceptions::throw_sqlite_error(result, db.sql());
520+
return db;
521+
}
522+
483523
template<typename T> database_binder &operator<<(database_binder& db, T&& val) {
484524
int result = bind_col_in_db(db._stmt.get(), db._next_index(), std::forward<T>(val));
485525
if(result != SQLITE_OK)
@@ -488,6 +528,7 @@ namespace sqlite {
488528
}
489529
// 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!)
490530
template<typename T> database_binder operator << (database_binder&& db, const T& val) { db << val; return std::move(db); }
531+
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); }
491532

492533
namespace sql_function_binder {
493534
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)" << named_parameter(":second", 2) << named_parameter(":first", a);
15+
16+
db << "SELECT b FROM foo WHERE a=?;" << 1 >> a;
17+
18+
REQUIRE(a == 2);
19+
}

0 commit comments

Comments
 (0)