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

blob_t implementation #125

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
101 changes: 79 additions & 22 deletions hdr/sqlite_modern_cpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,39 @@ namespace sqlite {
static void iterate(Tuple&, database_binder&) {}
};

template<typename>
struct blob_t;

template<typename T, typename A>
struct blob_t<std::vector<T,A>> {
blob_t() { }
blob_t(std::vector<T,A>&& v) : vec(std::move(v)) { }
std::vector<T,A> vec;

typedef T value_type;
typedef A allocator_type;
};

template<typename T, typename A>
struct blob_t<std::vector<T,A>&> {
blob_t(std::vector<T,A>& v) : vec(v) { }
std::vector<T,A>& vec;

typedef T value_type;
typedef A allocator_type;
};


template<typename T, typename A>
auto blob(std::vector<T,A>&& v) {
return blob_t<std::vector<T,A>>(std::move(v));
};

template<typename T, typename A>
auto blob(std::vector<T,A>& v) {
return blob_t<std::vector<T,A>&>(v);
};

class database_binder {

public:
Expand All @@ -85,7 +118,7 @@ namespace sqlite {
errors::throw_sqlite_error(hresult, sql());
}
}

std::string sql() {
#if SQLITE_VERSION_NUMBER >= 3014000
auto sqlite_deleter = [](void *ptr) {sqlite3_free(ptr);};
Expand All @@ -106,7 +139,7 @@ namespace sqlite {
_next_index();
--_inx;
}
execution_started = state;
execution_started = state;
}
bool used() const { return execution_started; }

Expand Down Expand Up @@ -185,7 +218,7 @@ namespace sqlite {
return tmp;
}

