You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
143 lines
4.8 KiB
143 lines
4.8 KiB
///
|
|
/// Copyright 2015 Oliver Giles
|
|
///
|
|
/// This file is part of Laminar
|
|
///
|
|
/// Laminar is free software: you can redistribute it and/or modify
|
|
/// it under the terms of the GNU General Public License as published by
|
|
/// the Free Software Foundation, either version 3 of the License, or
|
|
/// (at your option) any later version.
|
|
///
|
|
/// Laminar is distributed in the hope that it will be useful,
|
|
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
/// GNU General Public License for more details.
|
|
///
|
|
/// You should have received a copy of the GNU General Public License
|
|
/// along with Laminar. If not, see <http://www.gnu.org/licenses/>
|
|
///
|
|
#ifndef _LAMINAR_DATABASE_H_
|
|
#define _LAMINAR_DATABASE_H_
|
|
|
|
#include <string>
|
|
#include <functional>
|
|
|
|
struct sqlite3;
|
|
struct sqlite3_stmt;
|
|
|
|
// This is a small sqlite wrapper using some clever template action
|
|
// to somewhat reduce verbosity. Usage:
|
|
// db.stmt("SELECT result WHERE name = ?")
|
|
// .bind(name)
|
|
// .fetch([](int result) {
|
|
// // function called for each retrieved row
|
|
// doSomething(result);
|
|
// });
|
|
class Database {
|
|
public:
|
|
Database(const char* path);
|
|
~Database();
|
|
|
|
private:
|
|
// Represents a database statement. Call Database::stmt() to get
|
|
// one, then call bind(), fetch() or exec() on the returned object
|
|
class Statement {
|
|
private:
|
|
// Internal template helper that defines the type
|
|
// in the variadic type array Args at offset N
|
|
template<int N, typename T, typename...Args>
|
|
struct typeindex : typeindex<N-1, Args...> {};
|
|
template<typename T, typename...Args>
|
|
struct typeindex<0, T, Args...> { typedef T type; };
|
|
|
|
public:
|
|
Statement(sqlite3* db, const char* query);
|
|
~Statement();
|
|
|
|
// Bind several parameters in a single call. They are bound
|
|
// by index in the order passed into this function
|
|
template<typename...Args>
|
|
Statement& bind(Args...args) {
|
|
return bindRecursive<Args...>(1, args...);
|
|
}
|
|
// Fetch columns. Supply a callback that will be executed for
|
|
// each row in the resultset, with arguments matching the
|
|
// expected column types
|
|
template<typename...Args>
|
|
void fetch(typename typeindex<0, std::function<void(Args...)>>::type callback) {
|
|
FetchMarshaller<Args...> fm(this, callback);
|
|
}
|
|
// execute without fetching any parameters. Intended for
|
|
// non-SELECT statements;
|
|
bool exec();
|
|
|
|
private:
|
|
// Internal template helper used to unpack arguments into
|
|
// the fetch callback.
|
|
template<int...N> struct rng { };
|
|
// Internal template helper to generate a rng<> object:
|
|
// genrng<4>::type is rng<0,1,2,3>
|
|
template<int J, int...N>
|
|
struct genrng : genrng<J-1, J-1, N...> {};
|
|
template<int...N>
|
|
struct genrng<0, N...> { typedef rng<N...> type; };
|
|
|
|
template<typename...Args>
|
|
struct FetchMarshaller {
|
|
FetchMarshaller(Statement* st, std::function<void(Args...)> cb){
|
|
marshal(st, cb, typename genrng<sizeof...(Args)>::type());
|
|
}
|
|
template<int...N>
|
|
void marshal(Statement* st, std::function<void(Args...)> cb, rng<N...>) {
|
|
while(st->row()) {
|
|
cb(st->fetchColumn<typename typeindex<N, Args...>::type>(N)...);
|
|
}
|
|
}
|
|
};
|
|
template<typename...Args>
|
|
friend class FetchMarshaller;
|
|
|
|
bool row();
|
|
|
|
template<typename T, typename...Args>
|
|
Statement& bindRecursive(int i, T v, Args...args) {
|
|
bindValue(i, v); // specialization must exist for T
|
|
return bindRecursive(i + 1, args...);
|
|
}
|
|
// template terminating condition
|
|
Statement& bindRecursive(int) {
|
|
return *this;
|
|
}
|
|
|
|
// Bind value specializations
|
|
void bindValue(int i, int e);
|
|
void bindValue(int i, const char* e);
|
|
void bindValue(int i, std::string e);
|
|
|
|
// Declaration for fetch column interface,
|
|
// intentionally missing definition
|
|
template<typename T>
|
|
T fetchColumn(int col);
|
|
|
|
sqlite3_stmt* stmt;
|
|
};
|
|
|
|
public:
|
|
Statement stmt(const char* q) {
|
|
return Statement(hdl, q);
|
|
}
|
|
// shorthand
|
|
bool exec(const char* q) { return Statement(hdl, q).exec(); }
|
|
private:
|
|
|
|
sqlite3* hdl;
|
|
};
|
|
|
|
// specialization declarations, defined in source file
|
|
template<> std::string Database::Statement::fetchColumn(int col);
|
|
template<> const char* Database::Statement::fetchColumn(int col);
|
|
template<> int Database::Statement::fetchColumn(int col);
|
|
template<> time_t Database::Statement::fetchColumn(int col);
|
|
|
|
#endif // _LAMINAR_DATABASE_H_
|