Skip to content

Commit ee1654e

Browse files
committed
Merge pull request #32 from aminroosta/blob
Blob support with `std::vector<T>`
2 parents 471f8af + e3c9fdf commit ee1654e

File tree

3 files changed

+136
-17
lines changed

3 files changed

+136
-17
lines changed

Diff for: README.md

+33-15
Original file line numberDiff line numberDiff line change
@@ -93,22 +93,22 @@ It is possible to retain and reuse statments this will keep the query plan and i
9393
int tmp = 8;
9494
ps << tmp;
9595

96-
// now you can execute it with `operator>>` or `execute()`.
96+
// now you can execute it with `operator>>` or `execute()`.
9797
// If the statment was executed once it will not be executed again when it goes out of scope.
9898
// But beware that it will execute on destruction if it wasn't executed!
9999
ps >> [&](int a,int b){ ... };
100100

101101
// after a successfull execution the statment needs to be reset to be execute again. This will reset the bound values too!
102102
ps.reset();
103-
103+
104104
// If you dont need the returned values you can execute it like this
105105
ps.execute(); // the statment will not be reset!
106106

107107
// there is a convinience operator to execute and reset in one go
108108
ps++;
109109

110110
// To disable the execution of a statment when it goes out of scope and wasn't used
111-
ps.used(true); // or false if you want it to execute even if it was used
111+
ps.used(true); // or false if you want it to execute even if it was used
112112

113113
// Usage Example:
114114

