From 4a6086aac5025663caaa94474f9a9c95650556c6 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Tue, 29 Nov 2022 00:06:35 -0300 Subject: [PATCH] add error handling in the model files and knowledge base --- src/knowledge_base.cpp | 108 ++++++++++--- src/knowledge_base.h | 13 +- src/models/entity.cpp | 162 +++++++++++-------- src/models/entity.h | 4 +- src/models/error.h | 146 +++++++++++++++++ src/models/fact.cpp | 294 +++++++++++++++++++++------------- src/models/fact.h | 4 +- src/models/rule.cpp | 1 + src/models/suggest_action.cpp | 1 + src/models/verb.cpp | 199 ++++++++++++----------- src/models/verb.h | 4 +- 11 files changed, 630 insertions(+), 306 deletions(-) create mode 100644 src/models/error.h diff --git a/src/knowledge_base.cpp b/src/knowledge_base.cpp index 51bfde1..a0209dd 100644 --- a/src/knowledge_base.cpp +++ b/src/knowledge_base.cpp @@ -1,13 +1,16 @@ #include "knowledge_base.h" #include "models/action.h" #include "models/entity.h" +#include "models/error.h" #include "models/fact.h" #include "models/rule.h" #include "models/suggest_action.h" #include "models/verb.h" +#include #include #include +#include obelisk::KnowledgeBase::KnowledgeBase(const char* filename, int flags) { @@ -23,6 +26,8 @@ obelisk::KnowledgeBase::KnowledgeBase(const char* filename, int flags) logSqliteError(result); } + enableForeignKeys(); + if (!dbExists) { createTable(obelisk::Action::createTable); @@ -42,16 +47,25 @@ obelisk::KnowledgeBase::~KnowledgeBase() } } -void obelisk::KnowledgeBase::createTable(std::function function) +/** + * @brief Enable foreign key functionality in the open database. + * + * This must always be done when the connection is opened or it will not + * enforce the foreign key constraints. + */ +void obelisk::KnowledgeBase::enableForeignKeys() { - char* tmp; - auto result = sqlite3_exec(dbConnection_, function(), NULL, NULL, &tmp); + char* errmsg; + int result = sqlite3_exec(dbConnection_, + "PRAGMA foreign_keys = ON;", + NULL, + NULL, + &errmsg); if (result != SQLITE_OK) { logSqliteError(result); - if (tmp) + if (errmsg) { - std::string errmsg(tmp); throw obelisk::KnowledgeBaseException(errmsg); } else @@ -61,53 +75,103 @@ void obelisk::KnowledgeBase::createTable(std::function function) } } -// TODO: change these to throw errors instead of return int -int obelisk::KnowledgeBase::addEntities(std::vector& entities) +void obelisk::KnowledgeBase::createTable(std::function function) +{ + char* errmsg; + int result = sqlite3_exec(dbConnection_, function(), NULL, NULL, &errmsg); + if (result != SQLITE_OK) + { + logSqliteError(result); + if (errmsg) + { + throw obelisk::KnowledgeBaseException(errmsg); + } + else + { + throw obelisk::KnowledgeBaseException(); + } + } +} + +void obelisk::KnowledgeBase::addEntities(std::vector& entities) { for (auto& entity : entities) { - entity.insertEntity(dbConnection_); + try + { + entity.insertEntity(dbConnection_); + } + catch (obelisk::DatabaseException::ConstraintException& exception) + { + // ignore unique constraint error + if (std::strcmp(exception.what(), + "UNIQUE constraint failed: entity.name") + != 0) + { + throw; + } + } } - return 0; } -int obelisk::KnowledgeBase::addVerbs(std::vector& verbs) +void obelisk::KnowledgeBase::addVerbs(std::vector& verbs) { for (auto& verb : verbs) { - verb.insertVerb(dbConnection_); + try + { + verb.insertVerb(dbConnection_); + } + catch (obelisk::DatabaseException::ConstraintException& exception) + { + // ignore unique constraint error + if (std::strcmp(exception.what(), + "UNIQUE constraint failed: verb.name") + != 0) + { + throw; + } + } } - return 0; } -int obelisk::KnowledgeBase::addFacts(std::vector& facts) +void obelisk::KnowledgeBase::addFacts(std::vector& facts) { for (auto& fact : facts) { - fact.insertFact(dbConnection_); + try + { + fact.insertFact(dbConnection_); + } + catch (obelisk::DatabaseException::ConstraintException& exception) + { + // ignore unique constraint error + if (std::strcmp(exception.what(), + "UNIQUE constraint failed: fact.left_entity, fact.right_entity, fact.verb") + != 0) + { + throw; + } + } } - return 0; } -int obelisk::KnowledgeBase::getEntity(obelisk::Entity& entity) +void obelisk::KnowledgeBase::getEntity(obelisk::Entity& entity) { entity.selectEntity(dbConnection_); - return 0; } -int obelisk::KnowledgeBase::getVerb(obelisk::Verb& verb) +void obelisk::KnowledgeBase::getVerb(obelisk::Verb& verb) { verb.selectVerb(dbConnection_); - return 0; } -int obelisk::KnowledgeBase::getFact(obelisk::Fact& fact) +void obelisk::KnowledgeBase::getFact(obelisk::Fact& fact) { fact.selectFact(dbConnection_); - return 0; } -// TODO: log files? +// TODO: log files? or just throw an error? void obelisk::KnowledgeBase::logSqliteError(int result) { std::cout << sqlite3_errstr(result) << std::endl; diff --git a/src/knowledge_base.h b/src/knowledge_base.h index e690fe8..7458f65 100644 --- a/src/knowledge_base.h +++ b/src/knowledge_base.h @@ -24,6 +24,7 @@ namespace obelisk int flags_; void logSqliteError(int result); + void enableForeignKeys(); void createTable(std::function function); public: @@ -37,13 +38,13 @@ namespace obelisk ~KnowledgeBase(); - int addEntities(std::vector& entities); - int addVerbs(std::vector& verbs); - int addFacts(std::vector& facts); + void addEntities(std::vector& entities); + void addVerbs(std::vector& verbs); + void addFacts(std::vector& facts); - int getEntity(obelisk::Entity& entity); - int getVerb(obelisk::Verb& verb); - int getFact(obelisk::Fact& fact); + void getEntity(obelisk::Entity& entity); + void getVerb(obelisk::Verb& verb); + void getFact(obelisk::Fact& fact); void getDouble(double& result, float var1, float var2); void getFloat(float& result1, float& result2, double var); diff --git a/src/models/entity.cpp b/src/models/entity.cpp index 7089841..d7e81a8 100644 --- a/src/models/entity.cpp +++ b/src/models/entity.cpp @@ -1,4 +1,5 @@ #include "models/entity.h" +#include "models/error.h" const char* obelisk::Entity::createTable() { @@ -11,118 +12,139 @@ const char* obelisk::Entity::createTable() )"; } -int obelisk::Entity::selectEntity(sqlite3* dbConnection) +void obelisk::Entity::selectEntity(sqlite3* dbConnection) { - // TODO: check if database is open + if (dbConnection == nullptr) + { + throw obelisk::DatabaseException("database isn't open"); + } + sqlite3_stmt* ppStmt = nullptr; - const char* pzTail = nullptr; auto result = sqlite3_prepare_v2(dbConnection, - "SELECT id, name FROM entity WHERE name=?;", + "SELECT id, name FROM entity WHERE name=?", -1, &ppStmt, - &pzTail); + nullptr); + if (result != SQLITE_OK) { - // TODO: something went wrong throw an error + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); } - if (pzTail != nullptr) + result = sqlite3_bind_text(ppStmt, 1, getName().c_str(), -1, SQLITE_STATIC); + switch (result) { - // TODO: Something was not used... throw an error - } - - result - = sqlite3_bind_text(ppStmt, 1, getName().c_str(), -1, SQLITE_TRANSIENT); - if (result != SQLITE_OK) - { - // TODO: Something is wrong... throw an error + case SQLITE_OK : + break; + case SQLITE_TOOBIG : + throw obelisk::DatabaseException::SizeException(); + break; + case SQLITE_RANGE : + throw obelisk::DatabaseException::RangeException(); + break; + case SQLITE_NOMEM : + throw obelisk::DatabaseException::MemoryException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; } result = sqlite3_step(ppStmt); - if (result != SQLITE_DONE) + switch (result) { - // TODO: Something is wrong... throw an error + case SQLITE_DONE : + // no rows in the database + break; + case SQLITE_ROW : + setId(sqlite3_column_int(ppStmt, 0)); + setName((char*) sqlite3_column_text(ppStmt, 1)); + break; + case SQLITE_BUSY : + throw obelisk::DatabaseException::BusyException(); + break; + case SQLITE_MISUSE : + throw obelisk::DatabaseException::MisuseException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; } - if (result == SQLITE_ROW) - { - setId(sqlite3_column_int(ppStmt, 0)); - setName((char*) sqlite3_column_text(ppStmt, 1)); + result = sqlite3_finalize(ppStmt); - result = sqlite3_finalize(ppStmt); - if (result != SQLITE_OK) - { - // TODO: Something is wrong... throw an error - } - return 0; - } - else + if (result != SQLITE_OK) { - result = sqlite3_finalize(ppStmt); - if (result != SQLITE_OK) - { - // TODO: Something is wrong... throw an error - } - return 0; + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); } } -int obelisk::Entity::insertEntity(sqlite3* dbConnection) +void obelisk::Entity::insertEntity(sqlite3* dbConnection) { - // TODO: check if database is open - - /*selectEntity(dbConnection); - if (getId() != 0) + if (dbConnection == nullptr) { - // TODO: already exists in database, throw an error? Or skip past it? - return -1; - }*/ - - sqlite3_stmt* ppStmt = nullptr; - const char* pzTail = nullptr; - - auto result = sqlite3_prepare_v2(dbConnection, - "INSERT INTO entity (name) VALUES (?);", - -1, - &ppStmt, - &pzTail); - if (result != SQLITE_OK) - { - // TODO: something went wrong throw an error + throw obelisk::DatabaseException("database isn't open"); } - if (pzTail != nullptr) + sqlite3_stmt* ppStmt = nullptr; + + auto result = sqlite3_prepare_v2(dbConnection, + "INSERT INTO entity (name) VALUES (?)", + -1, + &ppStmt, + nullptr); + if (result != SQLITE_OK) { - // TODO: Something was not used... throw an error + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); } result = sqlite3_bind_text(ppStmt, 1, getName().c_str(), -1, SQLITE_TRANSIENT); - if (result != SQLITE_OK) + switch (result) { - // TODO: Something is wrong... throw an error + case SQLITE_OK : + break; + case SQLITE_TOOBIG : + throw obelisk::DatabaseException::SizeException(); + break; + case SQLITE_RANGE : + throw obelisk::DatabaseException::RangeException(); + break; + case SQLITE_NOMEM : + throw obelisk::DatabaseException::MemoryException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; } result = sqlite3_step(ppStmt); - if (result != SQLITE_DONE) + switch (result) { - // TODO: Something is wrong... throw an error + case SQLITE_DONE : + setId((int) sqlite3_last_insert_rowid(dbConnection)); + sqlite3_set_last_insert_rowid(dbConnection, 0); + break; + case SQLITE_CONSTRAINT : + throw obelisk::DatabaseException::ConstraintException( + sqlite3_errmsg(dbConnection)); + case SQLITE_BUSY : + throw obelisk::DatabaseException::BusyException(); + break; + case SQLITE_MISUSE : + throw obelisk::DatabaseException::MisuseException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; } - else - { - setId((int) sqlite3_last_insert_rowid(dbConnection)); - } - - sqlite3_set_last_insert_rowid(dbConnection, 0); result = sqlite3_finalize(ppStmt); if (result != SQLITE_OK) { - // TODO: Something is wrong... throw an error + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); } - - return 0; } int& obelisk::Entity::getId() diff --git a/src/models/entity.h b/src/models/entity.h index f799f31..7e4d0e0 100644 --- a/src/models/entity.h +++ b/src/models/entity.h @@ -46,8 +46,8 @@ namespace obelisk std::string& getName(); void setName(std::string name); - int selectEntity(sqlite3* dbConnection); - int insertEntity(sqlite3* dbConnection); + void selectEntity(sqlite3* dbConnection); + void insertEntity(sqlite3* dbConnection); }; } // namespace obelisk diff --git a/src/models/error.h b/src/models/error.h new file mode 100644 index 0000000..e8ace1b --- /dev/null +++ b/src/models/error.h @@ -0,0 +1,146 @@ +#ifndef OBELISK_MODELS_ERROR_H +#define OBELISK_MODELS_ERROR_H + +#include +#include + +namespace obelisk +{ + class DatabaseException : public std::exception + { + private: + const std::string errorMessage_; + + public: + DatabaseException() : + errorMessage_("an unknown error ocurred") + { + } + + DatabaseException(const int errorCode) : + errorMessage_( + "database error " + std::to_string(errorCode) + " ocurred") + { + } + + DatabaseException(const std::string& errorMessage) : + errorMessage_(errorMessage) + { + } + + const char* what() const noexcept + { + return errorMessage_.c_str(); + } + + class SizeException : public std::exception + { + private: + const std::string errorMessage_; + + public: + SizeException() : + errorMessage_("size of string or blob exceeds limits") + { + } + + const char* what() const noexcept + { + return errorMessage_.c_str(); + } + }; + + class RangeException : public std::exception + { + private: + const std::string errorMessage_; + + public: + RangeException() : + errorMessage_("parameter index is out of range") + { + } + + const char* what() const noexcept + { + return errorMessage_.c_str(); + } + }; + + class MemoryException : public std::exception + { + private: + const std::string errorMessage_; + + public: + MemoryException() : + errorMessage_("not enough memory for operation") + { + } + + const char* what() const noexcept + { + return errorMessage_.c_str(); + } + }; + + class BusyException : public std::exception + { + private: + const std::string errorMessage_; + + public: + BusyException() : + errorMessage_( + "database was busy and operation not performed") + { + } + + const char* what() const noexcept + { + return errorMessage_.c_str(); + } + }; + + class MisuseException : public std::exception + { + private: + const std::string errorMessage_; + + public: + MisuseException() : + errorMessage_("misuse of the database routine") + { + } + + const char* what() const noexcept + { + return errorMessage_.c_str(); + } + }; + + class ConstraintException : public std::exception + { + private: + const std::string errorMessage_; + + public: + ConstraintException() : + errorMessage_("a constraint exception occurred") + { + } + + ConstraintException(const std::string& errorMessage) : + errorMessage_(errorMessage) + { + } + + const char* what() const noexcept + { + return errorMessage_.c_str(); + } + }; + }; +} // namespace obelisk + +#endif diff --git a/src/models/fact.cpp b/src/models/fact.cpp index 51c6c40..467373d 100644 --- a/src/models/fact.cpp +++ b/src/models/fact.cpp @@ -1,3 +1,4 @@ +#include "models/error.h" #include "models/fact.h" const char* obelisk::Fact::createTable() @@ -9,6 +10,7 @@ const char* obelisk::Fact::createTable() "right_entity" INTEGER NOT NULL, "verb" INTEGER NOT NULL, PRIMARY KEY("id" AUTOINCREMENT), + UNIQUE("left_entity", "right_entity", "verb") FOREIGN KEY("verb") REFERENCES "verb"("id") ON DELETE RESTRICT, FOREIGN KEY("right_entity") REFERENCES "entity"("id") ON DELETE RESTRICT, FOREIGN KEY("left_entity") REFERENCES "entity"("id") ON DELETE RESTRICT @@ -16,146 +18,214 @@ const char* obelisk::Fact::createTable() )"; } -int obelisk::Fact::selectFact(sqlite3* dbConnection) +void obelisk::Fact::selectFact(sqlite3* dbConnection) { - // TODO: check if database is open - - sqlite3_stmt* ppStmt = nullptr; - const char* pzTail = nullptr; - - auto result = sqlite3_prepare_v2(dbConnection, - "SELECT id, left_entity, right_entity, verb FROM fact WHERE (left_entity=? AND right_entity=? AND verb=?);", - -1, - &ppStmt, - &pzTail); - if (result != SQLITE_OK) + if (dbConnection == nullptr) { - // TODO: something went wrong throw an error + throw obelisk::DatabaseException("database isn't open"); } - if (pzTail != nullptr) + sqlite3_stmt* ppStmt = nullptr; + + auto result = sqlite3_prepare_v2(dbConnection, + "SELECT id, left_entity, right_entity, verb FROM fact WHERE (left_entity=? AND right_entity=? AND verb=?)", + -1, + &ppStmt, + nullptr); + if (result != SQLITE_OK) { - // TODO: Something was not used... throw an error + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); } result = sqlite3_bind_int(ppStmt, 1, getLeftEntity().getId()); - if (result != SQLITE_OK) + switch (result) { - // TODO: Something is wrong... throw an error + case SQLITE_OK : + break; + case SQLITE_TOOBIG : + throw obelisk::DatabaseException::SizeException(); + break; + case SQLITE_RANGE : + throw obelisk::DatabaseException::RangeException(); + break; + case SQLITE_NOMEM : + throw obelisk::DatabaseException::MemoryException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; } result = sqlite3_bind_int(ppStmt, 2, getRightEntity().getId()); - if (result != SQLITE_OK) + switch (result) { - // TODO: Something is wrong... throw an error + case SQLITE_OK : + break; + case SQLITE_TOOBIG : + throw obelisk::DatabaseException::SizeException(); + break; + case SQLITE_RANGE : + throw obelisk::DatabaseException::RangeException(); + break; + case SQLITE_NOMEM : + throw obelisk::DatabaseException::MemoryException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; } result = sqlite3_bind_int(ppStmt, 3, getVerb().getId()); - if (result != SQLITE_OK) + switch (result) { - // TODO: Something is wrong... throw an error + case SQLITE_OK : + break; + case SQLITE_TOOBIG : + throw obelisk::DatabaseException::SizeException(); + break; + case SQLITE_RANGE : + throw obelisk::DatabaseException::RangeException(); + break; + case SQLITE_NOMEM : + throw obelisk::DatabaseException::MemoryException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; } result = sqlite3_step(ppStmt); - if (result != SQLITE_DONE) + switch (result) { - // TODO: Something is wrong... throw an error + case SQLITE_DONE : + // no rows in the database + break; + case SQLITE_ROW : + setId(sqlite3_column_int(ppStmt, 0)); + getLeftEntity().setId(sqlite3_column_int(ppStmt, 1)); + getRightEntity().setId(sqlite3_column_int(ppStmt, 2)); + getVerb().setId(sqlite3_column_int(ppStmt, 3)); + break; + case SQLITE_BUSY : + throw obelisk::DatabaseException::BusyException(); + break; + case SQLITE_MISUSE : + throw obelisk::DatabaseException::MisuseException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; } - if (result == SQLITE_ROW) - { - setId(sqlite3_column_int(ppStmt, 0)); - getLeftEntity().setId(sqlite3_column_int(ppStmt, 1)); - getRightEntity().setId(sqlite3_column_int(ppStmt, 2)); - getVerb().setId(sqlite3_column_int(ppStmt, 3)); - - result = sqlite3_finalize(ppStmt); - if (result != SQLITE_OK) - { - // TODO: Something is wrong... throw an error - } - return 0; - } - else - { - result = sqlite3_finalize(ppStmt); - if (result != SQLITE_OK) - { - // TODO: Something is wrong... throw an error - } - return 0; - } -} - -int obelisk::Fact::insertFact(sqlite3* dbConnection) -{ - // TODO: make sure database is open - - // check if the fact id exists, based on the ids of the entities and verb - /*selectFact(dbConnection); - if (getId() != 0) - { - // TODO: Verb is already in database, throw an error? Or just skip it? - return -1; - }*/ - - // TODO: verify that verbId, leftEntityId, and rightEntityId are not 0 - - sqlite3_stmt* ppStmt = nullptr; - const char* pzTail = nullptr; - - auto result = sqlite3_prepare_v2(dbConnection, - "INSERT INTO fact (left_entity, right_entity, verb) VALUES (?, ?, ?);", - -1, - &ppStmt, - &pzTail); - if (result != SQLITE_OK) - { - // TODO: something went wrong throw an error - } - - if (pzTail != nullptr) - { - // TODO: Something was not used... throw an error - } - - result = sqlite3_bind_int(ppStmt, 1, getLeftEntity().getId()); - if (result != SQLITE_OK) - { - // TODO: Something is wrong... throw an error - } - - result = sqlite3_bind_int(ppStmt, 2, getRightEntity().getId()); - if (result != SQLITE_OK) - { - // TODO: Something is wrong... throw an error - } - - result = sqlite3_bind_int(ppStmt, 3, getVerb().getId()); - if (result != SQLITE_OK) - { - // TODO: Something is wrong... throw an error - } - - result = sqlite3_step(ppStmt); - if (result != SQLITE_DONE) - { - // TODO: Something is wrong... throw an error - } - else - { - setId((int) sqlite3_last_insert_rowid(dbConnection)); - } - - sqlite3_set_last_insert_rowid(dbConnection, 0); - result = sqlite3_finalize(ppStmt); if (result != SQLITE_OK) { - // TODO: Something is wrong... throw an error + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + } +} + +void obelisk::Fact::insertFact(sqlite3* dbConnection) +{ + if (dbConnection == nullptr) + { + throw obelisk::DatabaseException("database isn't open"); } - return 0; + sqlite3_stmt* ppStmt = nullptr; + + auto result = sqlite3_prepare_v2(dbConnection, + "INSERT INTO fact (left_entity, right_entity, verb) VALUES (?, ?, ?)", + -1, + &ppStmt, + nullptr); + if (result != SQLITE_OK) + { + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + } + + result = sqlite3_bind_int(ppStmt, 1, getLeftEntity().getId()); + switch (result) + { + case SQLITE_OK : + break; + case SQLITE_TOOBIG : + throw obelisk::DatabaseException::SizeException(); + break; + case SQLITE_RANGE : + throw obelisk::DatabaseException::RangeException(); + break; + case SQLITE_NOMEM : + throw obelisk::DatabaseException::MemoryException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; + } + + result = sqlite3_bind_int(ppStmt, 2, getRightEntity().getId()); + switch (result) + { + case SQLITE_OK : + break; + case SQLITE_TOOBIG : + throw obelisk::DatabaseException::SizeException(); + break; + case SQLITE_RANGE : + throw obelisk::DatabaseException::RangeException(); + break; + case SQLITE_NOMEM : + throw obelisk::DatabaseException::MemoryException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; + } + + result = sqlite3_bind_int(ppStmt, 3, getVerb().getId()); + switch (result) + { + case SQLITE_OK : + break; + case SQLITE_TOOBIG : + throw obelisk::DatabaseException::SizeException(); + break; + case SQLITE_RANGE : + throw obelisk::DatabaseException::RangeException(); + break; + case SQLITE_NOMEM : + throw obelisk::DatabaseException::MemoryException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; + } + + result = sqlite3_step(ppStmt); + switch (result) + { + case SQLITE_DONE : + setId((int) sqlite3_last_insert_rowid(dbConnection)); + sqlite3_set_last_insert_rowid(dbConnection, 0); + break; + case SQLITE_CONSTRAINT : + throw obelisk::DatabaseException::ConstraintException( + sqlite3_errmsg(dbConnection)); + case SQLITE_BUSY : + throw obelisk::DatabaseException::BusyException(); + break; + case SQLITE_MISUSE : + throw obelisk::DatabaseException::MisuseException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; + } + + result = sqlite3_finalize(ppStmt); + if (result != SQLITE_OK) + { + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + } } int& obelisk::Fact::getId() diff --git a/src/models/fact.h b/src/models/fact.h index 332f089..0bdf0ef 100644 --- a/src/models/fact.h +++ b/src/models/fact.h @@ -69,8 +69,8 @@ namespace obelisk Verb& getVerb(); void setVerb(obelisk::Verb verb); - int selectFact(sqlite3* dbConnection); - int insertFact(sqlite3* dbConnection); + void selectFact(sqlite3* dbConnection); + void insertFact(sqlite3* dbConnection); }; } // namespace obelisk diff --git a/src/models/rule.cpp b/src/models/rule.cpp index e9bde88..7dfc4f5 100644 --- a/src/models/rule.cpp +++ b/src/models/rule.cpp @@ -8,6 +8,7 @@ const char* obelisk::Rule::createTable() "fact" INTEGER NOT NULL, "reason" INTEGER NOT NULL CHECK("reason" != "fact"), PRIMARY KEY("id" AUTOINCREMENT), + UNIQUE("fact", "reason"), FOREIGN KEY("fact") REFERENCES "fact"("id") ON DELETE RESTRICT, FOREIGN KEY("reason") REFERENCES "fact"("id") ON DELETE RESTRICT ); diff --git a/src/models/suggest_action.cpp b/src/models/suggest_action.cpp index 929aa5b..838a33b 100644 --- a/src/models/suggest_action.cpp +++ b/src/models/suggest_action.cpp @@ -9,6 +9,7 @@ const char* obelisk::SuggestAction::createTable() "true_action" INTEGER NOT NULL, "false_action" INTEGER NOT NULL, PRIMARY KEY("id" AUTOINCREMENT), + UNIQUE("fact", "true_action", "false_action"), FOREIGN KEY("fact") REFERENCES "fact"("id") ON DELETE RESTRICT, FOREIGN KEY("true_action") REFERENCES "action"("id") ON DELETE RESTRICT, FOREIGN KEY("false_action") REFERENCES "action"("id") ON DELETE RESTRICT diff --git a/src/models/verb.cpp b/src/models/verb.cpp index a49b3bd..9649533 100644 --- a/src/models/verb.cpp +++ b/src/models/verb.cpp @@ -1,3 +1,4 @@ +#include "models/error.h" #include "models/verb.h" #include @@ -13,119 +14,137 @@ const char* obelisk::Verb::createTable() )"; } -int obelisk::Verb::selectVerb(sqlite3* dbConnection) +void obelisk::Verb::selectVerb(sqlite3* dbConnection) { - // TODO: check if database is open + if (dbConnection == nullptr) + { + throw obelisk::DatabaseException("database isn't open"); + } sqlite3_stmt* ppStmt = nullptr; - const char* pzTail = nullptr; auto result = sqlite3_prepare_v2(dbConnection, - "SELECT id, name FROM verb WHERE name=?;", + "SELECT id, name FROM verb WHERE name=?", -1, &ppStmt, - &pzTail); + nullptr); if (result != SQLITE_OK) { - // TODO: something went wrong throw an error + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); } - if (pzTail != nullptr) + result = sqlite3_bind_text(ppStmt, 1, getName().c_str(), -1, SQLITE_STATIC); + switch (result) { - // TODO: Something was not used... throw an error - } - - result - = sqlite3_bind_text(ppStmt, 1, getName().c_str(), -1, SQLITE_TRANSIENT); - if (result != SQLITE_OK) - { - // TODO: Something is wrong... throw an error + case SQLITE_OK : + break; + case SQLITE_TOOBIG : + throw obelisk::DatabaseException::SizeException(); + break; + case SQLITE_RANGE : + throw obelisk::DatabaseException::RangeException(); + break; + case SQLITE_NOMEM : + throw obelisk::DatabaseException::MemoryException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; } result = sqlite3_step(ppStmt); - if (result != SQLITE_DONE) + switch (result) { - // TODO: Something is wrong... throw an error + case SQLITE_DONE : + // no rows in the database + break; + case SQLITE_ROW : + setId(sqlite3_column_int(ppStmt, 0)); + setName((char*) sqlite3_column_text(ppStmt, 1)); + break; + case SQLITE_BUSY : + throw obelisk::DatabaseException::BusyException(); + break; + case SQLITE_MISUSE : + throw obelisk::DatabaseException::MisuseException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; } - if (result == SQLITE_ROW) - { - setId(sqlite3_column_int(ppStmt, 0)); - setName((char*) sqlite3_column_text(ppStmt, 1)); - - result = sqlite3_finalize(ppStmt); - if (result != SQLITE_OK) - { - // TODO: Something is wrong... throw an error - } - return 0; - } - else - { - result = sqlite3_finalize(ppStmt); - if (result != SQLITE_OK) - { - // TODO: Something is wrong... throw an error - } - return 0; - } -} - -int obelisk::Verb::insertVerb(sqlite3* dbConnection) -{ - // TODO: make sure database is open - - /*selectVerb(dbConnection); - if (getId() != 0) - { - // TODO: Verb is already in database, throw an error? Or just skip it? - return -1; - }*/ - - sqlite3_stmt* ppStmt = nullptr; - const char* pzTail = nullptr; - - auto result = sqlite3_prepare_v2(dbConnection, - "INSERT INTO verb (name) VALUES (?);", - -1, - &ppStmt, - &pzTail); - if (result != SQLITE_OK) - { - // TODO: something went wrong throw an error - } - - if (pzTail != nullptr) - { - // TODO: Something was not used... throw an error - } - - result - = sqlite3_bind_text(ppStmt, 1, getName().c_str(), -1, SQLITE_TRANSIENT); - if (result != SQLITE_OK) - { - // TODO: Something is wrong... throw an error - } - - result = sqlite3_step(ppStmt); - if (result != SQLITE_DONE) - { - // TODO: Something is wrong... throw an error - } - else - { - setId((int) sqlite3_last_insert_rowid(dbConnection)); - } - - sqlite3_set_last_insert_rowid(dbConnection, 0); - result = sqlite3_finalize(ppStmt); if (result != SQLITE_OK) { - // TODO: Something is wrong... throw an error + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + } +} + +void obelisk::Verb::insertVerb(sqlite3* dbConnection) +{ + if (dbConnection == nullptr) + { + throw obelisk::DatabaseException("database isn't open"); } - return 0; + sqlite3_stmt* ppStmt = nullptr; + + auto result = sqlite3_prepare_v2(dbConnection, + "INSERT INTO verb (name) VALUES (?)", + -1, + &ppStmt, + nullptr); + if (result != SQLITE_OK) + { + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + } + + result + = sqlite3_bind_text(ppStmt, 1, getName().c_str(), -1, SQLITE_TRANSIENT); + switch (result) + { + case SQLITE_OK : + break; + case SQLITE_TOOBIG : + throw obelisk::DatabaseException::SizeException(); + break; + case SQLITE_RANGE : + throw obelisk::DatabaseException::RangeException(); + break; + case SQLITE_NOMEM : + throw obelisk::DatabaseException::MemoryException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; + } + + result = sqlite3_step(ppStmt); + switch (result) + { + case SQLITE_DONE : + setId((int) sqlite3_last_insert_rowid(dbConnection)); + sqlite3_set_last_insert_rowid(dbConnection, 0); + break; + case SQLITE_CONSTRAINT : + throw obelisk::DatabaseException::ConstraintException( + sqlite3_errmsg(dbConnection)); + case SQLITE_BUSY : + throw obelisk::DatabaseException::BusyException(); + break; + case SQLITE_MISUSE : + throw obelisk::DatabaseException::MisuseException(); + break; + default : + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + break; + } + + result = sqlite3_finalize(ppStmt); + if (result != SQLITE_OK) + { + throw obelisk::DatabaseException(sqlite3_errmsg(dbConnection)); + } } int& obelisk::Verb::getId() diff --git a/src/models/verb.h b/src/models/verb.h index cf74204..058d1d1 100644 --- a/src/models/verb.h +++ b/src/models/verb.h @@ -46,8 +46,8 @@ namespace obelisk std::string& getName(); void setName(std::string name); - int selectVerb(sqlite3* dbConnection); - int insertVerb(sqlite3* dbConnection); + void selectVerb(sqlite3* dbConnection); + void insertVerb(sqlite3* dbConnection); }; } // namespace obelisk