From 63711877057f1f89b4d1774e24fe20907a3af656 Mon Sep 17 00:00:00 2001 From: Dominick Allen Date: Thu, 26 Sep 2024 07:46:06 -0500 Subject: Add SQLite interface. --- source/fud_sqlite.cpp | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 source/fud_sqlite.cpp (limited to 'source/fud_sqlite.cpp') diff --git a/source/fud_sqlite.cpp b/source/fud_sqlite.cpp new file mode 100644 index 0000000..7ad8a12 --- /dev/null +++ b/source/fud_sqlite.cpp @@ -0,0 +1,220 @@ +/* + * libfud + * Copyright 2024 Dominick Allen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fud_sqlite.hpp" + +namespace fud { + +SqliteDb::SqliteDb(const String& name, SqliteOpenMode mode, int extraFlags) : + m_name{name}, m_mode{mode}, m_extraFlags{extraFlags} +{ + initialize(); +} + +SqliteDb::SqliteDb(const char* name, SqliteOpenMode mode, int extraFlags) : + m_name{name}, m_mode{mode}, m_extraFlags{extraFlags} +{ + initialize(); +} + +SqliteDb::SqliteDb(SqliteDb&& rhs) : + m_name{std::move(rhs.m_name)}, m_nameValid{rhs.m_nameValid}, m_dbHandle{rhs.m_dbHandle}, + m_errorCode{rhs.m_errorCode}, m_mode{rhs.m_mode}, m_extraFlags{rhs.m_extraFlags} +{ + rhs.m_nameValid = false; + rhs.m_dbHandle = nullptr; +} + +SqliteDb::~SqliteDb() +{ + if (m_dbHandle != nullptr) { + sqlite3_close(m_dbHandle); + m_dbHandle = nullptr; + } +} + +SqliteDb& SqliteDb::operator=(SqliteDb&& rhs) +{ + m_name = std::move(rhs.m_name); + m_nameValid = rhs.m_nameValid; + m_dbHandle = rhs.m_dbHandle; + m_errorCode = rhs.m_errorCode; + + rhs.m_nameValid = false; + rhs.m_dbHandle = nullptr; + + return *this; +} + +bool SqliteDb::valid() const +{ + return m_nameValid && m_dbHandle != nullptr && m_errorCode == SQLITE_OK; +} + +bool SqliteDb::revalidate() +{ + if (m_nameValid && m_dbHandle != nullptr) { + m_errorCode = SQLITE_OK; + return true; + } + return false; +} + +FudStatus SqliteDb::exec( + const String& statement, + int (*callback)(void*, int, char**, char**), + void* context, + String* errorMessage) +{ + if (!valid()) { + return FudStatus::ObjectInvalid; + } + + if (!statement.utf8Valid()) { + return FudStatus::Utf8Invalid; + } + + char* errorMsgPtr = nullptr; + char** errorMsgPtrAddress = &errorMsgPtr; + if (errorMessage == nullptr) { + errorMsgPtrAddress = nullptr; + } + + m_errorCode = sqlite3_exec( + m_dbHandle, + statement.c_str(), + callback, + context, + errorMsgPtrAddress); + + return m_errorCode == SQLITE_OK ? FudStatus::Success : FudStatus::Failure; +} + +void SqliteDb::initialize() +{ + m_nameValid = m_name.utf8Valid(); + + if (!m_nameValid) { + return; + } + + m_errorCode = open(); +} + +int SqliteDb::open() +{ + // use default vfs + return sqlite3_open_v2(m_name.c_str(), &m_dbHandle, static_cast(m_mode) | m_extraFlags, nullptr); +} + +Result SqliteDb::prepare(const String& dql) +{ + using RetType = Result; + SqliteStatement preparedStatement{*this, dql}; + + if (!preparedStatement.valid() || preparedStatement.status() != FudStatus::Success) { + m_errorCode = preparedStatement.errorCode(); + return RetType::error(preparedStatement.status()); + } + + return RetType::okay(std::move(preparedStatement)); +} + +SqliteStatement::SqliteStatement(const SqliteDb& sqliteDb, const String& input) +{ + if (!sqliteDb.valid()) { + m_status = FudStatus::ObjectInvalid; + return; + } + + if (!input.utf8Valid() || input.length() > INT_MAX) { + m_status = FudStatus::InvalidInput; + return; + } + + int statementLength = static_cast(input.length()); + m_errorCode = sqlite3_prepare_v2(sqliteDb.handle(), input.c_str(), statementLength, &m_preparedStatement, &m_tail); + if (m_errorCode == SQLITE_OK) { + m_status = FudStatus::Success; + } +} + +SqliteStatement::SqliteStatement(SqliteStatement&& rhs) : + m_input{std::move(rhs.m_input)}, m_tail{rhs.m_tail}, m_status{rhs.m_status}, m_errorCode{rhs.m_errorCode}, + m_preparedStatement{rhs.m_preparedStatement} +{ + rhs.m_tail = nullptr; + rhs.m_status = FudStatus::ObjectInvalid; + rhs.m_preparedStatement = nullptr; +} + +SqliteStatement& SqliteStatement::operator=(SqliteStatement&& rhs) +{ + m_input = std::move(rhs.m_input); + m_tail = rhs.m_tail; + m_status = rhs.m_status; + m_errorCode = rhs.m_errorCode; + m_preparedStatement = rhs.m_preparedStatement; + + rhs.m_tail = nullptr; + rhs.m_status = FudStatus::ObjectInvalid; + rhs.m_preparedStatement = nullptr; + + return *this; +} + +SqliteStatement::~SqliteStatement() +{ + if (m_preparedStatement != nullptr) { + sqlite3_finalize(m_preparedStatement); + m_preparedStatement = nullptr; + } +} + +bool SqliteStatement::valid() const +{ + return m_status == FudStatus::Success && m_preparedStatement != nullptr; +} + +sqlite3_stmt* SqliteStatement::statement() +{ + return m_preparedStatement; +} + +int SqliteStatement::step() +{ + if (!valid()) { + m_errorCode = SQLITE_MISUSE; + } else { + m_errorCode = sqlite3_step(m_preparedStatement); + } + return m_errorCode; +} + +FudStatus SqliteStatement::reset() +{ + if (!valid()) { + return FudStatus::ObjectInvalid; + } + m_errorCode = sqlite3_reset(m_preparedStatement); + if (m_errorCode != SQLITE_OK) { + return FudStatus::Failure; + } + return FudStatus::Success; +} + +} // namespace fud -- cgit v1.2.3