@@ -131,9 +131,9 @@ Take this example on how to deal with a database backup using SQLITEs own functi
131131
132132
auto con = db.connection(); // get a handle to the DB we want to backup in our scope
133133
// this way we are sure the DB is open and ok while we backup
134-
134+
135135
// Init Backup and make sure its freed on exit or exceptions!
136-
auto state =
136+
auto state =
137137
std::unique_ptr<sqlite3_backup,decltype(&sqlite3_backup_finish)>(
138138
sqlite3_backup_init(backup.connection().get(), "main", con.get(), "main"),
139139
sqlite3_backup_finish
@@ -166,7 +166,7 @@ You can use transactions with `begin;`, `commit;` and `rollback;` commands.
166166
<< u"jack"
167167
<< 68.5;
168168
db << "commit;"; // commit all the changes.
169-
169+
170170
db << "begin;"; // begin another transaction ....
171171
db << "insert into user (age,name,weight) values (?,?,?);" // utf16 string
172172
<< 19
@@ -176,6 +176,24 @@ You can use transactions with `begin;`, `commit;` and `rollback;` commands.
176176

177177
```
178178

179+
Blob
180+
=====
181+
Use `std::vector<T>` to store and retrieve blob data.
182+
`T` could be `char,short,int,long,long long, float or double`.
183+
184+
```c++
185+
db << "CREATE TABLE person (name TEXT, numbers BLOB);";
186+
db << "INSERT INTO person VALUES (?, ?)" << "bob" << vector<int> { 1, 2, 3, 4};
187+
db << "INSERT INTO person VALUES (?, ?)" << "sara" << vector<double> { 1.0, 2.0, 3.0, 4.0};
188+
189+
vector<int> numbers_bob;
190+
db << "SELECT numbers from person where name = ?;" << "bob" >> numbers_bob;
191+
192+
db << "SELECT numbers from person where name = ?;" << "sara" >> [](vector<double> numbers_sara){
193+
for(auto e : numbers_sara) cout << e << ' '; cout << endl;
194+
};
195+
```
196+
179197
Dealing with NULL values
180198
=====
181199
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.
@@ -184,45 +202,45 @@ If you have databases where some rows may be null, you can use boost::optional t
184202
185203
#define _MODERN_SQLITE_BOOST_OPTIONAL_SUPPORT
186204
#include <sqlite_modern_cpp.h>
187-
205+
188206
struct User {
189207
long long _id;
190208
boost::optional<int> age;
191209
boost::optional<string> name;
192210
boost::optional<real> weight;
193211
};
194-
212+
195213
{
196214
User user;
197215
user.name = "bob";
198-
216+
199217
// Same database as above
200218
database db("dbfile.db");
201-
219+
202220
// Here, age and weight will be inserted as NULL in the database.
203221
db << "insert into user (age,name,weight) values (?,?,?);"
204222
<< user.age
205223
<< user.name
206224
<< user.weight;
207-
225+
208226
user._id = db.last_insert_rowid();
209227
}
210-
228+
211229
{
212230
// Here, the User instance will retain the NULL value(s) from the database.
213231
db << "select _id,age,name,weight from user where age > ? ;"
214232
<< 18
215233
>> [&](long long id,
216-
boost::optional<int> age,
234+
boost::optional<int> age,
217235
boost::optional<string> name
218236
boost::optional<real> weight) {
219-
237+
220238
User user;
221239
user._id = id;
222240
user.age = age;
223241
user.name = move(name);
224242
user.weight = weight;
225-
243+
226244
cout << "id=" << user._id
227245
<< " age = " << (user.age ? to_string(*user.age) ? string("NULL"))
228246
<< " name = " << (user.name ? *user.name : string("NULL"))

Diff for: hdr/sqlite_modern_cpp.h

+34-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <ctime>
77
#include <tuple>
88
#include <memory>
9+
#include <vector>
910

1011
#ifdef _MODERN_SQLITE_BOOST_OPTIONAL_SUPPORT
1112
#include <boost/optional.hpp>
@@ -188,17 +189,27 @@ namespace sqlite {
188189
}
189190

190191
template <typename Type>
191-
using is_sqlite_value = std::integral_constant<
192+
struct is_sqlite_value : public std::integral_constant<
192193
bool,
193194
std::is_floating_point<Type>::value
194195
|| std::is_integral<Type>::value
195196
|| std::is_same<std::string, Type>::value
196197
|| std::is_same<std::u16string, Type>::value
197198
|| std::is_same<sqlite_int64, Type>::value
198-
>;
199+
> { };
200+
template <typename Type>
201+
struct is_sqlite_value< std::vector<Type> > : public std::integral_constant<
202+
bool,
203+
std::is_floating_point<Type>::value
204+
|| std::is_integral<Type>::value
205+
|| std::is_same<sqlite_int64, Type>::value
206+
> { };
207+
199208

200209
template<typename T> friend database_binder::chain_type& operator <<(database_binder::chain_type& db, const T& val);
201210
template<typename T> friend void get_col_from_db(database_binder& db, int inx, T& val);
211+
template<typename T> friend database_binder::chain_type& operator <<(database_binder::chain_type& db, const std::vector<T>& val);
212+
template<typename T> friend void get_col_from_db(database_binder& db, int inx, std::vector<T>& val);
202213
template<typename T> friend T operator++(database_binder& db, int);
203214

204215

@@ -414,6 +425,27 @@ namespace sqlite {
414425
}
415426
}
416427

428+
// vector<T>
429+
template<typename T> inline database_binder::chain_type& operator<<(database_binder::chain_type& db, const std::vector<T>& vec) {
430+
void const* buf = reinterpret_cast<void const *>(vec.data());
431+
int bytes = vec.size() * sizeof(T);
432+
int hresult;
433+
if((hresult = sqlite3_bind_blob(db->_stmt.get(), db->_inx, buf, bytes, SQLITE_TRANSIENT)) != SQLITE_OK) {
434+
exceptions::throw_sqlite_error(hresult);
435+
}
436+
++db->_inx;
437+
return db;
438+
}
439+
template<typename T> inline void get_col_from_db(database_binder& db, int inx, std::vector<T>& vec) {
440+
if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) {
441+
vec.clear();
442+
} else {
443+
int bytes = sqlite3_column_bytes(db._stmt.get(), inx);
444+
T const* buf = reinterpret_cast<T const *>(sqlite3_column_blob(db._stmt.get(), inx));
445+
vec = std::vector<T>(buf, buf + bytes/sizeof(T));
446+
}
447+
}
448+
417449
// std::string
418450
template<> inline void get_col_from_db(database_binder& db, int inx, std::string & s) {
419451
if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) {

Diff for: tests/blob_example.cc

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#include <iostream>
2+
#include <cstdlib>
3+
#include <unistd.h>
4+
#include <vector>
5+
#include <string>
6+
#include <sqlite_modern_cpp.h>
7+
using namespace sqlite;
8+
using namespace std;
9+
10+
int main()
11+
{
12+
try
13+
{
14+
database db(":memory:");
15+
16+
db << "CREATE TABLE person (name TEXT, numbers BLOB);";
17+
db << "INSERT INTO person VALUES (?, ?)" << "bob" << vector<int> { 1, 2, 3, 4};
18+
db << "INSERT INTO person VALUES (?, ?)" << "jack" << vector<char> { '1', '2', '3', '4'};
19+
db << "INSERT INTO person VALUES (?, ?)" << "sara" << vector<double> { 1.0, 2.0, 3.0, 4.0};
20+
21+
vector<int> numbers_bob;
22+
db << "SELECT numbers from person where name = ?;" << "bob" >> numbers_bob;
23+
24+
if(numbers_bob.size() != 4 || numbers_bob[0] != 1
25+
|| numbers_bob[1] != 2 || numbers_bob[2] != 3 || numbers_bob[3] != 4 ) {
26+
cout << "Bad result on line " << __LINE__ << endl;
27+
exit(EXIT_FAILURE);
28+
}
29+
//else { for(auto e : numbers_bob) cout << e << ' '; cout << endl; }
30+
31+
vector<char> numbers_jack;
32+
db << "SELECT numbers from person where name = ?;" << "jack" >> numbers_jack;
33+
34+
if(numbers_jack.size() != 4 || numbers_jack[0] != '1'
35+
|| numbers_jack[1] != '2' || numbers_jack[2] != '3' || numbers_jack[3] != '4' ) {
36+
cout << "Bad result on line " << __LINE__ << endl;
37+
exit(EXIT_FAILURE);
38+
}
39+
//else { for(auto e : numbers_jack) cout << e << ' '; cout << endl; }
40+
41+
vector<double> numbers_sara;
42+
db << "SELECT numbers from person where name = ?;" << "sara" >> numbers_sara;
43+
44+
if(numbers_sara.size() != 4 || numbers_sara[0] != 1
45+
|| numbers_sara[1] != 2 || numbers_sara[2] != 3 || numbers_sara[3] != 4 ) {
46+
cout << "Bad result on line " << __LINE__ << endl;
47+
exit(EXIT_FAILURE);
48+
}
49+
//else {
50+
//db << "SELECT numbers from person where name = ?;" << "sara" >> [](vector<double> numbers_sara){
51+
//for(auto e : numbers_sara) cout << e << ' '; cout << endl;
52+
//};
53+
//}
54+
55+
}
56+
catch(sqlite_exception e)
57+
{
58+
cout << "Unexpected error " << e.what() << endl;
59+
exit(EXIT_FAILURE);
60+
}
61+
catch(...)
62+
{
63+
cout << "Unknown error\n";
64+
exit(EXIT_FAILURE);
65+
}
66+
67+
cout << "OK\n";
68+
exit(EXIT_SUCCESS);
69+
}

0 commit comments

Comments
 (0)