aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRobin Krahl <me@robin-krahl.de>2015-07-18 23:30:42 +0200
committerRobin Krahl <me@robin-krahl.de>2015-07-18 23:30:42 +0200
commit56b56f46f13a7838e113b529a75771a9fb07bdf7 (patch)
treef599b98c83a126845cc834ddbdce7d2b5e52213e /src
parent6228b0a15c86d2105a29cbccb48db6bd83b82f71 (diff)
downloadsqlitepp-56b56f46f13a7838e113b529a75771a9fb07bdf7.tar.gz
sqlitepp-56b56f46f13a7838e113b529a75771a9fb07bdf7.tar.bz2
Refactoring and update.
Diffstat (limited to 'src')
-rw-r--r--src/sqlitepp.cpp240
-rw-r--r--src/sqlitepp/sqlitepp.cc264
-rw-r--r--src/sqlitepp/sqlitepp_test.cc100
-rw-r--r--src/sqlitepptest.cpp108
4 files changed, 364 insertions, 348 deletions
diff --git a/src/sqlitepp.cpp b/src/sqlitepp.cpp
deleted file mode 100644
index 8cdb357..0000000
--- a/src/sqlitepp.cpp
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * (C) 2014 Robin Krahl
- * MIT license -- http://opensource.org/licenses/MIT
- */
-
-#include "sqlitepp.h"
-
-#include <exception>
-#include <iostream>
-#include <sstream>
-
-sqlitepp::Openable::Openable(const bool open, const std::string & name) :
- m_open(open), m_name(name) {
-}
-
-const bool sqlitepp::Openable::isOpen() const {
- return m_open;
-}
-
-void sqlitepp::Openable::requireOpen() const {
- if (!m_open) {
- throw std::logic_error(m_name + " is not open.");
- }
-}
-
-void sqlitepp::Openable::setOpen(const bool open) {
- m_open = open;
-}
-
-const std::string sqlitepp::DatabaseError::getErrorMessage(const int errorCode, const std::string & errorMessage) {
- std::ostringstream stringStream;
- stringStream << "Caught SQLite3 error " << errorCode << " meaning: " << errorMessage;
- return stringStream.str();
-}
-
-sqlitepp::DatabaseError::DatabaseError(const int errorCode) :
- std::runtime_error(getErrorMessage(errorCode, sqlite3_errstr(errorCode))), m_errorCode(errorCode) {
-}
-
-sqlitepp::DatabaseError::DatabaseError(const int errorCode, const std::string & errorMessage) :
- std::runtime_error(getErrorMessage(errorCode, errorMessage)), m_errorCode(errorCode)
-{
-}
-
-const int sqlitepp::DatabaseError::errorCode() const {
- return m_errorCode;
-}
-
-sqlitepp::Statement::Statement(sqlitepp::Database & database, const std::string & statement) :
- Openable(true, "Statement"), m_canRead(false) {
- database.requireOpen();
- int result = sqlite3_prepare_v2(database.m_handle, statement.c_str(), -1, &m_handle, NULL);
- if (result != SQLITE_OK) {
- throw DatabaseError(result, sqlite3_errmsg(database.m_handle));
- }
- if (m_handle == NULL) {
- throw std::runtime_error("Statement handle is NULL");
- }
-}
-
-sqlitepp::Statement::~Statement() {
- if (isOpen()) {
- // errors that could occur during finalizing are ignored as they have
- // already been handled!
- sqlite3_finalize(m_handle);
- setOpen(false);
- }
-}
-
-void sqlitepp::Statement::bindDouble(const int index, const double value) {
- requireOpen();
- handleBindResult(index, sqlite3_bind_double(m_handle, index, value));
-}
-
-void sqlitepp::Statement::bindDouble(const std::string & name, const double value) {
- bindDouble(getParameterIndex(name), value);
-}
-
-void sqlitepp::Statement::bindInt(const int index, const int value) {
- requireOpen();
- handleBindResult(index, sqlite3_bind_int(m_handle, index, value));
-}
-
-void sqlitepp::Statement::bindInt(const std::string & name, const int value) {
- bindInt(getParameterIndex(name), value);
-}
-
-void sqlitepp::Statement::bindString(const int index, const std::string & value) {
- requireOpen();
- handleBindResult(index, sqlite3_bind_text(m_handle, index, value.c_str(), -1, NULL));
-}
-
-void sqlitepp::Statement::bindString(const std::string & name, const std::string & value) {
- bindString(getParameterIndex(name), value);
-}
-
-const bool sqlitepp::Statement::canRead() const {
- return m_canRead;
-}
-
-const int sqlitepp::Statement::columnCount() const {
- requireOpen();
- requireCanRead();
- return sqlite3_column_count(m_handle);
-}
-
-const double sqlitepp::Statement::readDouble(const int column) const {
- requireOpen();
- requireCanRead();
- return sqlite3_column_double(m_handle, column);
-}
-
-const int sqlitepp::Statement::readInt(const int column) const {
- requireOpen();
- requireCanRead();
- return sqlite3_column_int(m_handle, column);
-}
-
-const std::string sqlitepp::Statement::readString(const int column) const {
- requireOpen();
- requireCanRead();
- return std::string((const char *) sqlite3_column_text(m_handle, column));
-}
-
-void sqlitepp::Statement::requireCanRead() const {
- if (!m_canRead) {
- throw std::logic_error("Trying to read from statement without data");
- }
-}
-
-const bool sqlitepp::Statement::step() {
- requireOpen();
- int result = sqlite3_step(m_handle);
- if (result == SQLITE_ROW) {
- m_canRead = true;
- } else if (result == SQLITE_DONE) {
- m_canRead = false;
- } else {
- throw DatabaseError(result);
- }
- return m_canRead;
-}
-
-void sqlitepp::Statement::finalize() {
- if (isOpen()) {
- // errors that could occur during finalizing are ignored as they have
- // already been handled!
- sqlite3_finalize(m_handle);
- setOpen(false);
- }
-}
-
-const bool sqlitepp::Statement::reset() {
- requireOpen();
- return sqlite3_reset(m_handle) == SQLITE_OK;
-}
-
-int sqlitepp::Statement::getParameterIndex(const std::string & name) const {
- requireOpen();
- int index = sqlite3_bind_parameter_index(m_handle, name.c_str());
- if (index == 0) {
- throw std::invalid_argument("No such parameter: " + name);
- }
- return index;
-}
-
-void sqlitepp::Statement::handleBindResult(const int index, const int result) const {
- switch (result) {
- case SQLITE_OK:
- break;
- case SQLITE_RANGE:
- throw std::out_of_range("Bind index out of range: " + index);
- case SQLITE_NOMEM:
- throw std::runtime_error("No memory to bind parameter");
- default:
- throw DatabaseError(result);
- }
-}
-
-sqlitepp::Database::Database() : Openable(false, "Database") {
-}
-
-sqlitepp::Database::Database(const std::string & file) : Openable(false, "Database") {
- open(file);
-}
-
-sqlitepp::Database::~Database() {
- if (isOpen()) {
- int result = sqlite3_close(m_handle);
- if (result != SQLITE_OK) {
- std::cerr << "sqlitepp::Database::~Database(): Close failed with code "
- << result << " meaning: " << sqlite3_errstr(result);
- std::abort();
- } else {
- setOpen(false);
- }
- }
- // m_handle is deleted by sqlite3_close
-}
-
-void sqlitepp::Database::close() {
- if (isOpen()) {
- int result = sqlite3_close(m_handle);
- if (result == SQLITE_OK) {
- setOpen(false);
- } else {
- throw sqlitepp::DatabaseError(result);
- }
- }
-}
-
-void sqlitepp::Database::execute(const std::string & sql) {
- requireOpen();
- Statement statement(*this, sql);
- statement.step();
- statement.finalize();
-}
-
-void sqlitepp::Database::open(const std::string & file) {
- if (isOpen()) {
- throw std::logic_error("sqlitepp::Database::open(std::string&): Database already open");
- }
- int result = sqlite3_open(file.c_str(), & m_handle);
-
- if (m_handle == NULL) {
- throw std::runtime_error("sqlitepp::Database::open(std::string&): Can't allocate memory");
- }
-
- if (result == SQLITE_OK) {
- setOpen(true);
- } else {
- std::string errorMessage = sqlite3_errmsg(m_handle);
- sqlite3_close(m_handle);
- throw sqlitepp::DatabaseError(result, errorMessage);
- }
-}
-
-std::shared_ptr<sqlitepp::Statement> sqlitepp::Database::prepare(const std::string & sql) {
- return std::shared_ptr<sqlitepp::Statement>(new sqlitepp::Statement(*this, sql));
-} \ No newline at end of file
diff --git a/src/sqlitepp/sqlitepp.cc b/src/sqlitepp/sqlitepp.cc
new file mode 100644
index 0000000..d64b5ba
--- /dev/null
+++ b/src/sqlitepp/sqlitepp.cc
@@ -0,0 +1,264 @@
+// Copyright (C) 2014--2015 Robin Krahl <robin.krahl@ireas.org>
+// MIT license -- http://opensource.org/licenses/MIT
+
+#include "sqlitepp/sqlitepp.h"
+#include <exception>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+namespace sqlitepp {
+
+Openable::Openable(const bool open, const std::string& name)
+ : m_open(open), m_name(name) {
+}
+
+bool Openable::isOpen() const {
+ return m_open;
+}
+
+void Openable::requireOpen() const {
+ if (!m_open) {
+ throw std::logic_error(m_name + " is not open.");
+ }
+}
+
+void Openable::setOpen(const bool open) {
+ m_open = open;
+}
+
+std::string DatabaseError::getErrorMessage(const int errorCode,
+ const std::string& errorMessage) {
+ std::ostringstream stringStream;
+ stringStream << "Caught SQLite3 error " << errorCode << " meaning: "
+ << errorMessage;
+ return stringStream.str();
+}
+
+DatabaseError::DatabaseError(const int errorCode)
+ : DatabaseError(errorCode, sqlite3_errstr(errorCode)) {
+}
+
+DatabaseError::DatabaseError(const int errorCode,
+ const std::string& errorMessage)
+ : std::runtime_error(getErrorMessage(errorCode, errorMessage)),
+ m_errorCode(errorCode) {
+}
+
+int DatabaseError::errorCode() const {
+ return m_errorCode;
+}
+
+Statement::Statement(sqlite3_stmt* handle)
+ : Openable(true, "Statement"), m_canRead(false), m_handle(handle) {
+}
+
+Statement::~Statement() {
+ if (isOpen()) {
+ // errors that could occur during finalizing are ignored as they have
+ // already been handled!
+ sqlite3_finalize(m_handle);
+ setOpen(false);
+ }
+}
+
+void Statement::bind(const int index, const double value) {
+ requireOpen();
+ handleBindResult(index, sqlite3_bind_double(m_handle, index, value));
+}
+
+void Statement::bind(const std::string& name, const double value) {
+ bind(getParameterIndex(name), value);
+}
+
+void Statement::bind(const int index, const int value) {
+ requireOpen();
+ handleBindResult(index, sqlite3_bind_int(m_handle, index, value));
+}
+
+void Statement::bind(const std::string& name, const int value) {
+ bind(getParameterIndex(name), value);
+}
+
+void Statement::bind(const int index, const std::string& value) {
+ requireOpen();
+ handleBindResult(index, sqlite3_bind_text(m_handle, index, value.c_str(),
+ value.size(), NULL));
+}
+
+void Statement::bind(const std::string& name, const std::string& value) {
+ bind(getParameterIndex(name), value);
+}
+
+ResultSet Statement::execute() {
+ step();
+ return ResultSet(m_instancePointer.lock());
+}
+
+void Statement::requireCanRead() const {
+ if (!m_canRead) {
+ throw std::logic_error("Trying to read from statement without data");
+ }
+}
+
+void Statement::setInstancePointer(
+ const std::weak_ptr<Statement>& instancePointer) {
+ m_instancePointer = instancePointer;
+}
+
+bool Statement::step() {
+ requireOpen();
+ int result = sqlite3_step(m_handle);
+ if (result == SQLITE_ROW) {
+ m_canRead = true;
+ } else if (result == SQLITE_DONE) {
+ m_canRead = false;
+ } else {
+ throw DatabaseError(result);
+ }
+ return m_canRead;
+}
+
+void Statement::close() {
+ if (isOpen()) {
+ // errors that could occur during finalizing are ignored as they have
+ // already been handled!
+ sqlite3_finalize(m_handle);
+ setOpen(false);
+ }
+}
+
+bool Statement::reset() {
+ requireOpen();
+ return sqlite3_reset(m_handle) == SQLITE_OK;
+}
+
+int Statement::getParameterIndex(const std::string& name) const {
+ requireOpen();
+ int index = sqlite3_bind_parameter_index(m_handle, name.c_str());
+ if (index == 0) {
+ throw std::invalid_argument("No such parameter: " + name);
+ }
+ return index;
+}
+
+void Statement::handleBindResult(const int index, const int result) const {
+ switch (result) {
+ case SQLITE_OK:
+ break;
+ case SQLITE_RANGE:
+ throw std::out_of_range("Bind index out of range: " + index);
+ case SQLITE_NOMEM:
+ throw std::runtime_error("No memory to bind parameter");
+ default:
+ throw DatabaseError(result);
+ }
+}
+
+Database::Database() : Openable(false, "Database") {
+}
+
+Database::Database(const std::string & file) : Database() {
+ open(file);
+}
+
+Database::~Database() {
+ if (isOpen()) {
+ sqlite3_close(m_handle);
+ setOpen(false);
+ }
+ // m_handle is deleted by sqlite3_close
+}
+
+void Database::close() {
+ if (isOpen()) {
+ int result = sqlite3_close(m_handle);
+ if (result == SQLITE_OK) {
+ setOpen(false);
+ } else {
+ throw sqlitepp::DatabaseError(result);
+ }
+ }
+}
+
+void Database::execute(const std::string& sql) {
+ requireOpen();
+ std::shared_ptr<Statement> statement = prepare(sql);
+ statement->step();
+}
+
+void Database::open(const std::string& file) {
+ if (isOpen()) {
+ throw std::logic_error("sqlitepp::Database::open(std::string&): "
+ "Database already open");
+ }
+ int result = sqlite3_open(file.c_str(), &m_handle);
+
+ if (m_handle == NULL) {
+ throw std::runtime_error("sqlitepp::Database::open(std::string&): "
+ "Can't allocate memory");
+ }
+
+ if (result == SQLITE_OK) {
+ setOpen(true);
+ } else {
+ std::string errorMessage = sqlite3_errmsg(m_handle);
+ sqlite3_close(m_handle);
+ throw sqlitepp::DatabaseError(result, errorMessage);
+ }
+}
+
+std::shared_ptr<Statement> Database::prepare(const std::string& sql) {
+ requireOpen();
+ sqlite3_stmt* statementHandle;
+ int result = sqlite3_prepare_v2(m_handle, sql.c_str(), sql.size(),
+ &statementHandle, NULL);
+ if (result != SQLITE_OK) {
+ throw DatabaseError(result, sqlite3_errmsg(m_handle));
+ }
+ if (statementHandle == NULL) {
+ throw std::runtime_error("Statement handle is NULL");
+ }
+ auto statement = std::shared_ptr<Statement>(new Statement(statementHandle));
+ statement->setInstancePointer(std::weak_ptr<Statement>(statement));
+ return statement;
+}
+
+ResultSet::ResultSet(const std::shared_ptr<Statement> statement)
+ : m_statement(statement) {
+}
+
+bool ResultSet::canRead() const {
+ return m_statement->m_canRead;
+}
+
+int ResultSet::columnCount() const {
+ m_statement->requireOpen();
+ m_statement->requireCanRead();
+ return sqlite3_column_count(m_statement->m_handle);
+}
+
+double ResultSet::readDouble(const int column) const {
+ m_statement->requireOpen();
+ m_statement->requireCanRead();
+ return sqlite3_column_double(m_statement->m_handle, column);
+}
+
+int ResultSet::readInt(const int column) const {
+ m_statement->requireOpen();
+ m_statement->requireCanRead();
+ return sqlite3_column_int(m_statement->m_handle, column);
+}
+
+std::string ResultSet::readString(const int column) const {
+ m_statement->requireOpen();
+ m_statement->requireCanRead();
+ return std::string((const char*) sqlite3_column_text(m_statement->m_handle,
+ column));
+}
+
+bool ResultSet::next() {
+ return m_statement->step();
+}
+
+} // namespace sqlitepp
diff --git a/src/sqlitepp/sqlitepp_test.cc b/src/sqlitepp/sqlitepp_test.cc
new file mode 100644
index 0000000..b7ea5f6
--- /dev/null
+++ b/src/sqlitepp/sqlitepp_test.cc
@@ -0,0 +1,100 @@
+// Copyright (C) 2014--2015 Robin Krahl <robin.krahl@ireas.org>
+// MIT license -- http://opensource.org/licenses/MIT
+
+#include <stdexcept>
+#include <fstream>
+#include <iostream>
+#include "gtest/gtest.h"
+#include "sqlitepp/sqlitepp.h"
+
+TEST(Database, openClose) {
+ sqlitepp::Database database;
+ EXPECT_FALSE(database.isOpen());
+ database.open("/tmp/test.db");
+ EXPECT_TRUE(database.isOpen());
+ database.close();
+ EXPECT_FALSE(database.isOpen());
+ database.open("/tmp/test2.db");
+ EXPECT_TRUE(database.isOpen());
+ database.close();
+ EXPECT_FALSE(database.isOpen());
+ sqlitepp::Database database2("/tmp/test.db");
+ EXPECT_TRUE(database2.isOpen());
+ EXPECT_THROW(database2.open("/tmp/test2.db"), std::logic_error);
+ EXPECT_TRUE(database2.isOpen());
+ database2.close();
+ EXPECT_FALSE(database2.isOpen());
+
+ std::ifstream testStream("/tmp/test.db");
+ EXPECT_TRUE(testStream.good());
+ testStream.close();
+ testStream.open("/tmp/test2.db");
+ EXPECT_TRUE(testStream.good());
+ testStream.close();
+}
+
+TEST(Database, copy) {
+ sqlitepp::Database database;
+ // MUST NOT COMPILE:
+ // sqlitepp::Database database2 = database;
+ database.close();
+ sqlitepp::Database database3;
+ // MUST NOT COMPILE:
+ // database3 = database;
+ database3.close();
+}
+
+TEST(Database, prepare) {
+ sqlitepp::Database database("/tmp/test.db");
+ std::shared_ptr<sqlitepp::Statement> statement = database.prepare(
+ "CREATE TABLE IF NOT EXISTS test (id, value);");
+ EXPECT_TRUE(statement->isOpen());
+ statement->close();
+ EXPECT_FALSE(statement->isOpen());
+ database.close();
+}
+
+TEST(Database, execute) {
+ sqlitepp::Database database("/tmp/test.db");
+ database.execute("CREATE TABLE IF NOT EXISTS test (id, value);");
+}
+
+TEST(Database, insert) {
+ sqlitepp::Database database("/tmp/test.db");
+ std::shared_ptr<sqlitepp::Statement> statement = database.prepare(
+ "INSERT INTO test (id, value) VALUES (:id, ?)");
+ statement->bind(":id", 1);
+ statement->bind(2, "test value");
+ statement->execute();
+ statement->reset();
+ statement->bind(":id", 2);
+ statement->bind(2, "other value");
+ statement->execute();
+}
+
+TEST(Database, query) {
+ sqlitepp::Database database("/tmp/test.db");
+ std::shared_ptr<sqlitepp::Statement> statement = database.prepare(
+ "SELECT id, value FROM test;");
+ sqlitepp::ResultSet resultSet = statement->execute();
+ EXPECT_TRUE(resultSet.canRead());
+ EXPECT_EQ(2, resultSet.columnCount());
+ int id = resultSet.readInt(0);
+ std::string value = resultSet.readString(1);
+ EXPECT_EQ(1, id);
+ EXPECT_EQ("test value", value);
+ EXPECT_TRUE(resultSet.next());
+ EXPECT_TRUE(resultSet.canRead());
+ id = resultSet.readInt(0);
+ value = resultSet.readString(1);
+ EXPECT_EQ(2, id);
+ EXPECT_EQ("other value", value);
+ EXPECT_FALSE(resultSet.next());
+ EXPECT_FALSE(resultSet.canRead());
+}
+
+TEST(Database, cleanup) {
+ sqlitepp::Database database("/tmp/test.db");
+ database.execute("DROP TABLE test;");
+ database.close();
+}
diff --git a/src/sqlitepptest.cpp b/src/sqlitepptest.cpp
deleted file mode 100644
index 7974eae..0000000
--- a/src/sqlitepptest.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * (C) 2014 Robin Krahl
- * MIT license -- http://opensource.org/licenses/MIT
- */
-
-#include "sqlitepp.h"
-
-#define BOOST_TEST_DYN_LINK
-#define BOOST_TEST_MODULE SQLitePPTest
-#include <boost/test/unit_test.hpp>
-
-#include <fstream>
-#include <iostream>
-#include <stdexcept>
-
-BOOST_AUTO_TEST_CASE(openClose) {
- sqlitepp::Database database;
- BOOST_CHECK(!database.isOpen());
- database.open("/tmp/test.db");
- BOOST_CHECK(database.isOpen());
- database.close();
- BOOST_CHECK(!database.isOpen());
- database.open("/tmp/test2.db");
- BOOST_CHECK(database.isOpen());
- database.close();
- BOOST_CHECK(!database.isOpen());
- sqlitepp::Database database2("/tmp/test.db");
- BOOST_CHECK(database2.isOpen());
- try {
- database2.open("/tmp/test2.db");
- BOOST_ERROR("Calling open() to an open database does not throw an exception.");
- } catch (std::logic_error &) {
- // everything fine
- }
- BOOST_CHECK(database2.isOpen());
- database2.close();
- BOOST_CHECK(!database2.isOpen());
-
- std::ifstream testStream("/tmp/test.db");
- BOOST_CHECK(testStream.good());
- testStream.close();
- testStream.open("/tmp/test2.db");
- BOOST_CHECK(testStream.good());
- testStream.close();
-}
-
-BOOST_AUTO_TEST_CASE(copy) {
- sqlitepp::Database database;
- // MUST NOT COMPILE:
- // sqlitepp::Database database2 = database;
- database.close();
- sqlitepp::Database database3;
- // MUST NOT COMPILE:
- // database3 = database;
- database3.close();
-}
-
-BOOST_AUTO_TEST_CASE(prepare) {
- sqlitepp::Database database("/tmp/test.db");
- sqlitepp::Statement statement(database, "CREATE TABLE IF NOT EXISTS test (id, value);");
- // TODO check std::logic_error
- BOOST_CHECK(statement.isOpen());
- statement.finalize();
- BOOST_CHECK(!statement.isOpen());
- database.close();
-}
-
-BOOST_AUTO_TEST_CASE(execute) {
- sqlitepp::Database database("/tmp/test.db");
- database.execute("CREATE TABLE IF NOT EXISTS test (id, value);");
- sqlitepp::Statement statement(database, "INSERT INTO test (id, value) VALUES (:id, ?)");
- statement.bindInt(":id", 1);
- statement.bindString(2, "test value");
- statement.step();
- statement.reset();
- statement.bindInt(":id", 2);
- statement.bindString(2, "other value");
- statement.step();
- statement.finalize();
-}
-
-BOOST_AUTO_TEST_CASE(query) {
- sqlitepp::Database database("/tmp/test.db");
- sqlitepp::Statement statement(database, "SELECT id, value FROM test;");
- bool hasNext = statement.step();
- BOOST_CHECK(hasNext);
- BOOST_CHECK_EQUAL(statement.columnCount(), 2);
- int id = statement.readInt(0);
- std::string value = statement.readString(1);
- BOOST_CHECK_EQUAL(id, 1);
- BOOST_CHECK_EQUAL(value, "test value");
- hasNext = statement.step();
- BOOST_CHECK(hasNext);
- id = statement.readInt(0);
- value = statement.readString(1);
- BOOST_CHECK_EQUAL(id, 2);
- BOOST_CHECK_EQUAL(value, "other value");
- hasNext = statement.step();
- BOOST_CHECK(!hasNext);
- statement.finalize();
- database.close();
-}
-
-BOOST_AUTO_TEST_CASE(cleanup) {
- sqlitepp::Database database("/tmp/test.db");
- database.execute("DROP TABLE test;");
- database.close();
-} \ No newline at end of file