/* * 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. */ #ifndef FUD_SQLITE_HPP #define FUD_SQLITE_HPP #include #include #include #include namespace fud { class SqliteStatement; // NOLINTNEXTLINE(performance-enum-size) enum class SqliteOpenMode : int { ReadOnly = SQLITE_OPEN_READONLY, ReadWrite = SQLITE_OPEN_READWRITE, ReadWriteCreate = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE }; class SqliteErrorMsg; class SqliteDb; using SqliteDbResult = Result; class SqliteDb { private: constexpr SqliteDb() = default; public: static SqliteDbResult make(const String& name, SqliteOpenMode mode, int extraFlags = 0); static SqliteDbResult make(StringView name, SqliteOpenMode mode, int extraFlags = 0); static SqliteDbResult make(const char* cStrName, SqliteOpenMode mode, int extraFlags = 0); SqliteDb(const SqliteDb&) = delete; SqliteDb(SqliteDb&& rhs) noexcept; ~SqliteDb(); SqliteDb& operator=(const SqliteDb&) = delete; SqliteDb& operator=(SqliteDb&& rhs) noexcept; [[nodiscard]] bool valid() const; bool revalidate(); Result prepare(const String& sql); FudStatus exec( const String& statement, int (*callback)(void*, int, char**, char**), void* context); FudStatus exec( const String& statement, int (*callback)(void*, int, char**, char**), void* context, SqliteErrorMsg& errorMessage); template FudStatus bind(SqliteStatement& statement, int index, T value); FudStatus step(SqliteStatement& statement); Result columnInt32(SqliteStatement& statement, int index); Result columnInt64(SqliteStatement& statement, int index); Result columnDouble(SqliteStatement& statement, int index); Result columnText(SqliteStatement& statement, int index); [[nodiscard]] constexpr int errorCode() const { return m_errorCode; } [[nodiscard]] constexpr sqlite3* handle() const { return m_dbHandle; } private: static SqliteDbResult finishMake(SqliteOpenMode& mode, int& extraFlags, SqliteDb&& sqlDb); // private methods FudStatus initialize(); [[nodiscard]] int open(); // private data members String m_name{}; bool m_nameValid{false}; sqlite3* m_dbHandle{nullptr}; int m_errorCode{0}; SqliteOpenMode m_mode{}; int m_extraFlags{}; }; class SqliteStatement { public: SqliteStatement(const SqliteDb& sqliteDb, const String& input); SqliteStatement(const SqliteStatement&) = delete; SqliteStatement(SqliteStatement&& rhs) noexcept; ~SqliteStatement(); SqliteStatement& operator=(const SqliteStatement&) = delete; SqliteStatement& operator=(SqliteStatement&& rhs) noexcept = delete; [[nodiscard]] bool valid() const; sqlite3_stmt* statement(); FudStatus reset(); [[nodiscard]] constexpr int errorCode() const { return m_errorCode; } constexpr FudStatus status() { return m_status; } private: const char* m_tail{nullptr}; FudStatus m_status{FudStatus::ObjectInvalid}; int m_errorCode{0}; sqlite3_stmt* m_preparedStatement{nullptr}; }; class SqliteErrorMsg { public: constexpr SqliteErrorMsg() = default; explicit constexpr SqliteErrorMsg(char* errorMsg) : m_errorMsg{errorMsg} { } SqliteErrorMsg(const SqliteErrorMsg&) = delete; SqliteErrorMsg(SqliteErrorMsg&& rhs) noexcept; ~SqliteErrorMsg(); SqliteErrorMsg& operator=(const SqliteErrorMsg&) = delete; SqliteErrorMsg& operator=(SqliteErrorMsg&& rhs) noexcept; void setMessage(char* newMessage); [[nodiscard]] const char* message() const { return m_errorMsg; } private: char* m_errorMsg{nullptr}; }; template FudStatus SqliteDb::bind(SqliteStatement& statement, int index, T value) { static_assert(std::is_same_v|| std::is_same_v || std::is_same_v || std::is_same_v); if (index < 0) { return FudStatus::ArgumentInvalid; } if constexpr (std::is_same_v) { m_errorCode = sqlite3_bind_int(statement.statement(), index, value); } else if constexpr (std::is_same_v) { m_errorCode = sqlite3_bind_int64(statement.statement(), index, value); } else if constexpr (std::is_same_v) { m_errorCode = sqlite3_bind_double(statement.statement(), index, value); } else if constexpr (std::is_same_v) { if (value.length() > std::numeric_limits::max()) { return FudStatus::ArgumentInvalid; } int stringLength = static_cast(value.length()); m_errorCode = ::sqlite3_bind_text(statement.statement(), index, value.c_str(), stringLength, nullptr); } else { return FudStatus::Failure; } if (m_errorCode != SQLITE_OK) { return FudStatus::Failure; } return FudStatus::Success; } } // namespace fud #endif