Skip to content

Commit 54d2ef0

Browse files
author
Harald
committed
add sample code from the talk
0 parents  commit 54d2ef0

File tree

2 files changed

+347
-0
lines changed

2 files changed

+347
-0
lines changed

Makefile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
CFLAGS= -Wall -Wextra -g -pedantic
3+
CXXFLAGS= -Wall -Wextra -g -pedantic -std=c++11
4+
LDFLAGS= -lsqlite3
5+
#LDFLAGS= -z nodeflib
6+
7+
all: sample1
8+
9+
sample1: sample1.o
10+
g++ $(LDFLAGS) $< -o $@
11+
12+
%.o : %.cpp
13+
g++ $(CXXFLAGS) -c $<
14+
15+
16+
clean:
17+
rm -f *.o sample1
18+

sample1.cpp

Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
#include <memory>
2+
#include <cstdlib>
3+
#include <iostream>
4+
#include <sqlite3.h>
5+
6+
template< bool B, class T = void >
7+
using enable_if_t = typename std::enable_if<B,T>::type;
8+
9+
//
10+
// not_null
11+
// borrowed from GLS, https://github.com/Microsoft/GSL
12+
// backported to copmile with gcc 4.8 on RHEL 6
13+
//
14+
// Restricts a pointer or smart pointer to only hold non-null values.
15+
//
16+
// Has zero size overhead over T.
17+
//
18+
// If T is a pointer (i.e. T == U*) then
19+
// - allow construction from U* or U&
20+
// - disallow construction from nullptr_t
21+
// - disallow default construction
22+
// - ensure construction from U* fails with nullptr
23+
// - allow implicit conversion to U*
24+
//
25+
template <class T>
26+
class not_null
27+
{
28+
static_assert(std::is_assignable<T&, std::nullptr_t>::value, "T cannot be assigned nullptr.");
29+
30+
public:
31+
not_null(T t) : ptr_(t) { ensure_invariant(); }
32+
not_null& operator=(const T& t)
33+
{
34+
ptr_ = t;
35+
ensure_invariant();
36+
return *this;
37+
}
38+
39+
not_null(const not_null& other) = default;
40+
not_null& operator=(const not_null& other) = default;
41+
42+
template <typename U, typename Dummy = enable_if_t<std::is_convertible<U, T>::value>>
43+
not_null(const not_null<U>& other)
44+
{
45+
*this = other;
46+
}
47+
48+
template <typename U, typename Dummy = enable_if_t<std::is_convertible<U, T>::value>>
49+
not_null& operator=(const not_null<U>& other)
50+
{
51+
ptr_ = other.get();
52+
return *this;
53+
}
54+
55+
// prevents compilation when someone attempts to assign a nullptr
56+
not_null(std::nullptr_t) = delete;
57+
not_null(int) = delete;
58+
not_null<T>& operator=(std::nullptr_t) = delete;
59+
not_null<T>& operator=(int) = delete;
60+
61+
T get() const
62+
{
63+
#ifdef _MSC_VER
64+
__assume(ptr_ != nullptr);
65+
#endif
66+
return ptr_;
67+
} // the assume() should help the optimizer
68+
69+
operator T() const { return get(); }
70+
T operator->() const { return get(); }
71+
72+
bool operator==(const T& rhs) const { return ptr_ == rhs; }
73+
bool operator!=(const T& rhs) const { return !(*this == rhs); }
74+
private:
75+
T ptr_;
76+
77+
// we assume that the compiler can hoist/prove away most of the checks inlined from this
78+
// function
79+
// if not, we could make them optional via conditional compilation
80+
void ensure_invariant() const { Ensure(ptr_ != nullptr); }
81+
82+
// tmpfix, until inlcude the defaultu assing
83+
void Ensure(bool flag) const { if(not flag) throw "Ensure failed" ;}
84+
85+
// unwanted operators...pointers only point to single objects!
86+
// TODO ensure all arithmetic ops on this type are unavailable
87+
not_null<T>& operator++() = delete;
88+
not_null<T>& operator--() = delete;
89+
not_null<T> operator++(int) = delete;
90+
not_null<T> operator--(int) = delete;
91+
not_null<T>& operator+(size_t) = delete;
92+
not_null<T>& operator+=(size_t) = delete;
93+
not_null<T>& operator-(size_t) = delete;
94+
not_null<T>& operator-=(size_t) = delete;
95+
};
96+
97+
using database = std::unique_ptr<sqlite3, decltype(&sqlite3_close)> ;
98+
99+
database open_database(const char* name)
100+
{
101+
sqlite3* db = nullptr;
102+
auto rc = sqlite3_open (name, &db);
103+
if(rc != SQLITE_OK) {
104+
std::cerr << "Unable to open database '" << name << "': "
105+
<< sqlite3_errmsg (db);
106+
sqlite3_close (db);
107+
std::exit(EXIT_FAILURE);
108+
}
109+
return database{db, sqlite3_close} ;
110+
}
111+
112+
void execute (not_null<sqlite3*> db, const char* sql)
113+
{
114+
char* errmsg = 0;
115+
int rc = sqlite3_exec (db, sql, 0, 0, &errmsg);
116+
if (rc != SQLITE_OK) {
117+
std::cerr << "Unable to execute '" << sql << "': "
118+
<< errmsg ;
119+
sqlite3_free(errmsg) ;
120+
std::exit(EXIT_FAILURE);
121+
}
122+
}
123+
124+
125+
using statement = std::unique_ptr<sqlite3_stmt, decltype(&sqlite3_finalize)> ;
126+
127+
statement create_statement(not_null<sqlite3*> db, const std::string& sql)
128+
{
129+
sqlite3_stmt* stmt = nullptr;
130+
int rc = sqlite3_prepare_v2 (db,
131+
sql.c_str (), sql.length(),
132+
&stmt, nullptr);
133+
if (rc != SQLITE_OK) {
134+
std::cerr << "Unable to create statement '" << sql << "': "
135+
<< sqlite3_errmsg(db);
136+
std::exit(EXIT_FAILURE);
137+
}
138+
return statement(stmt, sqlite3_finalize);
139+
}
140+
141+
142+
using stmt_callback =
143+
std::function<bool(not_null<sqlite3_stmt*>)> ;
144+
145+
void run(not_null<sqlite3_stmt*> stmt,
146+
stmt_callback callback = stmt_callback{})
147+
{
148+
using reset_guard
149+
= std::unique_ptr<sqlite3_stmt, decltype (&sqlite3_reset)>;
150+
151+
auto reset = reset_guard (stmt.get(), &sqlite3_reset);
152+
153+
auto step_next = [&](int rc){
154+
if (rc == SQLITE_OK || rc == SQLITE_DONE)
155+
return false ;
156+
else if (rc == SQLITE_ROW)
157+
if(callback)
158+
return callback(stmt);
159+
// else ... some error handling
160+
return false ;
161+
};
162+
163+
while(step_next(sqlite3_step(stmt))) ;
164+
}
165+
166+
167+
168+
169+
bool dump_current_row(not_null<sqlite3_stmt*> stmt)
170+
{
171+
for (int i = 0 ; i < sqlite3_column_count(stmt); ++i) {
172+
auto columntype = sqlite3_column_type(stmt, i) ;
173+
174+
if(columntype == SQLITE_NULL) {
175+
std::cout << "<NULL>" ;
176+
}
177+
else if (columntype == SQLITE_INTEGER){
178+
std::cout << sqlite3_column_int64(stmt, i);
179+
}
180+
else if (columntype == SQLITE_FLOAT){
181+
std::cout << sqlite3_column_double(stmt, i) ;
182+
}
183+
else if (columntype == SQLITE_TEXT ){
184+
auto first = sqlite3_column_text (stmt, i);
185+
std::size_t s = sqlite3_column_bytes (stmt, i);
186+
std::cout << "'" << (s > 0 ?
187+
std::string((const char*)first, s) : "") << "'";
188+
}
189+
else if (columntype == SQLITE_BLOB ){
190+
std::cout << "<BLO000B>" ;
191+
}
192+
std::cout << "|" ;
193+
}
194+
std::cout << "\n" ;
195+
return true ;
196+
}
197+
198+
199+
bool print_thing(not_null<sqlite3_stmt*> stmt) {
200+
201+
auto id = [&](){return sqlite3_column_int64(stmt, 0);} ;
202+
203+
auto name = [&](){ auto first = sqlite3_column_text (stmt, 1);
204+
std::size_t s = sqlite3_column_bytes (stmt, 1);
205+
return s > 0 ? std::string ((const char*)first, s)
206+
: std::string{};
207+
};
208+
auto value = [&]() {return sqlite3_column_double(stmt, 2);};
209+
210+
std::cout << id() << ", " << name() << ", " << value() << std::endl;
211+
return true ;
212+
}
213+
214+
215+
216+
int64_t key(not_null<sqlite3_stmt*> stmt)
217+
{
218+
return sqlite3_column_int64(stmt, 0) ;
219+
}
220+
221+
std::string value(not_null<sqlite3_stmt*> stmt)
222+
{
223+
const char* first = (const char*)sqlite3_column_text (stmt, 1);
224+
std::size_t s = sqlite3_column_bytes (stmt, 1);
225+
return s > 0 ? std::string (first, s) : std::string{};
226+
}
227+
228+
229+
void parameter(not_null<sqlite3_stmt*> stmt, int index, int64_t value)
230+
{
231+
auto rc = sqlite3_bind_int64 (stmt, index, value);
232+
if (rc != SQLITE_OK) throw "TODO" ;
233+
}
234+
235+
void parameter(not_null<sqlite3_stmt*> stmt, int index, double value)
236+
{
237+
auto rc = sqlite3_bind_double (stmt, index, value);
238+
if (rc != SQLITE_OK) throw "TODO" ;
239+
}
240+
241+
// real the same
242+
void parameter(not_null<sqlite3_stmt*> stmt,
243+
int index,
244+
const std::string& value)
245+
{
246+
auto rc = sqlite3_bind_text (stmt.get(), index,
247+
value.c_str (), value.size (),
248+
SQLITE_TRANSIENT);
249+
250+
if (rc != SQLITE_OK) throw "TODO" ;
251+
}
252+
// blob the same, SQLITE_STATIC/TRANSIENT copy + owner
253+
254+
255+
256+
struct Transaction
257+
{
258+
Transaction(not_null<sqlite3*> db) : _db{db}{
259+
execute(_db, "BEGIN TRANSACTION;") ;
260+
}
261+
~Transaction() {
262+
if(_db) execute(_db, "ROLLBACK TRANSACTION;") ;
263+
}
264+
void commit() {
265+
if(_db) execute(_db, "COMMIT TRANSACTION;") ;
266+
_db = nullptr ;
267+
}
268+
269+
Transaction (Transaction&&) = default ;
270+
271+
Transaction (Transaction&) = delete ;
272+
Transaction& operator=(Transaction&) = delete ;
273+
Transaction& operator=(Transaction&&) = delete ;
274+
275+
private: sqlite3* _db ;
276+
};
277+
278+
constexpr const char* create_things()
279+
{
280+
return R"~(BEGIN TRANSACTION ;
281+
CREATE TABLE things(id INTEGER PRIMARY KEY, name TEXT,value REAL);
282+
INSERT INTO things VALUES(1,'one', 1.1);
283+
INSERT INTO things VALUES(2,'two', 2.2);
284+
COMMIT TRANSACTION ;
285+
)~";
286+
}
287+
288+
289+
290+
statement create_things2(not_null<sqlite3*> db) {
291+
Transaction transaction(db) ;
292+
execute(db, R"~(CREATE TABLE things
293+
(id INTEGER PRIMARY KEY, name TEXT,value REAL); )~");
294+
295+
auto insert_thing = create_statement(db,
296+
"INSERT INTO things VALUES(@id,@name,@value);");
297+
// create the identity thing
298+
parameter(insert_thing.get(), 1, int64_t{0}) ;
299+
parameter(insert_thing.get(), 2, "") ;
300+
parameter(insert_thing.get(), 3, double{0.0}) ;
301+
run (insert_thing.get()) ;
302+
transaction.commit() ;
303+
// return createor
304+
return insert_thing ;
305+
}
306+
307+
308+
void main1()
309+
{
310+
auto db = open_database(":memory:");
311+
auto add_thing = create_things2(db.get());
312+
{ Transaction transaction(db.get()) ;
313+
parameter(add_thing.get(), 1, int64_t{1}) ;
314+
parameter(add_thing.get(), 2, "first") ;
315+
parameter(add_thing.get(), 3, "second") ; // Mistake !!
316+
run(add_thing.get());
317+
transaction.commit() ;
318+
}
319+
auto stmt = create_statement(db.get(), "SELECT * FROM things;");
320+
run(stmt.get(), dump_current_row);
321+
run (stmt.get(), print_thing);
322+
}
323+
324+
325+
int main()
326+
{
327+
main1();
328+
}
329+

0 commit comments

Comments
 (0)