template <typename Type>
template <typename OrgType, typename Type = typename std::remove_reference<OrgType>::type>
struct is_sqlite_value : public std::integral_constant<
bool,
std::is_floating_point<Type>::value
Expand All @@ -194,13 +227,23 @@ namespace sqlite {
|| std::is_same<std::u16string, Type>::value
|| std::is_same<sqlite_int64, Type>::value
> { };

template <typename Type, typename Allocator>
struct is_sqlite_value< std::vector<Type, Allocator> > : public std::integral_constant<
struct is_sqlite_value< blob_t< std::vector<Type, Allocator> > > : public std::integral_constant<
bool,
std::is_floating_point<Type>::value
|| std::is_integral<Type>::value
|| std::is_same<sqlite_int64, Type>::value
> { };

template <typename Type, typename Allocator>
struct is_sqlite_value< blob_t< std::vector<Type, Allocator>& > > : public std::integral_constant<
bool,
std::is_floating_point<Type>::value
|| std::is_integral<Type>::value
|| std::is_same<sqlite_int64, Type>::value
> { };

#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
template <typename ...Args>
struct is_sqlite_value< std::variant<Args...> > : public std::integral_constant<
Expand All @@ -210,9 +253,9 @@ namespace sqlite {
#endif


/* for vector<T, A> support */
template<typename T, typename A> friend database_binder& operator <<(database_binder& db, const std::vector<T, A>& val);
template<typename T, typename A> friend void get_col_from_db(database_binder& db, int inx, std::vector<T, A>& val);
/* for blob_t support */
template<typename VECTOR> friend database_binder& operator <<(database_binder& db, const blob_t<VECTOR>& blb);
template<typename VECTOR> friend void get_col_from_db(database_binder& db, int inx, blob_t<VECTOR>& val);
/* for nullptr & unique_ptr support */
friend database_binder& operator <<(database_binder& db, std::nullptr_t);
template<typename T> friend database_binder& operator <<(database_binder& db, const std::unique_ptr<T>& val);
Expand Down Expand Up @@ -270,11 +313,21 @@ namespace sqlite {
}

template <typename Result>
typename std::enable_if<is_sqlite_value<Result>::value, void>::type operator>>(
Result& value) {
typename std::enable_if<is_sqlite_value<Result>::value, Result>::type operator>>(
Result&& value) {
this->_extract_single_value([&value, this] {
get_col_from_db(*this, 0, value);
});
return value;
}
template <typename T, typename A>
typename std::vector<T,A> operator>>(
blob_t<std::vector<T,A>>&& value) {
this->_extract_single_value([&value, this] {
get_col_from_db(*this, 0, value);
});
std::vector<T,A> result(std::move(value.vec));
return result;
}

template<typename... Types>
Expand Down Expand Up @@ -361,7 +414,7 @@ namespace sqlite {
Values&&... values
);
}

enum class OpenFlags {
READONLY = SQLITE_OPEN_READONLY,
READWRITE = SQLITE_OPEN_READWRITE,
Expand Down Expand Up @@ -629,36 +682,40 @@ namespace sqlite {
}

// vector<T, A>
template<typename T, typename A> inline database_binder& operator<<(database_binder& db, const std::vector<T, A>& vec) {
void const* buf = reinterpret_cast<void const *>(vec.data());
int bytes = vec.size() * sizeof(T);
template<typename VECTOR> inline database_binder& operator<<(database_binder& db, const blob_t<VECTOR>& blb) {
void const* buf = reinterpret_cast<void const *>(blb.vec.data());
int bytes = blb.vec.size() * sizeof(decltype(blb.vec.back()));
int hresult;
if((hresult = sqlite3_bind_blob(db._stmt.get(), db._next_index(), buf, bytes, SQLITE_TRANSIENT)) != SQLITE_OK) {
errors::throw_sqlite_error(hresult, db.sql());
}
return db;
}
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());
int bytes = vec.size() * sizeof(T);
template<typename VECTOR> inline void store_result_in_db(sqlite3_context* db, const blob_t<VECTOR>& blb) {
void const* buf = reinterpret_cast<void const *>(blb.vec.data());
int bytes = blb.vec.size() * sizeof(decltype(blb.vec.back()));
sqlite3_result_blob(db, buf, bytes, SQLITE_TRANSIENT);
}
template<typename T, typename A> inline void get_col_from_db(database_binder& db, int inx, std::vector<T, A>& vec) {
template<typename VECTOR> inline void get_col_from_db(database_binder& db, int inx, blob_t<VECTOR>& blb) {
if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) {
vec.clear();
blb.vec.clear();
} else {
int bytes = sqlite3_column_bytes(db._stmt.get(), inx);
using T = typename blob_t<VECTOR>::value_type;
using A = typename blob_t<VECTOR>::allocator_type;
T const* buf = reinterpret_cast<T const *>(sqlite3_column_blob(db._stmt.get(), inx));
vec = std::vector<T, A>(buf, buf + bytes/sizeof(T));
blb.vec = std::vector<T, A>(buf, buf + bytes/sizeof(T));
}
}
template<typename T, typename A> inline void get_val_from_db(sqlite3_value *value, std::vector<T, A>& vec) {
template<typename VECTOR> inline void get_val_from_db(sqlite3_value *value, blob_t<VECTOR>& blb) {
if(sqlite3_value_type(value) == SQLITE_NULL) {
vec.clear();
blb.vec.clear();
} else {
int bytes = sqlite3_value_bytes(value);
using T = typename blob_t<VECTOR>::value_type;
using A = typename blob_t<VECTOR>::allocator_type;
T const* buf = reinterpret_cast<T const *>(sqlite3_value_blob(value));
vec = std::vector<T, A>(buf, buf + bytes/sizeof(T));
blb.vec = std::vector<T, A>(buf, buf + bytes/sizeof(T));
}
}

Expand Down
47 changes: 20 additions & 27 deletions tests/blob_example.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,44 +13,37 @@ int main()
database db(":memory:");

db << "CREATE TABLE person (name TEXT, numbers BLOB);";
db << "INSERT INTO person VALUES (?, ?)" << "bob" << vector<int> { 1, 2, 3, 4};
db << "INSERT INTO person VALUES (?, ?)" << "jack" << vector<char> { '1', '2', '3', '4'};
db << "INSERT INTO person VALUES (?, ?)" << "sara" << vector<double> { 1.0, 2.0, 3.0, 4.0};
db << "INSERT INTO person VALUES (?, ?)" << "bob" << blob(vector<int> { 1, 2, 3});
db << "INSERT INTO person VALUES (?, ?)" << "jack" << blob(vector<char> { '1', '2', '3'});
db << "INSERT INTO person VALUES (?, ?)" << "sara" << blob(vector<double> { 1.0, 2.0, 3.0});

// Extract to lvalue blob
vector<int> numbers_bob;
db << "SELECT numbers from person where name = ?;" << "bob" >> numbers_bob;
db << "SELECT numbers from person where name = ?;" << "bob" >> blob(numbers_bob);

if(numbers_bob.size() != 4 || numbers_bob[0] != 1
|| numbers_bob[1] != 2 || numbers_bob[2] != 3 || numbers_bob[3] != 4 ) {
cout << "Bad result on line " << __LINE__ << endl;
exit(EXIT_FAILURE);
}
//else { for(auto e : numbers_bob) cout << e << ' '; cout << endl; }

vector<char> numbers_jack;
db << "SELECT numbers from person where name = ?;" << "jack" >> numbers_jack;

if(numbers_jack.size() != 4 || numbers_jack[0] != '1'
|| numbers_jack[1] != '2' || numbers_jack[2] != '3' || numbers_jack[3] != '4' ) {
if(numbers_bob.size() != 3 || numbers_bob[0] != 1 || numbers_bob[1] != 2 || numbers_bob[2] != 3) {
cout << "Bad result on line " << __LINE__ << endl;
exit(EXIT_FAILURE);
}
//else { for(auto e : numbers_jack) cout << e << ' '; cout << endl; }

vector<double> numbers_sara;
db << "SELECT numbers from person where name = ?;" << "sara" >> numbers_sara;

if(numbers_sara.size() != 4 || numbers_sara[0] != 1
|| numbers_sara[1] != 2 || numbers_sara[2] != 3 || numbers_sara[3] != 4 ) {
// Extract to lambda blob parameter
vector<char> numbers_jack;
db << "SELECT numbers from person where name = ?;" << "jack" >> [](blob_t<vector<char>> numbers_jack) {
if(numbers_jack.vec.size() != 3 || numbers_jack.vec[0] != '1'
|| numbers_jack.vec[1] != '2' || numbers_jack.vec[2] != '3') {
cout << "Bad result on line " << __LINE__ << endl;
exit(EXIT_FAILURE);
}
};

// Extract to rvalue blob
auto numbers_sara = db << "SELECT numbers from person where name = ?;" << "sara" >> blob(vector<double>());

if(numbers_sara.size() != 3 || numbers_sara[0] != 1 || numbers_sara[1] != 2 || numbers_sara[2] != 3) {
cout << "Bad result on line " << __LINE__ << endl;
exit(EXIT_FAILURE);
}
//else {
//db << "SELECT numbers from person where name = ?;" << "sara" >> [](vector<double> numbers_sara){
//for(auto e : numbers_sara) cout << e << ' '; cout << endl;
//};
//}

}
catch(sqlite_exception e)
{
Expand Down
11 changes: 6 additions & 5 deletions tests/nullptr_uniqueptr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,25 @@ int main() {
try {
database db(":memory:");
db << "CREATE TABLE tbl (id integer,age integer, name string, img blob);";
db << "INSERT INTO tbl VALUES (?, ?, ?, ?);" << 1 << 24 << "bob" << vector<int> { 1, 2 , 3};
db << "INSERT INTO tbl VALUES (?, ?, ?, ?);" << 1 << 24 << "bob" << blob(vector<int> { 1, 2 , 3});
unique_ptr<string> ptr_null;
db << "INSERT INTO tbl VALUES (?, ?, ?, ?);" << 2 << nullptr << ptr_null << nullptr;

db << "select age,name,img from tbl where id = 1" >> [](unique_ptr<int> age_p, unique_ptr<string> name_p, unique_ptr<vector<int>> img_p) {
db << "select age,name,img from tbl where id = 1" >> [](unique_ptr<int> age_p, unique_ptr<string> name_p, unique_ptr<blob_t<vector<int>>> img_p) {
if(age_p == nullptr || name_p == nullptr || img_p == nullptr) {
cerr << "ERROR: values should not be null" << std::endl;
exit(EXIT_FAILURE);
}

cout << "age:" << *age_p << " name:" << *name_p << " img:";
for(auto i : *img_p)
for(auto i : img_p->vec)
cout << i << ",";
cout << endl;
};

db << "select age,name,img from tbl where id = 2" >> [](unique_ptr<int> age_p, unique_ptr<string> name_p, unique_ptr<vector<int>> img_p) {
if(age_p != nullptr || name_p != nullptr || img_p != nullptr) {

db << "select age,name,img from tbl where id = 2" >> [](unique_ptr<int> age_p, unique_ptr<string> name_p, blob_t<vector<int>> img_p) {
if(age_p != nullptr || name_p != nullptr || !img_p.vec.empty()) {
cerr << "ERROR: values should be nullptr" << std::endl;
exit(EXIT_FAILURE);
}
Expand Down