#pragma once #include "RowSet.h" #include "Pointer.h" #define IF_ENDBG if (debugOutput) class Logger; namespace DB { extern Logger dbLogger; class Session; /** * @brief Structure to store a single value to bind to a prepared statement. * * @tparam T Type of sequence container, must have begin() and end() methods * @tparam The value type of the container must be DB::Any. */ struct BindType { Any value; std::string name; int idx = -1; }; /** * @brief Structure to store a sequential container * to bind multiple parameters at once. * * @tparam T Type of sequence container, must have begin() and end() methods * @tparam The value type of the container must be DB::Any. */ template struct BindSequenceType { T values; static_assert(std::is_same::value, "Container value type must be DB::Any"); }; /** * @brief Structure to store a map(relevance) container * to bind multiple parameters at once. * * @tparam T Type of map container, must have begin() and end() methods * @note The key type of the map must be std::string, * and the value type of the map must be DB::Any. */ template struct BindMapType { T values; static_assert(std::is_same::value, "Map key type must be std::string"); static_assert(std::is_same::value, "Map value type must be DB::Any"); }; template struct IntoType { T& value; }; class Stmt { protected: #if defined(LLDB_DEBUG_MODE) bool debugOutput = true; #else bool debugOutput = false; #endif bool autoExecute = false; ///< Whether to automatically execute the statement on bind std::weak_ptr parent; ///< Parent session std::weak_ptr self; public: Stmt(const std::weak_ptr& parent, bool autoExecute = false); virtual ~Stmt(); /** * @brief Turn on/off debug output. * * @param enable Enable or not */ LIAPI void setDebugOutput(bool enable); /** * @brief Bind a value to a statement parameter. * * @param value Value to bind * @param index Parameter index * @throws std::runtime_error If error occurs * * @par Implementation * @see SQLiteStmt::bind */ virtual Stmt& bind(const Any& value, int index) = 0; /** * @brief Bind a value to a statement parameter. * * @param value Value to bind * @param name Parameter name * @throws std::runtime_error If error occurs * * @par Impletementation * @see SQLiteStmt::bind */ virtual Stmt& bind(const Any& value, const std::string& name) = 0; /** * @brief Bind a value to the next statement parameter. * * @param value Value to bind * @throws std::runtime_error If error occurs * * @par Impletementation * @see SQLiteStmt::bind */ virtual Stmt& bind(const Any& value) = 0; /** * @brief Execute the statement(after binding all the parameters) * * @return Stmt& *this * @note If `this->autoExecute` is true, there is no need to call this method */ virtual Stmt& execute() = 0; /** * @brief Step to the next row(not fetch). * * @return bool True if there is a next row * * @par Impletementation * @see SQLiteStmt::step */ virtual bool step() = 0; /** * @brief Step to the next row(=step). * * @return bool True if there is a next row * * @par Impletementation * @see SQLiteStmt::next */ virtual bool next() = 0; /** * @brief Get weather all the rows have been fetched. * * @return bool True if all the rows have been fetched * * @par Impletementation * @see SQLiteStmt::done */ virtual bool done() = 0; /** * @brief Fetch the current row. * * @tparam T The type of the value to return * @return T The current row(converted) * @throws std::runtime_error If there is no row to fetch * * @par Example * @code * auto stmt = sess->prepare("SELECT * FROM table"); * while (stmt->step()) { * auto row = stmt->fetch(); * // Do something with the row * } * stmt->close(); * @endcode */ template inline T fetch() { return row_to(_Fetch()); } /** * @brief Fetch the current row. * * @param[out] row The current row * @return Stmt& *this */ template inline Stmt& fetch(T& row) { row = row_to(_Fetch()); return *this; } /** * @brief Fetch each of the result rows. * * @param cb Callback function to handle the result rows * @return Stmt& *this * @note Return false in callback to stop fetching * * @par Example * @code * sess->prepare("SELECT * FROM table") * ->fetchEach([](const Row& row) { * // Do something with the row * return true; * }) * ->close(); * @endcode */ inline Stmt& fetchEach(std::function cb) { do { auto res = _Fetch(); if (res.size() == 0) { continue; } if (!cb(res)) { break; } } while (step()); return *this; } /** * @brief Fetch each of the result rows(For compatibility). * * @param cb Callback function to handle the result rows * @return Stmt& *this * @note Return false in callback to stop fetching * @see Stmt::fetchEach */ inline Stmt& fetchAll(std::function cb) { return fetchEach(cb); } //virtual Stmt& fetchAll(std::function cb); /** * @brief Fetch all the result rows. * * @tparam T The value type of vector * @param[out] rows The result set * @return Stmt& *this */ template inline Stmt& fetchAll(std::vector& rows) { return fetchEach([&](const Row& row) { rows.push_back(row_to(row)); return true; }); return *this; } /** * @brief Fetch all the result rows. * * @tparam T The value type of vector * @return std::vector The result rows */ template inline std::vector fetchAll() { std::vector result; fetchAll(result); return result; } //virtual ResultSet fetchAll() = 0; //virtual Stmt& fetchAll(ResultSet& rows); inline ResultSet fetchAll() { ResultSet set; fetchAll(set); return set; } inline Stmt& fetchAll(ResultSet& rows) { return fetchEach([&rows](const Row& row) { rows.push_back(row); return true; }); } /** * @brief Reset the statement from executing state to perpared state * * @return Stmt& *this * * @par Note * Different between `reset()`, `reexec` and `clear()`: * - `reset()` : Reset the statement to the prepared state * - `reexec()`: Reset the statement to the prepared state and execute it * - `clear()` : Reset the statement to the prepared state and clear the parameters, but not execute it */ virtual Stmt& reset() = 0; /** * @brief Re-execute the statement(keep the currently bound value to re-excute). * * @return Stmt& *this * @note If you want to clear the bound value, use clear() instead. * @see Stmt::reset * * @par Impletementation * @see SQLiteStmt::reexec */ virtual Stmt& reexec() = 0; /** * @brief Clear all the bound values. * * @return Stmt& *this * @see Stmt::reset * * @par Impletementation * @see SQLiteStmt::clear */ virtual Stmt& clear() = 0; /** * @brief Close the statement. * * * @par Impletementation * @see SQLiteStmt::close */ virtual void close() = 0; /** * @brief Get the number of rows affected by the statement. * * @return int The number of rows affected * @note It will return -1(ULLONG_MAX - 1) if the row count is not available * * @par Impletementation * @see SQLiteStmt::getAffectedRows */ virtual uint64_t getAffectedRows() const = 0; /** * @brief Get the insert id of the statement * * @return uint64_t The insert id * @throws std::runtime_error If error occurs * @note It will return -1(ULLONG_MAX - 1) if the insert id is not available * * @par Implementation * @see SQLiteStmt::getInsertId */ virtual uint64_t getInsertId() const = 0; /** * @brief Get the number of the unbound parameters. * * @return int The number of the unbound parameters * * @par Impletementation * @see SQLiteStmt::getUnboundParams */ virtual int getUnboundParams() const = 0; /** * @brief Get the number of the bound parameters. * * @return int The number of the bound parameters * * @par Impletementation * @see SQLiteStmt::getBoundParams */ virtual int getBoundParams() const = 0; /** * @brief Get the number of parameters. * * @return int The number of parameters * * @par Impletementation * @see SQLiteStmt::getParamsCount */ virtual int getParamsCount() const = 0; /** * @brief Get the session. * * @return std::weak_ptr The session ptr */ virtual std::weak_ptr getParent() const; /** * @brief Get the shared pointer point to this * * @return SharedPointer The ptr */ virtual SharedPointer getSharedPointer() const; /** * @brief Get the session type * * @return DB::DBType The database type * * @par Impletementation * @see SQLiteStmt::getType */ virtual DBType getType() const = 0; /** * @brief Fetch the current row(internal). * * @return Row The current row */ virtual Row _Fetch() = 0; /** * @brief Operator<< to bind values. * * @param v The value * @return SharedPointer this */ inline SharedPointer operator<<(const Any& v) { bind(v); return getSharedPointer(); } /** * @brief Operator>> to store the result. * * @tparam T The value type * @param v Where to store * @return SharedPointer this */ template inline SharedPointer operator>>(T& v) { fetch(v); return getSharedPointer(); } template <> inline SharedPointer operator>>(ResultSet& v) { fetchAll(v); return getSharedPointer(); } template inline SharedPointer operator>>(std::vector& v) { fetchAll(v); return getSharedPointer(); } /** * @brief Operator, to bind single values. * * @param b The return value of DB::use * @return SharedPointer this */ virtual SharedPointer operator,(const BindType& b); /** * @brief Operator, to bind a sequence container. * * @param b The return value of DB::use * @return SharedPointer this */ template inline SharedPointer operator,(const BindSequenceType& b) { for (auto& v : b.values) { bind(v); } return getSharedPointer(); } /** * @brief Operator, to bind a row. * * @param b The return value of DB::use * @return SharedPointer this */ template <> inline SharedPointer operator,(const BindSequenceType& b) { if (b.values.header && b.values.header->size()) { b.values.forEach([&](const std::string& name, const Any& value) { bind(value, name); return true; }); } else { for (auto& v : b.values) { bind(v); } } return getSharedPointer(); } /** * @brief Operator, to bind a map container. * * @param b The return value of DB::bind * @return SharedPointer this */ template inline SharedPointer operator,(const BindMapType& b) { for (auto& v : b.values) { bind(v.second, v.first); } return getSharedPointer(); } /** * @brief Operator, to store a row of results. * * @param i The return value of DB::into * @return SharedPointer this */ template inline SharedPointer operator,(IntoType& i) { if (!done()) fetch(i.value); return getSharedPointer(); } /** * @brief Operator, to store a set of results. * * @param i The return value of DB::into * @return SharedPointer this */ template inline SharedPointer operator,(IntoType>& i) { fetchAll>(i.value); return getSharedPointer(); } /** * @brief Operator, to store a set of results. * * @param i The return value of DB::into * @return SharedPointer this */ template <> inline SharedPointer operator,(IntoType& i) { fetchAll(i.value); return getSharedPointer(); } /** * @brief Operator, to store a row of results. * * @param i The return value of DB::into * @return SharedPointer this */ template <> inline SharedPointer operator,(IntoType& i) { fetch(i.value); return getSharedPointer(); } /** * @brief Operator-> to implement better API. * * @return Stmt* this */ inline Stmt* operator->() { return this; } }; inline BindType use(const Any& value, int idx = -1) { return BindType{value, std::string(), idx}; } inline BindType use(const Any& value, const std::string& name) { return BindType{value, name}; } inline BindSequenceType use(const Row& values) { return BindSequenceType{values}; } template inline BindSequenceType> use(const std::vector& values) { return BindSequenceType>{to_any_container(values)}; } template inline BindSequenceType> use(const std::set& values) { return BindSequenceType>{to_any_container(values)}; } template inline BindSequenceType> use(const std::list& values) { return BindSequenceType>{to_any_container(values)}; } template inline BindSequenceType> use(const std::initializer_list& values) { return BindSequenceType>{to_any_container(std::vector(values))}; } template <> inline BindSequenceType> use(const std::vector& values) { return BindSequenceType>{values}; } template <> inline BindSequenceType> use(const std::set& values) { return BindSequenceType>{values}; } template <> inline BindSequenceType> use(const std::list& values) { return BindSequenceType>{values}; } template <> inline BindSequenceType> use(const std::initializer_list& values) { return BindSequenceType>{std::vector(values)}; } // Map template inline BindMapType> use(const std::map& values) { return BindMapType>{values}; } template inline BindMapType> use(const std::unordered_map& values) { return BindMapType>{values}; } template <> inline BindMapType> use(const std::map& values) { return BindMapType>{values}; } template <> inline BindMapType> use(const std::unordered_map& values) { return BindMapType>{values}; } inline BindMapType> use(const std::initializer_list>& values) { std::map result; for (auto& pair : values) { result.insert(std::make_pair(pair.first, pair.second)); } return BindMapType>{result}; } template inline IntoType into(T& out) { return IntoType{out}; } } // namespace DB