summaryrefslogtreecommitdiff
path: root/libsqlitepp
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2015-04-30 00:27:27 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2015-04-30 00:27:27 +0100
commitf634bafc38bb948e41f829b16f03e2f0b8ee8f93 (patch)
treec4f8ff17614b68531355e8047d81e96719101d10 /libsqlitepp
parentIgnore Vim swap files (diff)
downloadlibdbpp-sqlite-f634bafc38bb948e41f829b16f03e2f0b8ee8f93.tar.bz2
libdbpp-sqlite-f634bafc38bb948e41f829b16f03e2f0b8ee8f93.tar.xz
libdbpp-sqlite-f634bafc38bb948e41f829b16f03e2f0b8ee8f93.zip
Adds support for SQLite, SQLite mocking and some basic tests
Diffstat (limited to 'libsqlitepp')
-rw-r--r--libsqlitepp/Jamfile.jam19
-rw-r--r--libsqlitepp/command.cpp100
-rw-r--r--libsqlitepp/command.h39
-rw-r--r--libsqlitepp/connection.cpp128
-rw-r--r--libsqlitepp/connection.h39
-rw-r--r--libsqlitepp/error.cpp29
-rw-r--r--libsqlitepp/error.h24
-rw-r--r--libsqlitepp/modifycommand.cpp31
-rw-r--r--libsqlitepp/modifycommand.h25
-rw-r--r--libsqlitepp/selectcommand.cpp80
-rw-r--r--libsqlitepp/selectcommand.h21
11 files changed, 535 insertions, 0 deletions
diff --git a/libsqlitepp/Jamfile.jam b/libsqlitepp/Jamfile.jam
new file mode 100644
index 0000000..cac3186
--- /dev/null
+++ b/libsqlitepp/Jamfile.jam
@@ -0,0 +1,19 @@
+alias glibmm : : : :
+ <cflags>"`pkg-config --cflags glibmm-2.4`"
+ <linkflags>"`pkg-config --libs glibmm-2.4`"
+ ;
+
+lib libsqlite : : <name>sqlite3 ;
+
+lib sqlitepp :
+ [ glob *.cpp ] :
+ <cflags>-fPIC
+ <library>glibmm
+ <library>libsqlite
+ <library>../libdbpp
+ <include>../libmisc
+ : :
+ <include>.
+ <library>glibmm
+ <library>../libdbpp
+ ;
diff --git a/libsqlitepp/command.cpp b/libsqlitepp/command.cpp
new file mode 100644
index 0000000..ce2758b
--- /dev/null
+++ b/libsqlitepp/command.cpp
@@ -0,0 +1,100 @@
+#include "command.h"
+#include "connection.h"
+#include <stdlib.h>
+#include <string.h>
+
+SQLite::Command::Command(const Connection * conn, const std::string & sql) :
+ DB::Command(sql),
+ c(conn)
+{
+ if (sqlite3_prepare_v2(conn->db, sql.c_str(), sql.length(), &stmt, NULL) != SQLITE_OK) {
+ throw Error(sqlite3_errmsg(conn->db));
+ }
+}
+
+SQLite::Command::~Command()
+{
+ sqlite3_finalize(stmt);
+}
+
+void
+SQLite::Command::bindParamI(unsigned int n, int v)
+{
+ if (sqlite3_bind_int(stmt, n + 1, v) != SQLITE_OK) {
+ throw Error(sqlite3_errmsg(c->db));
+ }
+}
+void
+SQLite::Command::bindParamI(unsigned int n, long int v)
+{
+ if (sqlite3_bind_int64(stmt, n + 1, v) != SQLITE_OK) {
+ throw Error(sqlite3_errmsg(c->db));
+ }
+}
+void
+SQLite::Command::bindParamI(unsigned int n, long long int v)
+{
+ if (sqlite3_bind_int64(stmt, n + 1, v) != SQLITE_OK) {
+ throw Error(sqlite3_errmsg(c->db));
+ }
+}
+void
+SQLite::Command::bindParamI(unsigned int n, unsigned int v)
+{
+ if (sqlite3_bind_int64(stmt, n + 1, v) != SQLITE_OK) {
+ throw Error(sqlite3_errmsg(c->db));
+ }
+}
+void
+SQLite::Command::bindParamI(unsigned int n, long unsigned int v)
+{
+ if (sqlite3_bind_int64(stmt, n + 1, v) != SQLITE_OK) {
+ throw Error(sqlite3_errmsg(c->db));
+ }
+}
+void
+SQLite::Command::bindParamI(unsigned int n, long long unsigned int v)
+{
+ if (sqlite3_bind_int64(stmt, n + 1, v) != SQLITE_OK) {
+ throw Error(sqlite3_errmsg(c->db));
+ }
+}
+void
+SQLite::Command::bindParamF(unsigned int n, double v)
+{
+ if (sqlite3_bind_double(stmt, n + 1, v) != SQLITE_OK) {
+ throw Error(sqlite3_errmsg(c->db));
+ }
+}
+void
+SQLite::Command::bindParamF(unsigned int n, float v)
+{
+ if (sqlite3_bind_double(stmt, n + 1, v) != SQLITE_OK) {
+ throw Error(sqlite3_errmsg(c->db));
+ }
+}
+void
+SQLite::Command::bindParamS(unsigned int n, const Glib::ustring & s)
+{
+ if (sqlite3_bind_text(stmt, n + 1, s.c_str(), s.length(), SQLITE_STATIC) != SQLITE_OK) {
+ throw Error(sqlite3_errmsg(c->db));
+ }
+}
+void
+SQLite::Command::bindParamT(unsigned int, const boost::posix_time::time_duration &)
+{
+ throw Error("Not supported");
+}
+void
+SQLite::Command::bindParamT(unsigned int, const boost::posix_time::ptime &)
+{
+ throw Error("Not supported");
+}
+void
+SQLite::Command::bindNull(unsigned int n)
+{
+ if (sqlite3_bind_null(stmt, n + 1) != SQLITE_OK) {
+ throw Error(sqlite3_errmsg(c->db));
+ }
+}
+
diff --git a/libsqlitepp/command.h b/libsqlitepp/command.h
new file mode 100644
index 0000000..ee099e1
--- /dev/null
+++ b/libsqlitepp/command.h
@@ -0,0 +1,39 @@
+#ifndef SQLITE_COMMAND_H
+#define SQLITE_COMMAND_H
+
+#include "../libdbpp/command.h"
+#include <sqlite3.h>
+
+namespace SQLite {
+ class Connection;
+ class Command : public virtual DB::Command {
+ public:
+ Command(const Connection *, const std::string & sql);
+ virtual ~Command() = 0;
+
+ void bindParamI(unsigned int, int) override;
+ void bindParamI(unsigned int, long int) override;
+ void bindParamI(unsigned int, long long int) override;
+ void bindParamI(unsigned int, unsigned int) override;
+ void bindParamI(unsigned int, long unsigned int) override;
+ void bindParamI(unsigned int, long long unsigned int) override;
+
+ void bindParamF(unsigned int, double) override;
+ void bindParamF(unsigned int, float) override;
+
+ void bindParamS(unsigned int, const Glib::ustring&) override;
+
+ void bindParamT(unsigned int, const boost::posix_time::time_duration &) override;
+ void bindParamT(unsigned int, const boost::posix_time::ptime &) override;
+
+ void bindNull(unsigned int) override;
+
+ protected:
+ const Connection * c;
+ sqlite3_stmt * stmt;
+ };
+}
+
+#endif
+
+
diff --git a/libsqlitepp/connection.cpp b/libsqlitepp/connection.cpp
new file mode 100644
index 0000000..50df13e
--- /dev/null
+++ b/libsqlitepp/connection.cpp
@@ -0,0 +1,128 @@
+#include "connection.h"
+#include "error.h"
+#include "selectcommand.h"
+#include "modifycommand.h"
+
+SQLite::Connection::Connection(const std::string & str) :
+ txDepth(0),
+ rolledback(false)
+{
+ if (sqlite3_open(str.c_str(), &db) != SQLITE_OK) {
+ if (db) {
+ std::string err(sqlite3_errmsg(db));
+ sqlite3_close(db);
+ throw Error(err.c_str());
+ }
+ throw Error("Unknown error opening database");
+ }
+}
+
+SQLite::Connection::~Connection()
+{
+ sqlite3_close(db);
+}
+
+void
+SQLite::Connection::finish() const
+{
+ if (txDepth != 0) {
+ rollbackTx();
+ throw Error("Transaction still open");
+ }
+}
+
+int
+SQLite::Connection::beginTx() const
+{
+ if (txDepth == 0) {
+ if (sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL) != SQLITE_OK) {
+ throw Error(sqlite3_errmsg(db));
+ }
+ rolledback = false;
+ }
+ return ++txDepth;
+}
+
+int
+SQLite::Connection::commitTx() const
+{
+ if (rolledback) {
+ return rollbackTx();
+ }
+ if (--txDepth == 0) {
+ if (sqlite3_exec(db, "COMMIT TRANSACTION", NULL, NULL, NULL) != SQLITE_OK) {
+ throw Error(sqlite3_errmsg(db));
+ }
+ }
+ return txDepth;
+}
+
+int
+SQLite::Connection::rollbackTx() const
+{
+ if (--txDepth == 0) {
+ if (sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL) != SQLITE_OK) {
+ throw Error(sqlite3_errmsg(db));
+ }
+ }
+ else {
+ rolledback = true;
+ }
+ return txDepth;
+}
+
+bool
+SQLite::Connection::inTx() const
+{
+ return txDepth;
+}
+
+DB::BulkDeleteStyle
+SQLite::Connection::bulkDeleteStyle() const
+{
+ return DB::BulkDeleteUsingUsingAlias;
+}
+
+DB::BulkUpdateStyle
+SQLite::Connection::bulkUpdateStyle() const
+{
+ return DB::BulkUpdateUsingJoin;
+}
+
+void
+SQLite::Connection::ping() const
+{
+ // Can this fail?
+}
+
+
+DB::SelectCommand *
+SQLite::Connection::newSelectCommand(const std::string & sql) const
+{
+ return new SelectCommand(this, sql);
+}
+
+DB::ModifyCommand *
+SQLite::Connection::newModifyCommand(const std::string & sql) const
+{
+ return new ModifyCommand(this, sql);
+}
+
+void
+SQLite::Connection::beginBulkUpload(const char *, const char *) const
+{
+ throw Error("Not implemented");
+}
+
+void
+SQLite::Connection::endBulkUpload(const char *) const
+{
+ throw Error("Not implemented");
+}
+
+size_t
+SQLite::Connection::bulkUploadData(const char *, size_t) const
+{
+ throw Error("Not implemented");
+}
+
diff --git a/libsqlitepp/connection.h b/libsqlitepp/connection.h
new file mode 100644
index 0000000..aa73036
--- /dev/null
+++ b/libsqlitepp/connection.h
@@ -0,0 +1,39 @@
+#ifndef SQLITE_CONNECTION_H
+#define SQLITE_CONNECTION_H
+
+#include "../libdbpp/connection.h"
+#include "error.h"
+#include <sqlite3.h>
+
+namespace SQLite {
+ class Connection : public DB::Connection {
+ public:
+ Connection(const std::string & info);
+ ~Connection();
+
+ void finish() const;
+ int beginTx() const;
+ int commitTx() const;
+ int rollbackTx() const;
+ bool inTx() const;
+ void ping() const;
+ DB::BulkDeleteStyle bulkDeleteStyle() const;
+ DB::BulkUpdateStyle bulkUpdateStyle() const;
+
+ DB::SelectCommand * newSelectCommand(const std::string & sql) const;
+ DB::ModifyCommand * newModifyCommand(const std::string & sql) const;
+
+ void beginBulkUpload(const char *, const char *) const;
+ void endBulkUpload(const char *) const;
+ size_t bulkUploadData(const char *, size_t) const;
+
+ sqlite3 * db;
+
+ private:
+ mutable unsigned int txDepth;
+ mutable bool rolledback;
+ };
+}
+
+#endif
+
diff --git a/libsqlitepp/error.cpp b/libsqlitepp/error.cpp
new file mode 100644
index 0000000..9bdf80b
--- /dev/null
+++ b/libsqlitepp/error.cpp
@@ -0,0 +1,29 @@
+#include "error.h"
+#include <string.h>
+
+SQLite::Error::Error() :
+ msg(NULL)
+{
+}
+
+SQLite::Error::Error(const SQLite::Error & e) :
+ msg(e.msg ? strdup(e.msg) : NULL)
+{
+}
+
+SQLite::Error::Error(const char * e) :
+ msg(e ? strdup(e) : NULL)
+{
+}
+
+SQLite::Error::~Error() throw()
+{
+ free(msg);
+}
+
+const char *
+SQLite::Error::what() const throw()
+{
+ return msg ? msg : "No message";
+}
+
diff --git a/libsqlitepp/error.h b/libsqlitepp/error.h
new file mode 100644
index 0000000..a7f0ae1
--- /dev/null
+++ b/libsqlitepp/error.h
@@ -0,0 +1,24 @@
+#ifndef SQLITE_ERROR_H
+#define SQLITE_ERROR_H
+
+#include "../libdbpp/error.h"
+
+namespace SQLite {
+ class Error : public DB::Error {
+ public:
+ Error();
+ Error(const Error &);
+ Error(const char *);
+ ~Error() throw();
+
+ const char * what() const throw();
+
+ private:
+ char * msg;
+ };
+ class ConnectionError : public Error, public virtual DB::ConnectionError {
+ };
+}
+
+#endif
+
diff --git a/libsqlitepp/modifycommand.cpp b/libsqlitepp/modifycommand.cpp
new file mode 100644
index 0000000..1b44cf4
--- /dev/null
+++ b/libsqlitepp/modifycommand.cpp
@@ -0,0 +1,31 @@
+#include "modifycommand.h"
+#include "error.h"
+#include <stdlib.h>
+#include "connection.h"
+
+SQLite::ModifyCommand::ModifyCommand(const Connection * conn, const std::string & sql) :
+ DB::Command(sql),
+ DB::ModifyCommand(sql),
+ SQLite::Command(conn, sql)
+{
+}
+
+SQLite::ModifyCommand::~ModifyCommand()
+{
+}
+
+unsigned int
+SQLite::ModifyCommand::execute(bool anc)
+{
+ if (sqlite3_step(stmt) != SQLITE_DONE) {
+ sqlite3_reset(stmt);
+ throw Error(sqlite3_errmsg(c->db));
+ }
+ unsigned int rows = sqlite3_changes(c->db);
+ sqlite3_reset(stmt);
+ if (rows == 0 && !anc) {
+ throw Error("No rows affected");
+ }
+ return rows;
+}
+
diff --git a/libsqlitepp/modifycommand.h b/libsqlitepp/modifycommand.h
new file mode 100644
index 0000000..ffb1205
--- /dev/null
+++ b/libsqlitepp/modifycommand.h
@@ -0,0 +1,25 @@
+#ifndef SQLITE_MODIFYCOMMAND_H
+#define SQLITE_MODIFYCOMMAND_H
+
+#include "../libdbpp/modifycommand.h"
+#include "command.h"
+
+namespace SQLite {
+ class Connection;
+ class ModifyCommand : public DB::ModifyCommand, public Command {
+ public:
+ ModifyCommand(const Connection *, const std::string & sql);
+ virtual ~ModifyCommand();
+
+ unsigned int execute(bool);
+
+ private:
+ void prepare() const;
+ mutable bool prepared;
+ };
+}
+
+#endif
+
+
+
diff --git a/libsqlitepp/selectcommand.cpp b/libsqlitepp/selectcommand.cpp
new file mode 100644
index 0000000..a3d5ac7
--- /dev/null
+++ b/libsqlitepp/selectcommand.cpp
@@ -0,0 +1,80 @@
+#include "selectcommand.h"
+#include "connection.h"
+#include "error.h"
+#include <string.h>
+
+class Column : public DB::Column {
+ public:
+ Column(const Glib::ustring & n, unsigned int i, sqlite3_stmt * s) :
+ DB::Column(n, i),
+ stmt(s)
+ {
+ }
+
+ bool isNull() const {
+ return (SQLITE_NULL == sqlite3_column_type(stmt, colNo));
+ }
+
+ void apply(DB::HandleField & h) const {
+ switch (sqlite3_column_type(stmt, colNo)) {
+ case SQLITE_INTEGER:
+ h.integer(sqlite3_column_int64(stmt, colNo));
+ return;
+ case SQLITE_FLOAT:
+ h.floatingpoint(sqlite3_column_double(stmt, colNo));
+ return;
+ case SQLITE_TEXT:
+ {
+ auto t = sqlite3_column_text(stmt, colNo);
+ auto l = sqlite3_column_bytes(stmt, colNo);
+ h.string(reinterpret_cast<const char *>(t), l);
+ return;
+ }
+ case SQLITE_NULL:
+ h.null();
+ return;
+ case SQLITE_BLOB:
+ throw std::runtime_error("Blobs not supported");
+ }
+
+ }
+
+ void rebind(DB::Command*, unsigned int) const {
+ throw std::runtime_error("Not implemented");
+ }
+
+ private:
+ sqlite3_stmt * const stmt;
+};
+
+SQLite::SelectCommand::SelectCommand(const Connection * conn, const std::string & sql) :
+ DB::Command(sql),
+ DB::SelectCommand(sql),
+ SQLite::Command(conn, sql)
+{
+}
+
+void
+SQLite::SelectCommand::execute()
+{
+ // No explicit execute required
+}
+
+bool
+SQLite::SelectCommand::fetch()
+{
+ switch (sqlite3_step(stmt)) {
+ case SQLITE_ROW:
+ if (columns.empty()) {
+ for (int c = sqlite3_data_count(stmt) - 1; c >= 0; c -= 1) {
+ columns.insert(DB::ColumnPtr(new Column(sqlite3_column_name(stmt, c), c, stmt)));
+ }
+ }
+ return true;
+ case SQLITE_DONE:
+ return false;
+ default:
+ throw Error(sqlite3_errmsg(c->db));
+ }
+}
+
diff --git a/libsqlitepp/selectcommand.h b/libsqlitepp/selectcommand.h
new file mode 100644
index 0000000..be8b02b
--- /dev/null
+++ b/libsqlitepp/selectcommand.h
@@ -0,0 +1,21 @@
+#ifndef SQLITE_SELECTCOMMAND_H
+#define SQLITE_SELECTCOMMAND_H
+
+#include "../libdbpp/selectcommand.h"
+#include "command.h"
+
+namespace SQLite {
+ class Connection;
+ class ColumnBase;
+ class SelectCommand : public DB::SelectCommand, public Command {
+ public:
+ SelectCommand(const Connection *, const std::string & sql);
+
+ bool fetch();
+ void execute();
+ };
+}
+
+#endif
+
+