diff --git a/README.md b/README.md index 5f1d1d8f..cbe16938 100644 --- a/README.md +++ b/README.md @@ -194,12 +194,43 @@ Use `std::vector` to store and retrieve blob data. }; ``` -Dealing with NULL values +NULL values ===== -If you have databases where some rows may be null, you can use boost::optional to retain the NULL value between C++ variables and the database. Note that you must enable the boost support by defining _MODERN_SQLITE_BOOST_OPTIONAL_SUPPORT befor importing the header. +If you have databases where some rows may be null, you can use `std::unique_ptr` to retain the NULL values between C++ variables and the database. ```c++ +db << "CREATE TABLE tbl (id integer,age integer, name string, img blob);"; +db << "INSERT INTO tbl VALUES (?, ?, ?, ?);" << 1 << 24 << "bob" << vector { 1, 2 , 3}; +unique_ptr ptr_null; // you can even bind empty unique_ptr +db << "INSERT INTO tbl VALUES (?, ?, ?, ?);" << 2 << nullptr << ptr_null << nullptr; + +db << "select age,name,img from tbl where id = 1" + >> [](unique_ptr age_p, unique_ptr name_p, unique_ptr> img_p) { + if(age_p == nullptr || name_p == nullptr || img_p == nullptr) { + cerr << "ERROR: values should not be null" << std::endl; + } + + cout << "age:" << *age_p << " name:" << *name_p << " img:"; + for(auto i : *img_p) cout << i << ","; cout << endl; + }; + +db << "select age,name,img from tbl where id = 2" + >> [](unique_ptr age_p, unique_ptr name_p, unique_ptr> img_p) { + if(age_p != nullptr || name_p != nullptr || img_p != nullptr) { + cerr << "ERROR: values should be nullptr" << std::endl; + exit(EXIT_FAILURE); + } + + cout << "OK all three values are nullptr" << endl; + }; +``` + +NULL values (DEPRICATED) +===== +**Note: this option is deprecated and will be removed in future versions.** +You can enable boost support by defining _MODERN_SQLITE_BOOST_OPTIONAL_SUPPORT before importing sqlite_modern_cpp header. +```c++ #define _MODERN_SQLITE_BOOST_OPTIONAL_SUPPORT #include diff --git a/hdr/sqlite_modern_cpp.h b/hdr/sqlite_modern_cpp.h index 49ba5b57..6a864a50 100644 --- a/hdr/sqlite_modern_cpp.h +++ b/hdr/sqlite_modern_cpp.h @@ -210,8 +210,13 @@ namespace sqlite { template friend database_binder::chain_type& operator <<(database_binder::chain_type& db, const T& val); template friend void get_col_from_db(database_binder& db, int inx, T& val); + /* for vector support */ template friend database_binder::chain_type& operator <<(database_binder::chain_type& db, const std::vector& val); template friend void get_col_from_db(database_binder& db, int inx, std::vector& val); + /* for nullptr & unique_ptr support */ + friend database_binder::chain_type& operator <<(database_binder::chain_type& db, std::nullptr_t); + template friend database_binder::chain_type& operator <<(database_binder::chain_type& db, const std::unique_ptr& val); + template friend void get_col_from_db(database_binder& db, int inx, std::unique_ptr& val); template friend T operator++(database_binder& db, int); @@ -458,6 +463,35 @@ namespace sqlite { } } + /* for nullptr support */ + inline database_binder::chain_type& operator <<(database_binder::chain_type& db, std::nullptr_t) { + int hresult; + if((hresult = sqlite3_bind_null(db->_stmt.get(), db->_inx)) != SQLITE_OK) { + exceptions::throw_sqlite_error(hresult); + } + ++db->_inx; + return db; + } + /* for nullptr support */ + template inline database_binder::chain_type& operator <<(database_binder::chain_type& db, const std::unique_ptr& val) { + if(val) + db << *val; + else + db << nullptr; + return db; + } + + /* for unique_ptr support */ + template inline void get_col_from_db(database_binder& db, int inx, std::unique_ptr& _ptr_) { + if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) { + _ptr_ = nullptr; + } else { + auto underling_ptr = new T(); + get_col_from_db(db, inx, *underling_ptr); + _ptr_.reset(underling_ptr); + } + } + // std::string template<> inline void get_col_from_db(database_binder& db, int inx, std::string & s) { if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) { diff --git a/tests/nullptr_uniqueptr.cc b/tests/nullptr_uniqueptr.cc new file mode 100644 index 00000000..7bc193ae --- /dev/null +++ b/tests/nullptr_uniqueptr.cc @@ -0,0 +1,47 @@ +#include +#include +#include +#include +using namespace std; +using namespace sqlite; + +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 { 1, 2 , 3}; + unique_ptr ptr_null; + db << "INSERT INTO tbl VALUES (?, ?, ?, ?);" << 2 << nullptr << ptr_null << nullptr; + + db << "select age,name,img from tbl where id = 1" >> [](unique_ptr age_p, unique_ptr name_p, unique_ptr> 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) cout << i << ","; cout << endl; + }; + + db << "select age,name,img from tbl where id = 2" >> [](unique_ptr age_p, unique_ptr name_p, unique_ptr> img_p) { + if(age_p != nullptr || name_p != nullptr || img_p != nullptr) { + cerr << "ERROR: values should be nullptr" << std::endl; + exit(EXIT_FAILURE); + } + + cout << "OK all three values are nullptr" << endl; + }; + + } catch(sqlite_exception e) { + cout << "Sqlite error " << e.what() << endl; + exit(EXIT_FAILURE); + } catch(...) { + cout << "Unknown error\n"; + exit(EXIT_FAILURE); + } + + cout << "OK\n"; + exit(EXIT_SUCCESS); + return 0; +}