LiteLoaderBDS-1.16.40/LiteLoader/Header/DB/Stmt.h
2022-09-21 19:47:03 +08:00

673 lines
17 KiB
C++

#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 <typename T>
struct BindSequenceType
{
T values;
static_assert(std::is_same<typename T::value_type, Any>::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 <typename T>
struct BindMapType
{
T values;
static_assert(std::is_same<typename T::key_type, std::string>::value, "Map key type must be std::string");
static_assert(std::is_same<typename T::mapped_type, Any>::value, "Map value type must be DB::Any");
};
template <typename T>
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<Session> parent; ///< Parent session
std::weak_ptr<Stmt> self;
public:
Stmt(const std::weak_ptr<Session>& 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 <typename T = Row>
inline T fetch()
{
return row_to<T>(_Fetch());
}
/**
* @brief Fetch the current row.
*
* @param[out] row The current row
* @return Stmt& *this
*/
template <typename T = Row>
inline Stmt& fetch(T& row)
{
row = row_to<T>(_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<bool(const Row&)> 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<bool(const Row&)> cb)
{
return fetchEach(cb);
}
//virtual Stmt& fetchAll(std::function<bool(const Row&)> cb);
/**
* @brief Fetch all the result rows.
*
* @tparam T The value type of vector
* @param[out] rows The result set
* @return Stmt& *this
*/
template <typename T>
inline Stmt& fetchAll(std::vector<T>& rows) {
return fetchEach([&](const Row& row) {
rows.push_back(row_to<T>(row));
return true;
});
return *this;
}
/**
* @brief Fetch all the result rows.
*
* @tparam T The value type of vector
* @return std::vector<T> The result rows
*/
template <typename T>
inline std::vector<T> fetchAll() {
std::vector<T> 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<Session> The session ptr
*/
virtual std::weak_ptr<Session> getParent() const;
/**
* @brief Get the shared pointer point to this
*
* @return SharedPointer<Stmt> The ptr
*/
virtual SharedPointer<Stmt> 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<Stmt> this
*/
inline SharedPointer<Stmt> 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<Stmt> this
*/
template <typename T>
inline SharedPointer<Stmt> operator>>(T& v)
{
fetch(v);
return getSharedPointer();
}
template <>
inline SharedPointer<Stmt> operator>>(ResultSet& v)
{
fetchAll(v);
return getSharedPointer();
}
template <typename T>
inline SharedPointer<Stmt> operator>>(std::vector<T>& v)
{
fetchAll(v);
return getSharedPointer();
}
/**
* @brief Operator, to bind single values.
*
* @param b The return value of DB::use
* @return SharedPointer<Stmt> this
*/
virtual SharedPointer<Stmt> operator,(const BindType& b);
/**
* @brief Operator, to bind a sequence container.
*
* @param b The return value of DB::use
* @return SharedPointer<Stmt> this
*/
template <typename T>
inline SharedPointer<Stmt> operator,(const BindSequenceType<T>& 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<Stmt> this
*/
template <>
inline SharedPointer<Stmt> operator,(const BindSequenceType<Row>& 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<Stmt> this
*/
template <typename T>
inline SharedPointer<Stmt> operator,(const BindMapType<T>& 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<Stmt> this
*/
template <typename T>
inline SharedPointer<Stmt> operator,(IntoType<T>& i)
{
if (!done()) fetch<T>(i.value);
return getSharedPointer();
}
/**
* @brief Operator, to store a set of results.
*
* @param i The return value of DB::into
* @return SharedPointer<Stmt> this
*/
template <typename T>
inline SharedPointer<Stmt> operator,(IntoType<std::vector<T>>& i)
{
fetchAll<std::vector<T>>(i.value);
return getSharedPointer();
}
/**
* @brief Operator, to store a set of results.
*
* @param i The return value of DB::into
* @return SharedPointer<Stmt> this
*/
template <>
inline SharedPointer<Stmt> operator,(IntoType<ResultSet>& i)
{
fetchAll(i.value);
return getSharedPointer();
}
/**
* @brief Operator, to store a row of results.
*
* @param i The return value of DB::into
* @return SharedPointer<Stmt> this
*/
template <>
inline SharedPointer<Stmt> operator,(IntoType<Row>& 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<Row> use(const Row& values)
{
return BindSequenceType<Row>{values};
}
template <typename T>
inline BindSequenceType<std::vector<T>> use(const std::vector<T>& values)
{
return BindSequenceType<std::vector<Any>>{to_any_container(values)};
}
template <typename T>
inline BindSequenceType<std::set<T>> use(const std::set<T>& values)
{
return BindSequenceType<std::set<T>>{to_any_container(values)};
}
template <typename T>
inline BindSequenceType<std::list<T>> use(const std::list<T>& values)
{
return BindSequenceType<std::list<T>>{to_any_container(values)};
}
template <typename T>
inline BindSequenceType<std::vector<T>> use(const std::initializer_list<T>& values)
{
return BindSequenceType<std::vector<T>>{to_any_container(std::vector<T>(values))};
}
template <>
inline BindSequenceType<std::vector<Any>> use(const std::vector<Any>& values)
{
return BindSequenceType<std::vector<Any>>{values};
}
template <>
inline BindSequenceType<std::set<Any>> use(const std::set<Any>& values)
{
return BindSequenceType<std::set<Any>>{values};
}
template <>
inline BindSequenceType<std::list<Any>> use(const std::list<Any>& values)
{
return BindSequenceType<std::list<Any>>{values};
}
template <>
inline BindSequenceType<std::vector<Any>> use(const std::initializer_list<Any>& values)
{
return BindSequenceType<std::vector<Any>>{std::vector<Any>(values)};
}
// Map
template <typename T>
inline BindMapType<std::map<std::string, T>> use(const std::map<std::string, T>& values)
{
return BindMapType<std::map<std::string, T>>{values};
}
template <typename T>
inline BindMapType<std::unordered_map<std::string, T>> use(const std::unordered_map<std::string, T>& values)
{
return BindMapType<std::unordered_map<std::string, T>>{values};
}
template <>
inline BindMapType<std::map<std::string, Any>> use(const std::map<std::string, Any>& values)
{
return BindMapType<std::map<std::string, Any>>{values};
}
template <>
inline BindMapType<std::unordered_map<std::string, Any>> use(const std::unordered_map<std::string, Any>& values)
{
return BindMapType<std::unordered_map<std::string, Any>>{values};
}
inline BindMapType<std::map<std::string, Any>> use(const std::initializer_list<std::pair<std::string, Any>>& values)
{
std::map<std::string, Any> result;
for (auto& pair : values)
{
result.insert(std::make_pair(pair.first, pair.second));
}
return BindMapType<std::map<std::string, Any>>{result};
}
template <typename T>
inline IntoType<T> into(T& out)
{
return IntoType<T>{out};
}
} // namespace DB