/* * 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 { SqliteDbResult SqliteDb::make(const String& name, SqliteOpenMode mode, int extraFlags) { SqliteDb sqlDb{}; auto copyResult = sqlDb.m_name.copy(name); if (copyResult != FudStatus::Success) { return SqliteDbResult::error(copyResult); } sqlDb.m_mode = mode; sqlDb.m_extraFlags = extraFlags; auto status = sqlDb.initialize(); if (status != FudStatus::Success) { return SqliteDbResult::error(status); } return SqliteDbResult::okay(std::move(sqlDb)); } SqliteDbResult SqliteDb::make(const char* cStrName, SqliteOpenMode mode, int extraFlags) { auto nameResult = String::makeFromCString(cStrName); if (nameResult.isError()) { return SqliteDbResult::error(nameResult); } SqliteDb sqlDb{}; sqlDb.m_name = nameResult.takeOkay(); sqlDb.m_mode = mode; sqlDb.m_extraFlags = extraFlags; auto status = sqlDb.initialize(); if (status != FudStatus::Success) { return SqliteDbResult::error(status); } return SqliteDbResult::okay(std::move(sqlDb)); } SqliteDb::SqliteDb(SqliteDb&& rhs) noexcept : 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) noexcept { if (m_dbHandle != nullptr) { sqlite3_close(m_dbHandle); m_dbHandle = nullptr; } 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; 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, std::unique_ptr errorMessage) { if (!valid()) { return FudStatus::ObjectInvalid; } if (!statement.utf8Valid()) { return FudStatus::Utf8Invalid; } char* errorMsgPtr = nullptr; char** errorMsgPtrAddress = nullptr; if (errorMessage != nullptr) { errorMsgPtrAddress = &errorMsgPtr; } m_errorCode = sqlite3_exec(m_dbHandle, statement.c_str(), callback, context, errorMsgPtrAddress); if (errorMessage != nullptr) { errorMessage = std::make_unique(errorMsgPtr); } return m_errorCode == SQLITE_OK ? FudStatus::Success : FudStatus::Failure; } FudStatus SqliteDb::initialize() { m_nameValid = m_name.utf8Valid(); if (!m_nameValid) { return FudStatus::ArgumentInvalid; } m_errorCode = open(); if (m_errorCode != SQLITE_OK) { return FudStatus::Failure; } return FudStatus::Success; } 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& sql) { using RetType = Result; SqliteStatement preparedStatement{*this, sql}; 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::ArgumentInvalid; 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) noexcept : 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() { 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; } SqliteErrorMsg::SqliteErrorMsg(SqliteErrorMsg&& rhs) noexcept : m_errorMsg{rhs.m_errorMsg} { rhs.m_errorMsg = nullptr; } SqliteErrorMsg& SqliteErrorMsg::operator=(SqliteErrorMsg&& rhs) noexcept { if (m_errorMsg != nullptr) { sqlite3_free(m_errorMsg); m_errorMsg = nullptr; } m_errorMsg = rhs.m_errorMsg; rhs.m_errorMsg = nullptr; return *this; } SqliteErrorMsg::~SqliteErrorMsg() { if (m_errorMsg != nullptr) { sqlite3_free(m_errorMsg); m_errorMsg = nullptr; } } } // namespace fud