| | |
| | | #include "qtl_common.hpp" |
| | | #include "qtl_async.hpp" |
| | | |
| | | #if LIBMYSQL_VERSION_ID >=80000 |
| | | #if LIBMYSQL_VERSION_ID >= 80000 |
| | | typedef bool my_bool; |
| | | #endif //MySQL 8 |
| | | #endif // MySQL 8 |
| | | |
| | | #ifdef MARIADB_VERSION_ID |
| | | #define IS_LONGDATA(t) ((t) >= MYSQL_TYPE_TINY_BLOB && (t) <= MYSQL_TYPE_STRING) |
| | | #endif //MariaDB |
| | | #endif // MariaDB |
| | | |
| | | namespace qtl |
| | | { |
| | | |
| | | namespace mysql |
| | | { |
| | | namespace mysql |
| | | { |
| | | |
| | | struct init |
| | | { |
| | | init(int argc=-1, char **argv=nullptr, char **groups=nullptr) |
| | | { |
| | | //my_init(); |
| | | mysql_library_init(argc, argv, groups); |
| | | } |
| | | ~init() |
| | | { |
| | | mysql_library_end(); |
| | | } |
| | | }; |
| | | |
| | | struct thread_init |
| | | { |
| | | thread_init() |
| | | { |
| | | mysql_thread_init(); |
| | | } |
| | | ~thread_init() |
| | | { |
| | | mysql_thread_end(); |
| | | } |
| | | }; |
| | | |
| | | class binder : public MYSQL_BIND |
| | | { |
| | | friend class statement; |
| | | public: |
| | | binder() |
| | | { |
| | | init(); |
| | | } |
| | | void init() |
| | | { |
| | | memset(this, 0, sizeof(MYSQL_BIND)); |
| | | } |
| | | void bind() |
| | | { |
| | | init(); |
| | | buffer_type=MYSQL_TYPE_NULL; |
| | | } |
| | | void bind(null) |
| | | { |
| | | bind(); |
| | | } |
| | | void bind(std::nullptr_t) |
| | | { |
| | | bind(); |
| | | } |
| | | void bind(bool& v) |
| | | { |
| | | init(); |
| | | buffer_type = MYSQL_TYPE_BIT; |
| | | buffer = &v; |
| | | buffer_length = 1; |
| | | } |
| | | void bind(int8_t& v) |
| | | { |
| | | init(); |
| | | buffer_type=MYSQL_TYPE_TINY; |
| | | buffer=&v; |
| | | } |
| | | void bind(uint8_t& v) |
| | | { |
| | | init(); |
| | | buffer_type=MYSQL_TYPE_TINY; |
| | | buffer=&v; |
| | | is_unsigned=true; |
| | | } |
| | | void bind(int16_t& v) |
| | | { |
| | | init(); |
| | | buffer_type=MYSQL_TYPE_SHORT; |
| | | buffer=&v; |
| | | } |
| | | void bind(uint16_t& v) |
| | | { |
| | | init(); |
| | | buffer_type=MYSQL_TYPE_SHORT; |
| | | buffer=&v; |
| | | is_unsigned=true; |
| | | } |
| | | void bind(int32_t& v) |
| | | { |
| | | init(); |
| | | buffer_type=MYSQL_TYPE_LONG; |
| | | buffer=&v; |
| | | } |
| | | void bind(uint32_t& v) |
| | | { |
| | | init(); |
| | | buffer_type=MYSQL_TYPE_LONG; |
| | | buffer=&v; |
| | | is_unsigned=true; |
| | | } |
| | | void bind(int64_t& v) |
| | | { |
| | | init(); |
| | | buffer_type=MYSQL_TYPE_LONGLONG; |
| | | buffer=&v; |
| | | } |
| | | void bind(uint64_t& v) |
| | | { |
| | | init(); |
| | | buffer_type=MYSQL_TYPE_LONGLONG; |
| | | buffer=&v; |
| | | is_unsigned=true; |
| | | } |
| | | void bind(double& v) |
| | | { |
| | | init(); |
| | | buffer_type=MYSQL_TYPE_DOUBLE; |
| | | buffer=&v; |
| | | } |
| | | void bind(float& v) |
| | | { |
| | | init(); |
| | | buffer_type=MYSQL_TYPE_FLOAT; |
| | | buffer=&v; |
| | | } |
| | | void bind(MYSQL_TIME& v, enum_field_types type=MYSQL_TYPE_TIMESTAMP) |
| | | { |
| | | init(); |
| | | buffer_type=type; |
| | | buffer=&v; |
| | | } |
| | | void bind(void* data, unsigned long length, enum_field_types type=MYSQL_TYPE_BLOB) |
| | | { |
| | | init(); |
| | | buffer_type=type; |
| | | buffer=data; |
| | | buffer_length=length; |
| | | } |
| | | void bind(const const_blob_data& data, enum_field_types type=MYSQL_TYPE_BLOB) |
| | | { |
| | | init(); |
| | | buffer_type=type; |
| | | buffer=const_cast<void*>(data.data); |
| | | buffer_length=data.size; |
| | | } |
| | | void bind(blob_data& data, enum_field_types type=MYSQL_TYPE_BLOB) |
| | | { |
| | | init(); |
| | | buffer_type=type; |
| | | buffer=data.data; |
| | | buffer_length=data.size; |
| | | } |
| | | }; |
| | | |
| | | |
| | | template<typename T> |
| | | inline void bind(binder& binder, const T& v) |
| | | { |
| | | binder.bind(const_cast<T&>(v)); |
| | | } |
| | | |
| | | template<typename T> |
| | | inline void bind(binder& binder, T&& v) |
| | | { |
| | | binder.bind(v); |
| | | } |
| | | |
| | | inline void bind(binder& binder, const char* str, size_t length=0) |
| | | { |
| | | if(length==0) length=strlen(str); |
| | | binder.bind(const_cast<char*>(str), static_cast<unsigned long>(length), MYSQL_TYPE_VAR_STRING); |
| | | } |
| | | |
| | | class base_statement; |
| | | class basic_database; |
| | | |
| | | class error : public std::exception |
| | | { |
| | | public: |
| | | error() : m_error(0) { } |
| | | error(unsigned int err, const char* errmsg) : m_error(err), m_errmsg(errmsg) { } |
| | | explicit error(const base_statement& stmt); |
| | | explicit error(const basic_database& db); |
| | | error(const error& src) = default; |
| | | virtual ~error() throw() { } |
| | | int code() const throw() { return m_error; } |
| | | operator bool() const { return m_error != 0; } |
| | | virtual const char* what() const NOEXCEPT override { return m_errmsg.data(); } |
| | | private: |
| | | unsigned int m_error; |
| | | std::string m_errmsg; |
| | | }; |
| | | |
| | | class blobbuf : public qtl::blobbuf |
| | | { |
| | | public: |
| | | blobbuf() : m_stmt(nullptr), m_field(0) |
| | | { |
| | | } |
| | | blobbuf(const blobbuf&) = default; |
| | | blobbuf& operator=(const blobbuf&) = default; |
| | | virtual ~blobbuf() { overflow(); } |
| | | |
| | | void open(MYSQL_STMT* stmt, int field, const binder& b, std::ios_base::openmode mode) |
| | | { |
| | | if (m_stmt && m_field) |
| | | struct init |
| | | { |
| | | overflow(); |
| | | } |
| | | |
| | | assert(stmt != nullptr); |
| | | m_stmt = stmt; |
| | | m_field = field; |
| | | m_binder = b; |
| | | if (b.length) m_size = *b.length; |
| | | init_buffer(mode); |
| | | } |
| | | |
| | | void swap(blobbuf& other) |
| | | { |
| | | std::swap(m_stmt, other.m_stmt); |
| | | std::swap(m_binder, other.m_binder); |
| | | std::swap(m_field, other.m_field); |
| | | qtl::blobbuf::swap(other); |
| | | } |
| | | |
| | | private: |
| | | MYSQL_STMT* m_stmt; |
| | | binder m_binder; |
| | | int m_field; |
| | | |
| | | protected: |
| | | virtual bool read_blob(char* buffer, off_type& count, pos_type position) override |
| | | { |
| | | m_binder.buffer = buffer; |
| | | m_binder.buffer_length = count; |
| | | int ret = mysql_stmt_fetch_column(m_stmt, &m_binder, m_field, position); |
| | | switch (ret) |
| | | { |
| | | case 0: |
| | | count = std::min(m_binder.buffer_length, *m_binder.length); |
| | | return true; |
| | | case CR_NO_DATA: |
| | | return false; |
| | | default: |
| | | throw error(mysql_stmt_errno(m_stmt), mysql_stmt_error(m_stmt)); |
| | | } |
| | | } |
| | | |
| | | virtual void write_blob(const char* buffer, size_t count) override |
| | | { |
| | | int ret = mysql_stmt_send_long_data(m_stmt, m_field, buffer, count); |
| | | if (ret != 0) |
| | | throw error(mysql_stmt_errno(m_stmt), mysql_stmt_error(m_stmt)); |
| | | } |
| | | }; |
| | | |
| | | struct time : public MYSQL_TIME |
| | | { |
| | | time() |
| | | { |
| | | memset(this, 0, sizeof(MYSQL_TIME)); |
| | | time_type = MYSQL_TIMESTAMP_NONE; |
| | | } |
| | | time(const struct tm& tm) |
| | | { |
| | | memset(this, 0, sizeof(MYSQL_TIME)); |
| | | year = tm.tm_year + 1900; |
| | | month = tm.tm_mon + 1; |
| | | day = tm.tm_mday; |
| | | hour = tm.tm_hour; |
| | | minute = tm.tm_min; |
| | | second = tm.tm_sec; |
| | | time_type = MYSQL_TIMESTAMP_DATETIME; |
| | | } |
| | | time(time_t value) |
| | | { |
| | | struct tm tm; |
| | | #if defined(_MSC_VER) |
| | | localtime_s(&tm, &value); |
| | | #elif defined(_POSIX_VERSION) |
| | | localtime_r(&value, &tm); |
| | | #else |
| | | tm = *localtime(&value); |
| | | #endif |
| | | new(this)time(tm); |
| | | } |
| | | time(const time& src) |
| | | { |
| | | memcpy(this, &src, sizeof(MYSQL_TIME)); |
| | | } |
| | | time& operator=(const time& src) |
| | | { |
| | | if (this != &src) |
| | | memcpy(this, &src, sizeof(MYSQL_TIME)); |
| | | return *this; |
| | | } |
| | | |
| | | static time now() |
| | | { |
| | | time_t value; |
| | | ::time(&value); |
| | | return time(value); |
| | | } |
| | | |
| | | time_t as_tm(struct tm& tm) const |
| | | { |
| | | tm.tm_year = year - 1900; |
| | | tm.tm_mon = month - 1; |
| | | tm.tm_mday = day; |
| | | tm.tm_hour = hour; |
| | | tm.tm_min = minute; |
| | | tm.tm_sec = second; |
| | | return mktime(&tm); |
| | | } |
| | | time_t get_time() const |
| | | { |
| | | struct tm tm; |
| | | return as_tm(tm); |
| | | } |
| | | }; |
| | | |
| | | class base_statement |
| | | { |
| | | protected: |
| | | base_statement() : m_stmt(nullptr), m_result(nullptr) {} |
| | | base_statement(const base_statement&) = delete; |
| | | explicit base_statement(basic_database& db); |
| | | base_statement(base_statement&& src) |
| | | : m_stmt(src.m_stmt), m_result(src.m_result), |
| | | m_binders(std::move(src.m_binders)), m_binderAddins(std::move(src.m_binderAddins)) |
| | | { |
| | | src.m_stmt=nullptr; |
| | | src.m_result=nullptr; |
| | | } |
| | | base_statement& operator=(const base_statement&) = delete; |
| | | base_statement& operator=(base_statement&& src) |
| | | { |
| | | if(this!=&src) |
| | | { |
| | | m_stmt=src.m_stmt; |
| | | m_result=src.m_result; |
| | | src.m_stmt=nullptr; |
| | | src.m_result=nullptr; |
| | | m_binders=std::move(src.m_binders); |
| | | m_binderAddins=std::move(src.m_binderAddins); |
| | | } |
| | | return *this; |
| | | } |
| | | |
| | | public: |
| | | operator MYSQL_STMT*() { return m_stmt; } |
| | | |
| | | unsigned int get_parameter_count() const { return mysql_stmt_param_count(m_stmt); } |
| | | unsigned int get_column_count() const { return mysql_stmt_field_count(m_stmt); } |
| | | |
| | | unsigned long length(unsigned int index) const |
| | | { |
| | | return m_binderAddins[index].m_length; |
| | | } |
| | | |
| | | bool is_null(unsigned int index) const |
| | | { |
| | | return m_binderAddins[index].m_isNull!=0; |
| | | } |
| | | |
| | | size_t find_field(const char* name) const |
| | | { |
| | | if(m_result) |
| | | { |
| | | for(size_t i=0; i!=m_result->field_count; i++) |
| | | init(int argc = -1, char **argv = nullptr, char **groups = nullptr) |
| | | { |
| | | if(strncmp(m_result->fields[i].name, name, m_result->fields[i].name_length)==0) |
| | | return i; |
| | | // my_init(); |
| | | mysql_library_init(argc, argv, groups); |
| | | } |
| | | } |
| | | return -1; |
| | | } |
| | | |
| | | my_ulonglong affetced_rows() |
| | | { |
| | | return mysql_stmt_affected_rows(m_stmt); |
| | | } |
| | | my_ulonglong insert_id() |
| | | { |
| | | return mysql_stmt_insert_id(m_stmt); |
| | | } |
| | | |
| | | binder* get_binder(unsigned long index) |
| | | { |
| | | return &m_binders[index]; |
| | | } |
| | | |
| | | unsigned int error() const |
| | | { |
| | | return mysql_stmt_errno(m_stmt); |
| | | } |
| | | const char* errmsg() const |
| | | { |
| | | return mysql_stmt_error(m_stmt); |
| | | } |
| | | |
| | | MYSQL_RES* result() { return m_result; } |
| | | |
| | | void bind_param(size_t index, const char* param, size_t length) |
| | | { |
| | | bind(m_binders[index], param, length); |
| | | } |
| | | |
| | | void bind_param(size_t index, const std::nullptr_t&) |
| | | { |
| | | m_binders[index].bind(); |
| | | } |
| | | |
| | | void bind_param(size_t index, std::istream& param) |
| | | { |
| | | m_binders[index].bind(nullptr, 0, MYSQL_TYPE_LONG_BLOB); |
| | | m_binderAddins[index].m_after_fetch = [this, index, ¶m](const binder&) { |
| | | std::array<char, blob_buffer_size> buffer; |
| | | unsigned long readed = 0; |
| | | while (!param.eof() && !param.fail()) |
| | | ~init() |
| | | { |
| | | param.read(buffer.data(), buffer.size()); |
| | | readed = (unsigned long)param.gcount(); |
| | | if (readed > 0) |
| | | mysql_library_end(); |
| | | } |
| | | }; |
| | | |
| | | struct thread_init |
| | | { |
| | | thread_init() |
| | | { |
| | | mysql_thread_init(); |
| | | } |
| | | ~thread_init() |
| | | { |
| | | mysql_thread_end(); |
| | | } |
| | | }; |
| | | |
| | | class binder : public MYSQL_BIND |
| | | { |
| | | friend class statement; |
| | | |
| | | public: |
| | | binder() |
| | | { |
| | | init(); |
| | | } |
| | | void init() |
| | | { |
| | | memset(this, 0, sizeof(MYSQL_BIND)); |
| | | } |
| | | void bind() |
| | | { |
| | | init(); |
| | | buffer_type = MYSQL_TYPE_NULL; |
| | | } |
| | | void bind(null) |
| | | { |
| | | bind(); |
| | | } |
| | | void bind(std::nullptr_t) |
| | | { |
| | | bind(); |
| | | } |
| | | void bind(bool &v) |
| | | { |
| | | init(); |
| | | buffer_type = MYSQL_TYPE_BIT; |
| | | buffer = &v; |
| | | buffer_length = 1; |
| | | } |
| | | void bind(int8_t &v) |
| | | { |
| | | init(); |
| | | buffer_type = MYSQL_TYPE_TINY; |
| | | buffer = &v; |
| | | } |
| | | void bind(uint8_t &v) |
| | | { |
| | | init(); |
| | | buffer_type = MYSQL_TYPE_TINY; |
| | | buffer = &v; |
| | | is_unsigned = true; |
| | | } |
| | | void bind(int16_t &v) |
| | | { |
| | | init(); |
| | | buffer_type = MYSQL_TYPE_SHORT; |
| | | buffer = &v; |
| | | } |
| | | void bind(uint16_t &v) |
| | | { |
| | | init(); |
| | | buffer_type = MYSQL_TYPE_SHORT; |
| | | buffer = &v; |
| | | is_unsigned = true; |
| | | } |
| | | void bind(int32_t &v) |
| | | { |
| | | init(); |
| | | buffer_type = MYSQL_TYPE_LONG; |
| | | buffer = &v; |
| | | } |
| | | void bind(uint32_t &v) |
| | | { |
| | | init(); |
| | | buffer_type = MYSQL_TYPE_LONG; |
| | | buffer = &v; |
| | | is_unsigned = true; |
| | | } |
| | | void bind(int64_t &v) |
| | | { |
| | | init(); |
| | | buffer_type = MYSQL_TYPE_LONGLONG; |
| | | buffer = &v; |
| | | } |
| | | void bind(uint64_t &v) |
| | | { |
| | | init(); |
| | | buffer_type = MYSQL_TYPE_LONGLONG; |
| | | buffer = &v; |
| | | is_unsigned = true; |
| | | } |
| | | void bind(double &v) |
| | | { |
| | | init(); |
| | | buffer_type = MYSQL_TYPE_DOUBLE; |
| | | buffer = &v; |
| | | } |
| | | void bind(float &v) |
| | | { |
| | | init(); |
| | | buffer_type = MYSQL_TYPE_FLOAT; |
| | | buffer = &v; |
| | | } |
| | | void bind(MYSQL_TIME &v, enum_field_types type = MYSQL_TYPE_TIMESTAMP) |
| | | { |
| | | init(); |
| | | buffer_type = type; |
| | | buffer = &v; |
| | | } |
| | | void bind(void *data, unsigned long length, enum_field_types type = MYSQL_TYPE_BLOB) |
| | | { |
| | | init(); |
| | | buffer_type = type; |
| | | buffer = data; |
| | | buffer_length = length; |
| | | } |
| | | void bind(const const_blob_data &data, enum_field_types type = MYSQL_TYPE_BLOB) |
| | | { |
| | | init(); |
| | | buffer_type = type; |
| | | buffer = const_cast<void *>(data.data); |
| | | buffer_length = data.size; |
| | | } |
| | | void bind(blob_data &data, enum_field_types type = MYSQL_TYPE_BLOB) |
| | | { |
| | | init(); |
| | | buffer_type = type; |
| | | buffer = data.data; |
| | | buffer_length = data.size; |
| | | } |
| | | }; |
| | | |
| | | template <typename T> |
| | | inline void bind(binder &binder, const T &v) |
| | | { |
| | | binder.bind(const_cast<T &>(v)); |
| | | } |
| | | |
| | | template <typename T> |
| | | inline void bind(binder &binder, T &&v) |
| | | { |
| | | binder.bind(v); |
| | | } |
| | | |
| | | inline void bind(binder &binder, const char *str, size_t length = 0) |
| | | { |
| | | if (length == 0) |
| | | length = strlen(str); |
| | | binder.bind(const_cast<char *>(str), static_cast<unsigned long>(length), MYSQL_TYPE_VAR_STRING); |
| | | } |
| | | |
| | | class base_statement; |
| | | class basic_database; |
| | | |
| | | class error : public std::exception |
| | | { |
| | | public: |
| | | error() : m_error(0) {} |
| | | error(unsigned int err, const char *errmsg) : m_error(err), m_errmsg(errmsg) {} |
| | | explicit error(const base_statement &stmt); |
| | | explicit error(const basic_database &db); |
| | | error(const error &src) = default; |
| | | virtual ~error() throw() {} |
| | | int code() const throw() { return m_error; } |
| | | operator bool() const { return m_error != 0; } |
| | | virtual const char *what() const NOEXCEPT override { return m_errmsg.data(); } |
| | | |
| | | private: |
| | | unsigned int m_error; |
| | | std::string m_errmsg; |
| | | }; |
| | | |
| | | class blobbuf : public qtl::blobbuf |
| | | { |
| | | public: |
| | | blobbuf() : m_stmt(nullptr), m_field(0) |
| | | { |
| | | } |
| | | blobbuf(const blobbuf &) = default; |
| | | blobbuf &operator=(const blobbuf &) = default; |
| | | virtual ~blobbuf() { overflow(); } |
| | | |
| | | void open(MYSQL_STMT *stmt, int field, const binder &b, std::ios_base::openmode mode) |
| | | { |
| | | if (m_stmt && m_field) |
| | | { |
| | | if (mysql_stmt_send_long_data(m_stmt, index, buffer.data(), readed) != 0) |
| | | throw_exception(); |
| | | overflow(); |
| | | } |
| | | |
| | | assert(stmt != nullptr); |
| | | m_stmt = stmt; |
| | | m_field = field; |
| | | m_binder = b; |
| | | if (b.length) |
| | | m_size = *b.length; |
| | | init_buffer(mode); |
| | | } |
| | | |
| | | void swap(blobbuf &other) |
| | | { |
| | | std::swap(m_stmt, other.m_stmt); |
| | | std::swap(m_binder, other.m_binder); |
| | | std::swap(m_field, other.m_field); |
| | | qtl::blobbuf::swap(other); |
| | | } |
| | | |
| | | private: |
| | | MYSQL_STMT *m_stmt; |
| | | binder m_binder; |
| | | int m_field; |
| | | |
| | | protected: |
| | | virtual bool read_blob(char *buffer, off_type &count, pos_type position) override |
| | | { |
| | | m_binder.buffer = buffer; |
| | | m_binder.buffer_length = count; |
| | | int ret = mysql_stmt_fetch_column(m_stmt, &m_binder, m_field, position); |
| | | switch (ret) |
| | | { |
| | | case 0: |
| | | count = std::min(m_binder.buffer_length, *m_binder.length); |
| | | return true; |
| | | case CR_NO_DATA: |
| | | return false; |
| | | default: |
| | | throw error(mysql_stmt_errno(m_stmt), mysql_stmt_error(m_stmt)); |
| | | } |
| | | } |
| | | }; |
| | | } |
| | | |
| | | void bind_param(size_t index, const blob_writer& param) |
| | | { |
| | | m_binders[index].bind(nullptr, 0, MYSQL_TYPE_LONG_BLOB); |
| | | m_binderAddins[index].m_after_fetch = [this, index, ¶m](const binder& b) { |
| | | blobbuf buf; |
| | | buf.open(m_stmt, index, b, std::ios::out); |
| | | std::ostream s(&buf); |
| | | param(s); |
| | | }; |
| | | } |
| | | |
| | | template<class Param> |
| | | void bind_param(size_t index, const Param& param) |
| | | { |
| | | bind(m_binders[index], param); |
| | | } |
| | | |
| | | template<class Type> |
| | | void bind_field(size_t index, Type&& value) |
| | | { |
| | | if (m_result) |
| | | { |
| | | bind(m_binders[index], std::forward<Type>(value)); |
| | | m_binderAddins[index].m_after_fetch = |
| | | if_null<typename std::remove_reference<Type>::type>(value); |
| | | } |
| | | } |
| | | |
| | | void bind_field(size_t index, char* value, size_t length) |
| | | { |
| | | m_binders[index].bind(value, length - 1, MYSQL_TYPE_VAR_STRING); |
| | | m_binderAddins[index].m_after_fetch = [](const binder& bind) { |
| | | if (*bind.is_null) |
| | | memset(bind.buffer, 0, bind.buffer_length + 1); |
| | | else |
| | | virtual void write_blob(const char *buffer, size_t count) override |
| | | { |
| | | char* text = reinterpret_cast<char*>(bind.buffer); |
| | | text[*bind.length] = '\0'; |
| | | int ret = mysql_stmt_send_long_data(m_stmt, m_field, buffer, count); |
| | | if (ret != 0) |
| | | throw error(mysql_stmt_errno(m_stmt), mysql_stmt_error(m_stmt)); |
| | | } |
| | | }; |
| | | } |
| | | |
| | | template<size_t N> |
| | | void bind_field(size_t index, std::array<char, N>&& value) |
| | | { |
| | | bind_field(index, value.data(), value.size()); |
| | | } |
| | | |
| | | template<typename T> |
| | | void bind_field(size_t index, bind_string_helper<T>&& value) |
| | | { |
| | | if (m_result) |
| | | struct time : public MYSQL_TIME |
| | | { |
| | | MYSQL_FIELD* field = mysql_fetch_field_direct(m_result, (unsigned int)index); |
| | | if (field == nullptr) throw_exception(); |
| | | value.clear(); |
| | | typename bind_string_helper<T>::char_type* data = value.alloc(field->length); |
| | | m_binderAddins[index].m_before_fetch = [this, value](binder& b) mutable { |
| | | if (value.size() < b.buffer_length) |
| | | time() |
| | | { |
| | | memset(this, 0, sizeof(MYSQL_TIME)); |
| | | time_type = MYSQL_TIMESTAMP_NONE; |
| | | } |
| | | time(const struct tm &tm) |
| | | { |
| | | memset(this, 0, sizeof(MYSQL_TIME)); |
| | | year = tm.tm_year + 1900; |
| | | month = tm.tm_mon + 1; |
| | | day = tm.tm_mday; |
| | | hour = tm.tm_hour; |
| | | minute = tm.tm_min; |
| | | second = tm.tm_sec; |
| | | time_type = MYSQL_TIMESTAMP_DATETIME; |
| | | } |
| | | time(time_t value) |
| | | { |
| | | struct tm tm; |
| | | #if defined(_MSC_VER) |
| | | localtime_s(&tm, &value); |
| | | #elif defined(_POSIX_VERSION) |
| | | localtime_r(&value, &tm); |
| | | #else |
| | | tm = *localtime(&value); |
| | | #endif |
| | | new (this) time(tm); |
| | | } |
| | | time(const time &src) |
| | | { |
| | | memcpy(this, &src, sizeof(MYSQL_TIME)); |
| | | } |
| | | time &operator=(const time &src) |
| | | { |
| | | if (this != &src) |
| | | memcpy(this, &src, sizeof(MYSQL_TIME)); |
| | | return *this; |
| | | } |
| | | |
| | | static time now() |
| | | { |
| | | time_t value; |
| | | ::time(&value); |
| | | return time(value); |
| | | } |
| | | |
| | | time_t as_tm(struct tm &tm) const |
| | | { |
| | | tm.tm_year = year - 1900; |
| | | tm.tm_mon = month - 1; |
| | | tm.tm_mday = day; |
| | | tm.tm_hour = hour; |
| | | tm.tm_min = minute; |
| | | tm.tm_sec = second; |
| | | return mktime(&tm); |
| | | } |
| | | time_t get_time() const |
| | | { |
| | | struct tm tm; |
| | | return as_tm(tm); |
| | | } |
| | | }; |
| | | |
| | | class base_statement |
| | | { |
| | | protected: |
| | | base_statement() : m_stmt(nullptr), m_result(nullptr) {} |
| | | base_statement(const base_statement &) = delete; |
| | | explicit base_statement(basic_database &db); |
| | | base_statement(base_statement &&src) |
| | | : m_stmt(src.m_stmt), m_result(src.m_result), |
| | | m_binders(std::move(src.m_binders)), m_binderAddins(std::move(src.m_binderAddins)) |
| | | { |
| | | src.m_stmt = nullptr; |
| | | src.m_result = nullptr; |
| | | } |
| | | base_statement &operator=(const base_statement &) = delete; |
| | | base_statement &operator=(base_statement &&src) |
| | | { |
| | | if (this != &src) |
| | | { |
| | | value.alloc(b.buffer_length); |
| | | if (b.buffer != value.data()) |
| | | m_stmt = src.m_stmt; |
| | | m_result = src.m_result; |
| | | src.m_stmt = nullptr; |
| | | src.m_result = nullptr; |
| | | m_binders = std::move(src.m_binders); |
| | | m_binderAddins = std::move(src.m_binderAddins); |
| | | } |
| | | return *this; |
| | | } |
| | | |
| | | public: |
| | | operator MYSQL_STMT *() { return m_stmt; } |
| | | |
| | | unsigned int get_parameter_count() const { return mysql_stmt_param_count(m_stmt); } |
| | | unsigned int get_column_count() const { return mysql_stmt_field_count(m_stmt); } |
| | | |
| | | unsigned long length(unsigned int index) const |
| | | { |
| | | return m_binderAddins[index].m_length; |
| | | } |
| | | |
| | | bool is_null(unsigned int index) const |
| | | { |
| | | return m_binderAddins[index].m_isNull != 0; |
| | | } |
| | | |
| | | size_t find_field(const char *name) const |
| | | { |
| | | if (m_result) |
| | | { |
| | | for (size_t i = 0; i != m_result->field_count; i++) |
| | | { |
| | | b.buffer = const_cast<char*>(value.data()); |
| | | mysql_stmt_bind_result(m_stmt, &m_binders.front()); |
| | | if (strncmp(m_result->fields[i].name, name, m_result->fields[i].name_length) == 0) |
| | | return i; |
| | | } |
| | | } |
| | | }; |
| | | m_binderAddins[index].m_after_fetch = [value](const binder& b) mutable { |
| | | if (*b.is_null) value.clear(); |
| | | else value.truncate(*b.length); |
| | | }; |
| | | m_binders[index].bind((void*)data, field->length, field->type); |
| | | } |
| | | } |
| | | return -1; |
| | | } |
| | | |
| | | void bind_field(size_t index, std::ostream&& value) |
| | | { |
| | | if (m_result) |
| | | { |
| | | MYSQL_FIELD* field = mysql_fetch_field_direct(m_result, (unsigned int)index); |
| | | assert(IS_LONGDATA(field->type)); |
| | | m_binders[index].bind(nullptr, 0, field->type); |
| | | m_binderAddins[index].m_after_fetch = [this, index, &value](const binder& b) { |
| | | unsigned long readed = 0; |
| | | std::array<char, blob_buffer_size> buffer; |
| | | binder& bb = const_cast<binder&>(b); |
| | | if (*b.is_null) return; |
| | | bb.buffer = const_cast<char*>(buffer.data()); |
| | | bb.buffer_length = buffer.size(); |
| | | while (readed <= *b.length) |
| | | my_ulonglong affetced_rows() |
| | | { |
| | | return mysql_stmt_affected_rows(m_stmt); |
| | | } |
| | | my_ulonglong insert_id() |
| | | { |
| | | return mysql_stmt_insert_id(m_stmt); |
| | | } |
| | | |
| | | binder *get_binder(unsigned long index) |
| | | { |
| | | return &m_binders[index]; |
| | | } |
| | | |
| | | unsigned int error() const |
| | | { |
| | | return mysql_stmt_errno(m_stmt); |
| | | } |
| | | const char *errmsg() const |
| | | { |
| | | return mysql_stmt_error(m_stmt); |
| | | } |
| | | |
| | | MYSQL_RES *result() { return m_result; } |
| | | |
| | | void bind_param(size_t index, const char *param, size_t length) |
| | | { |
| | | bind(m_binders[index], param, length); |
| | | } |
| | | |
| | | void bind_param(size_t index, const std::nullptr_t &) |
| | | { |
| | | m_binders[index].bind(); |
| | | } |
| | | |
| | | void bind_param(size_t index, std::istream ¶m) |
| | | { |
| | | m_binders[index].bind(nullptr, 0, MYSQL_TYPE_LONG_BLOB); |
| | | m_binderAddins[index].m_after_fetch = [this, index, ¶m](const binder &) |
| | | { |
| | | int ret = mysql_stmt_fetch_column(m_stmt, &bb, index, readed); |
| | | if (ret != 0) |
| | | throw_exception(); |
| | | value.write(buffer.data(), std::min(b.buffer_length, *b.length - b.offset)); |
| | | readed += bb.buffer_length; |
| | | std::array<char, blob_buffer_size> buffer; |
| | | unsigned long readed = 0; |
| | | while (!param.eof() && !param.fail()) |
| | | { |
| | | param.read(buffer.data(), buffer.size()); |
| | | readed = (unsigned long)param.gcount(); |
| | | if (readed > 0) |
| | | { |
| | | if (mysql_stmt_send_long_data(m_stmt, index, buffer.data(), readed) != 0) |
| | | throw_exception(); |
| | | } |
| | | } |
| | | }; |
| | | } |
| | | |
| | | void bind_param(size_t index, const blob_writer ¶m) |
| | | { |
| | | m_binders[index].bind(nullptr, 0, MYSQL_TYPE_LONG_BLOB); |
| | | m_binderAddins[index].m_after_fetch = [this, index, ¶m](const binder &b) |
| | | { |
| | | blobbuf buf; |
| | | buf.open(m_stmt, index, b, std::ios::out); |
| | | std::ostream s(&buf); |
| | | param(s); |
| | | }; |
| | | } |
| | | |
| | | template <class Param> |
| | | void bind_param(size_t index, const Param ¶m) |
| | | { |
| | | bind(m_binders[index], param); |
| | | } |
| | | |
| | | template <class Type> |
| | | void bind_field(size_t index, Type &&value) |
| | | { |
| | | if (m_result) |
| | | { |
| | | bind(m_binders[index], std::forward<Type>(value)); |
| | | m_binderAddins[index].m_after_fetch = |
| | | if_null<typename std::remove_reference<Type>::type>(value); |
| | | } |
| | | }; |
| | | } |
| | | } |
| | | } |
| | | |
| | | void bind_field(size_t index, blobbuf&& value) |
| | | { |
| | | if (m_result) |
| | | { |
| | | MYSQL_FIELD* field = mysql_fetch_field_direct(m_result, (unsigned int)index); |
| | | assert(IS_LONGDATA(field->type)); |
| | | m_binders[index].bind(nullptr, 0, field->type); |
| | | m_binderAddins[index].m_after_fetch = [this, index, &value](const binder& b) { |
| | | if (*b.is_null) return; |
| | | value.open(m_stmt, index, b, std::ios::in); |
| | | }; |
| | | } |
| | | } |
| | | void bind_field(size_t index, char *value, size_t length) |
| | | { |
| | | m_binders[index].bind(value, length - 1, MYSQL_TYPE_VAR_STRING); |
| | | m_binderAddins[index].m_after_fetch = [](const binder &bind) |
| | | { |
| | | if (*bind.is_null) |
| | | memset(bind.buffer, 0, bind.buffer_length + 1); |
| | | else |
| | | { |
| | | char *text = reinterpret_cast<char *>(bind.buffer); |
| | | text[*bind.length] = '\0'; |
| | | } |
| | | }; |
| | | } |
| | | |
| | | template<typename Type> |
| | | void bind_field(size_t index, indicator<Type>&& value) |
| | | { |
| | | if (m_result) |
| | | { |
| | | qtl::bind_field(*this, index, value.data); |
| | | binder_addin& addin = m_binderAddins[index]; |
| | | auto fetch_fun = addin.m_after_fetch; |
| | | addin.m_after_fetch = [&addin, fetch_fun, &value](const binder& b) { |
| | | value.is_null = *b.is_null != 0; |
| | | value.length = *b.length; |
| | | value.is_truncated = addin.is_truncated; |
| | | if (fetch_fun) fetch_fun(b); |
| | | }; |
| | | } |
| | | } |
| | | template <size_t N> |
| | | void bind_field(size_t index, std::array<char, N> &&value) |
| | | { |
| | | bind_field(index, value.data(), value.size()); |
| | | } |
| | | |
| | | template <typename T> |
| | | void bind_field(size_t index, bind_string_helper<T> &&value) |
| | | { |
| | | if (m_result) |
| | | { |
| | | MYSQL_FIELD *field = mysql_fetch_field_direct(m_result, (unsigned int)index); |
| | | if (field == nullptr) |
| | | throw_exception(); |
| | | value.clear(); |
| | | typename bind_string_helper<T>::char_type *data = value.alloc(field->length); |
| | | m_binderAddins[index].m_before_fetch = [this, value](binder &b) mutable |
| | | { |
| | | if (value.size() < b.buffer_length) |
| | | { |
| | | value.alloc(b.buffer_length); |
| | | if (b.buffer != value.data()) |
| | | { |
| | | b.buffer = const_cast<char *>(value.data()); |
| | | mysql_stmt_bind_result(m_stmt, &m_binders.front()); |
| | | } |
| | | } |
| | | }; |
| | | m_binderAddins[index].m_after_fetch = [value](const binder &b) mutable |
| | | { |
| | | if (*b.is_null) |
| | | value.clear(); |
| | | else |
| | | value.truncate(*b.length); |
| | | }; |
| | | m_binders[index].bind((void *)data, field->length, field->type); |
| | | } |
| | | } |
| | | |
| | | void bind_field(size_t index, std::ostream &&value) |
| | | { |
| | | if (m_result) |
| | | { |
| | | MYSQL_FIELD *field = mysql_fetch_field_direct(m_result, (unsigned int)index); |
| | | assert(IS_LONGDATA(field->type)); |
| | | m_binders[index].bind(nullptr, 0, field->type); |
| | | m_binderAddins[index].m_after_fetch = [this, index, &value](const binder &b) |
| | | { |
| | | unsigned long readed = 0; |
| | | std::array<char, blob_buffer_size> buffer; |
| | | binder &bb = const_cast<binder &>(b); |
| | | if (*b.is_null) |
| | | return; |
| | | bb.buffer = const_cast<char *>(buffer.data()); |
| | | bb.buffer_length = buffer.size(); |
| | | while (readed <= *b.length) |
| | | { |
| | | int ret = mysql_stmt_fetch_column(m_stmt, &bb, index, readed); |
| | | if (ret != 0) |
| | | throw_exception(); |
| | | value.write(buffer.data(), std::min(b.buffer_length, *b.length - b.offset)); |
| | | readed += bb.buffer_length; |
| | | } |
| | | }; |
| | | } |
| | | } |
| | | |
| | | void bind_field(size_t index, blobbuf &&value) |
| | | { |
| | | if (m_result) |
| | | { |
| | | MYSQL_FIELD *field = mysql_fetch_field_direct(m_result, (unsigned int)index); |
| | | assert(IS_LONGDATA(field->type)); |
| | | m_binders[index].bind(nullptr, 0, field->type); |
| | | m_binderAddins[index].m_after_fetch = [this, index, &value](const binder &b) |
| | | { |
| | | if (*b.is_null) |
| | | return; |
| | | value.open(m_stmt, index, b, std::ios::in); |
| | | }; |
| | | } |
| | | } |
| | | |
| | | template <typename Type> |
| | | void bind_field(size_t index, indicator<Type> &&value) |
| | | { |
| | | if (m_result) |
| | | { |
| | | qtl::bind_field(*this, index, value.data); |
| | | binder_addin &addin = m_binderAddins[index]; |
| | | auto fetch_fun = addin.m_after_fetch; |
| | | addin.m_after_fetch = [&addin, fetch_fun, &value](const binder &b) |
| | | { |
| | | value.is_null = *b.is_null != 0; |
| | | value.length = *b.length; |
| | | value.is_truncated = addin.is_truncated; |
| | | if (fetch_fun) |
| | | fetch_fun(b); |
| | | }; |
| | | } |
| | | } |
| | | |
| | | #ifdef _QTL_ENABLE_CPP17 |
| | | |
| | | template<typename T> |
| | | inline void bind_field(size_t index, std::optional<T>&& value) |
| | | { |
| | | if (m_result) |
| | | { |
| | | qtl::bind_field(*this, index, *value); |
| | | binder_addin& addin = m_binderAddins[index]; |
| | | auto fetch_fun = addin.m_after_fetch; |
| | | addin.m_after_fetch = [&addin, fetch_fun, &value](const binder& b) { |
| | | if (fetch_fun) fetch_fun(b); |
| | | if (*b.is_null) value.reset(); |
| | | }; |
| | | } |
| | | } |
| | | |
| | | inline void bind_field(size_t index, std::any&& value) |
| | | { |
| | | if (m_result) |
| | | { |
| | | MYSQL_FIELD* field = mysql_fetch_field_direct(m_result, (unsigned int)index); |
| | | if (field == nullptr) throw_exception(); |
| | | switch (field->type) |
| | | template <typename T> |
| | | inline void bind_field(size_t index, std::optional<T> &&value) |
| | | { |
| | | case MYSQL_TYPE_NULL: |
| | | value.reset(); |
| | | break; |
| | | case MYSQL_TYPE_BIT: |
| | | value.emplace<bool>(); |
| | | bind_field(index, std::any_cast<bool&>(value)); |
| | | break; |
| | | case MYSQL_TYPE_TINY: |
| | | value.emplace<int8_t>(); |
| | | bind_field(index, std::any_cast<int8_t&>(value)); |
| | | break; |
| | | case MYSQL_TYPE_SHORT: |
| | | value.emplace<int16_t>(); |
| | | bind_field(index, std::any_cast<int16_t&>(value)); |
| | | break; |
| | | case MYSQL_TYPE_LONG: |
| | | value.emplace<int32_t>(); |
| | | bind_field(index, std::any_cast<int32_t&>(value)); |
| | | break; |
| | | case MYSQL_TYPE_LONGLONG: |
| | | value.emplace<int64_t>(); |
| | | bind_field(index, std::any_cast<int64_t&>(value)); |
| | | break; |
| | | case MYSQL_TYPE_FLOAT: |
| | | value.emplace<float>(); |
| | | bind_field(index, std::any_cast<float&>(value)); |
| | | break; |
| | | case MYSQL_TYPE_DOUBLE: |
| | | value.emplace<double>(); |
| | | bind_field(index, std::any_cast<double&>(value)); |
| | | break; |
| | | case MYSQL_TYPE_DATE: |
| | | case MYSQL_TYPE_TIME: |
| | | case MYSQL_TYPE_DATETIME: |
| | | case MYSQL_TYPE_TIMESTAMP: |
| | | case MYSQL_TYPE_TIMESTAMP2: |
| | | case MYSQL_TYPE_DATETIME2: |
| | | case MYSQL_TYPE_TIME2: |
| | | value.emplace<qtl::mysql::time>(); |
| | | bind_field(index, std::any_cast<qtl::mysql::time&>(value)); |
| | | break; |
| | | case MYSQL_TYPE_VARCHAR: |
| | | case MYSQL_TYPE_VAR_STRING: |
| | | case MYSQL_TYPE_STRING: |
| | | case MYSQL_TYPE_ENUM: |
| | | #if LIBMYSQL_VERSION_ID >= 50700 |
| | | case MYSQL_TYPE_JSON: |
| | | #endif |
| | | case MYSQL_TYPE_DECIMAL: |
| | | case MYSQL_TYPE_NEWDECIMAL: |
| | | case MYSQL_TYPE_GEOMETRY: |
| | | value.emplace<std::string>(); |
| | | bind_field(index, qtl::bind_string(std::any_cast<std::string&>(value))); |
| | | break; |
| | | case MYSQL_TYPE_TINY_BLOB: |
| | | case MYSQL_TYPE_MEDIUM_BLOB: |
| | | case MYSQL_TYPE_BLOB: |
| | | case MYSQL_TYPE_LONG_BLOB: |
| | | value.emplace<blobbuf>(); |
| | | bind_field(index, std::forward<blobbuf>(std::any_cast<blobbuf&>(value))); |
| | | break; |
| | | default: |
| | | throw mysql::error(CR_UNSUPPORTED_PARAM_TYPE, "Unsupported field type"); |
| | | if (m_result) |
| | | { |
| | | qtl::bind_field(*this, index, *value); |
| | | binder_addin &addin = m_binderAddins[index]; |
| | | auto fetch_fun = addin.m_after_fetch; |
| | | addin.m_after_fetch = [&addin, fetch_fun, &value](const binder &b) |
| | | { |
| | | if (fetch_fun) |
| | | fetch_fun(b); |
| | | if (*b.is_null) |
| | | value.reset(); |
| | | }; |
| | | } |
| | | } |
| | | binder_addin& addin = m_binderAddins[index]; |
| | | auto fetch_fun = addin.m_after_fetch; |
| | | addin.m_after_fetch = [&addin, fetch_fun, &value](const binder& b) { |
| | | if (fetch_fun) fetch_fun(b); |
| | | if (*b.is_null) value.reset(); |
| | | }; |
| | | } |
| | | } |
| | | |
| | | inline void bind_field(size_t index, std::any &&value) |
| | | { |
| | | if (m_result) |
| | | { |
| | | MYSQL_FIELD *field = mysql_fetch_field_direct(m_result, (unsigned int)index); |
| | | if (field == nullptr) |
| | | throw_exception(); |
| | | switch (field->type) |
| | | { |
| | | case MYSQL_TYPE_NULL: |
| | | value.reset(); |
| | | break; |
| | | case MYSQL_TYPE_BIT: |
| | | value.emplace<bool>(); |
| | | bind_field(index, std::any_cast<bool &>(value)); |
| | | break; |
| | | case MYSQL_TYPE_TINY: |
| | | value.emplace<int8_t>(); |
| | | bind_field(index, std::any_cast<int8_t &>(value)); |
| | | break; |
| | | case MYSQL_TYPE_SHORT: |
| | | value.emplace<int16_t>(); |
| | | bind_field(index, std::any_cast<int16_t &>(value)); |
| | | break; |
| | | case MYSQL_TYPE_LONG: |
| | | value.emplace<int32_t>(); |
| | | bind_field(index, std::any_cast<int32_t &>(value)); |
| | | break; |
| | | case MYSQL_TYPE_LONGLONG: |
| | | value.emplace<int64_t>(); |
| | | bind_field(index, std::any_cast<int64_t &>(value)); |
| | | break; |
| | | case MYSQL_TYPE_FLOAT: |
| | | value.emplace<float>(); |
| | | bind_field(index, std::any_cast<float &>(value)); |
| | | break; |
| | | case MYSQL_TYPE_DOUBLE: |
| | | value.emplace<double>(); |
| | | bind_field(index, std::any_cast<double &>(value)); |
| | | break; |
| | | case MYSQL_TYPE_DATE: |
| | | case MYSQL_TYPE_TIME: |
| | | case MYSQL_TYPE_DATETIME: |
| | | case MYSQL_TYPE_TIMESTAMP: |
| | | case MYSQL_TYPE_TIMESTAMP2: |
| | | case MYSQL_TYPE_DATETIME2: |
| | | case MYSQL_TYPE_TIME2: |
| | | value.emplace<qtl::mysql::time>(); |
| | | bind_field(index, std::any_cast<qtl::mysql::time &>(value)); |
| | | break; |
| | | case MYSQL_TYPE_VARCHAR: |
| | | case MYSQL_TYPE_VAR_STRING: |
| | | case MYSQL_TYPE_STRING: |
| | | case MYSQL_TYPE_ENUM: |
| | | #if LIBMYSQL_VERSION_ID >= 50700 |
| | | case MYSQL_TYPE_JSON: |
| | | #endif |
| | | case MYSQL_TYPE_DECIMAL: |
| | | case MYSQL_TYPE_NEWDECIMAL: |
| | | case MYSQL_TYPE_GEOMETRY: |
| | | value.emplace<std::string>(); |
| | | bind_field(index, qtl::bind_string(std::any_cast<std::string &>(value))); |
| | | break; |
| | | case MYSQL_TYPE_TINY_BLOB: |
| | | case MYSQL_TYPE_MEDIUM_BLOB: |
| | | case MYSQL_TYPE_BLOB: |
| | | case MYSQL_TYPE_LONG_BLOB: |
| | | value.emplace<blobbuf>(); |
| | | bind_field(index, std::forward<blobbuf>(std::any_cast<blobbuf &>(value))); |
| | | break; |
| | | default: |
| | | throw mysql::error(CR_UNSUPPORTED_PARAM_TYPE, "Unsupported field type"); |
| | | } |
| | | binder_addin &addin = m_binderAddins[index]; |
| | | auto fetch_fun = addin.m_after_fetch; |
| | | addin.m_after_fetch = [&addin, fetch_fun, &value](const binder &b) |
| | | { |
| | | if (fetch_fun) |
| | | fetch_fun(b); |
| | | if (*b.is_null) |
| | | value.reset(); |
| | | }; |
| | | } |
| | | } |
| | | |
| | | #endif // C++17 |
| | | |
| | | void close() |
| | | { |
| | | if (m_result) |
| | | { |
| | | mysql_free_result(m_result); |
| | | m_result = nullptr; |
| | | } |
| | | if (m_stmt) |
| | | { |
| | | mysql_stmt_close(m_stmt); |
| | | m_stmt = nullptr; |
| | | } |
| | | } |
| | | |
| | | protected: |
| | | MYSQL_STMT* m_stmt; |
| | | MYSQL_RES* m_result; |
| | | std::vector<binder> m_binders; |
| | | |
| | | struct binder_addin |
| | | { |
| | | unsigned long m_length; |
| | | my_bool m_isNull; |
| | | my_bool m_error; |
| | | bool is_truncated; |
| | | std::function<void(binder&)> m_before_fetch; |
| | | std::function<void(const binder&)> m_after_fetch; |
| | | }; |
| | | std::vector<binder_addin> m_binderAddins; |
| | | |
| | | void resize_binders(size_t n) |
| | | { |
| | | m_binders.resize(n); |
| | | m_binderAddins.resize(n); |
| | | } |
| | | void set_binders() |
| | | { |
| | | for(size_t i=0; i!=m_binders.size(); i++) |
| | | { |
| | | m_binderAddins[i].m_length=0; |
| | | m_binders[i].length=&m_binderAddins[i].m_length; |
| | | m_binderAddins[i].m_isNull=0; |
| | | m_binders[i].is_null=&m_binderAddins[i].m_isNull; |
| | | m_binderAddins[i].m_error=0; |
| | | m_binders[i].error=&m_binderAddins[i].m_error; |
| | | } |
| | | } |
| | | |
| | | void throw_exception() const { throw mysql::error(*this); } |
| | | |
| | | template<typename Value> |
| | | struct if_null |
| | | { |
| | | explicit if_null(Value& value, Value&& def=Value()) : m_value(value), m_def(std::move(def)) { } |
| | | void operator()(const binder& b) |
| | | { |
| | | if(*b.is_null) m_value=m_def; |
| | | } |
| | | Value& m_value; |
| | | Value m_def; |
| | | }; |
| | | |
| | | }; |
| | | |
| | | class statement : public base_statement |
| | | { |
| | | public: |
| | | statement() = default; |
| | | explicit statement(basic_database& db) : base_statement(db) { } |
| | | statement(statement&& src) : base_statement(std::move(src)) { } |
| | | statement& operator=(statement&& src) |
| | | { |
| | | base_statement::operator =(std::move(src)); |
| | | return *this; |
| | | } |
| | | ~statement() |
| | | { |
| | | close(); |
| | | } |
| | | |
| | | void open(const char *query_text, unsigned long text_length=0) |
| | | { |
| | | mysql_stmt_reset(m_stmt); |
| | | if(text_length==0) text_length=(unsigned long)strlen(query_text); |
| | | if(mysql_stmt_prepare(m_stmt, query_text, text_length)!=0) |
| | | throw_exception(); |
| | | } |
| | | |
| | | void execute() |
| | | { |
| | | resize_binders(0); |
| | | if(mysql_stmt_execute(m_stmt)!=0) |
| | | throw_exception(); |
| | | } |
| | | |
| | | template<typename BindProc> |
| | | void execute_custom(BindProc&& bind_proc) |
| | | { |
| | | unsigned long count = mysql_stmt_param_count(m_stmt); |
| | | if (count > 0) |
| | | { |
| | | resize_binders(count); |
| | | bind_proc(*this); |
| | | if (mysql_stmt_bind_param(m_stmt, &m_binders.front())) |
| | | throw_exception(); |
| | | for (size_t i = 0; i != count; i++) |
| | | void close() |
| | | { |
| | | if (m_binderAddins[i].m_after_fetch) |
| | | m_binderAddins[i].m_after_fetch(m_binders[i]); |
| | | if (m_result) |
| | | { |
| | | mysql_free_result(m_result); |
| | | m_result = nullptr; |
| | | } |
| | | if (m_stmt) |
| | | { |
| | | mysql_stmt_close(m_stmt); |
| | | m_stmt = nullptr; |
| | | } |
| | | } |
| | | } |
| | | if (mysql_stmt_execute(m_stmt) != 0) |
| | | throw_exception(); |
| | | } |
| | | |
| | | template<typename Types> |
| | | void execute(const Types& params) |
| | | { |
| | | execute_custom([¶ms](statement& stmt) { |
| | | qtl::bind_params(stmt, params); |
| | | }); |
| | | } |
| | | protected: |
| | | MYSQL_STMT *m_stmt; |
| | | MYSQL_RES *m_result; |
| | | std::vector<binder> m_binders; |
| | | |
| | | template<typename Types> |
| | | bool fetch(Types&& values) |
| | | { |
| | | if(m_result==nullptr) |
| | | { |
| | | unsigned long count=mysql_stmt_field_count(m_stmt); |
| | | if(count>0) |
| | | struct binder_addin |
| | | { |
| | | m_result=mysql_stmt_result_metadata(m_stmt); |
| | | if(m_result==nullptr) throw_exception(); |
| | | resize_binders(count); |
| | | qtl::bind_record(*this, std::forward<Types>(values)); |
| | | set_binders(); |
| | | if(mysql_stmt_bind_result(m_stmt, m_binders.data())!=0) |
| | | unsigned long m_length; |
| | | my_bool m_isNull; |
| | | my_bool m_error; |
| | | bool is_truncated; |
| | | std::function<void(binder &)> m_before_fetch; |
| | | std::function<void(const binder &)> m_after_fetch; |
| | | }; |
| | | std::vector<binder_addin> m_binderAddins; |
| | | |
| | | void resize_binders(size_t n) |
| | | { |
| | | m_binders.resize(n); |
| | | m_binderAddins.resize(n); |
| | | } |
| | | void set_binders() |
| | | { |
| | | for (size_t i = 0; i != m_binders.size(); i++) |
| | | { |
| | | m_binderAddins[i].m_length = 0; |
| | | m_binders[i].length = &m_binderAddins[i].m_length; |
| | | m_binderAddins[i].m_isNull = 0; |
| | | m_binders[i].is_null = &m_binderAddins[i].m_isNull; |
| | | m_binderAddins[i].m_error = 0; |
| | | m_binders[i].error = &m_binderAddins[i].m_error; |
| | | } |
| | | } |
| | | |
| | | void throw_exception() const { throw mysql::error(*this); } |
| | | |
| | | template <typename Value> |
| | | struct if_null |
| | | { |
| | | explicit if_null(Value &value, Value &&def = Value()) : m_value(value), m_def(std::move(def)) {} |
| | | void operator()(const binder &b) |
| | | { |
| | | if (*b.is_null) |
| | | m_value = m_def; |
| | | } |
| | | Value &m_value; |
| | | Value m_def; |
| | | }; |
| | | }; |
| | | |
| | | class statement : public base_statement |
| | | { |
| | | public: |
| | | statement() = default; |
| | | explicit statement(basic_database &db) : base_statement(db) {} |
| | | statement(statement &&src) : base_statement(std::move(src)) {} |
| | | statement &operator=(statement &&src) |
| | | { |
| | | base_statement::operator=(std::move(src)); |
| | | return *this; |
| | | } |
| | | ~statement() |
| | | { |
| | | close(); |
| | | } |
| | | |
| | | void open(const char *query_text, unsigned long text_length = 0) |
| | | { |
| | | mysql_stmt_reset(m_stmt); |
| | | if (text_length == 0) |
| | | text_length = (unsigned long)strlen(query_text); |
| | | if (mysql_stmt_prepare(m_stmt, query_text, text_length) != 0) |
| | | throw_exception(); |
| | | } |
| | | } |
| | | return fetch(); |
| | | } |
| | | |
| | | bool fetch() |
| | | { |
| | | for (size_t i = 0; i != m_binders.size(); i++) |
| | | { |
| | | if (m_binderAddins[i].m_before_fetch) |
| | | m_binderAddins[i].m_before_fetch(m_binders[i]); |
| | | } |
| | | int err=mysql_stmt_fetch(m_stmt); |
| | | if(err==0 || err==MYSQL_DATA_TRUNCATED) |
| | | { |
| | | for(size_t i=0; i!=m_binders.size(); i++) |
| | | void execute() |
| | | { |
| | | m_binderAddins[i].is_truncated = (err==MYSQL_DATA_TRUNCATED); |
| | | if(m_binderAddins[i].m_after_fetch) |
| | | m_binderAddins[i].m_after_fetch(m_binders[i]); |
| | | resize_binders(0); |
| | | if (mysql_stmt_execute(m_stmt) != 0) |
| | | throw_exception(); |
| | | } |
| | | return true; |
| | | } |
| | | else if(err==1) |
| | | throw_exception(); |
| | | return false; |
| | | } |
| | | |
| | | bool next_result() |
| | | { |
| | | if(m_result) |
| | | template <typename BindProc> |
| | | void execute_custom(BindProc &&bind_proc) |
| | | { |
| | | unsigned long count = mysql_stmt_param_count(m_stmt); |
| | | if (count > 0) |
| | | { |
| | | resize_binders(count); |
| | | bind_proc(*this); |
| | | if (mysql_stmt_bind_param(m_stmt, &m_binders.front())) |
| | | throw_exception(); |
| | | for (size_t i = 0; i != count; i++) |
| | | { |
| | | if (m_binderAddins[i].m_after_fetch) |
| | | m_binderAddins[i].m_after_fetch(m_binders[i]); |
| | | } |
| | | } |
| | | if (mysql_stmt_execute(m_stmt) != 0) |
| | | throw_exception(); |
| | | } |
| | | |
| | | template <typename Types> |
| | | void execute(const Types ¶ms) |
| | | { |
| | | execute_custom([¶ms](statement &stmt) |
| | | { qtl::bind_params(stmt, params); }); |
| | | } |
| | | |
| | | template <typename Types> |
| | | bool fetch(Types &&values) |
| | | { |
| | | if (m_result == nullptr) |
| | | { |
| | | unsigned long count = mysql_stmt_field_count(m_stmt); |
| | | if (count > 0) |
| | | { |
| | | m_result = mysql_stmt_result_metadata(m_stmt); |
| | | if (m_result == nullptr) |
| | | throw_exception(); |
| | | resize_binders(count); |
| | | qtl::bind_record(*this, std::forward<Types>(values)); |
| | | set_binders(); |
| | | if (mysql_stmt_bind_result(m_stmt, m_binders.data()) != 0) |
| | | throw_exception(); |
| | | } |
| | | } |
| | | return fetch(); |
| | | } |
| | | |
| | | bool fetch() |
| | | { |
| | | for (size_t i = 0; i != m_binders.size(); i++) |
| | | { |
| | | if (m_binderAddins[i].m_before_fetch) |
| | | m_binderAddins[i].m_before_fetch(m_binders[i]); |
| | | } |
| | | int err = mysql_stmt_fetch(m_stmt); |
| | | if (err == 0 || err == MYSQL_DATA_TRUNCATED) |
| | | { |
| | | for (size_t i = 0; i != m_binders.size(); i++) |
| | | { |
| | | m_binderAddins[i].is_truncated = (err == MYSQL_DATA_TRUNCATED); |
| | | if (m_binderAddins[i].m_after_fetch) |
| | | m_binderAddins[i].m_after_fetch(m_binders[i]); |
| | | } |
| | | return true; |
| | | } |
| | | else if (err == 1) |
| | | throw_exception(); |
| | | return false; |
| | | } |
| | | |
| | | bool next_result() |
| | | { |
| | | if (m_result) |
| | | { |
| | | mysql_free_result(m_result); |
| | | m_result = nullptr; |
| | | mysql_stmt_free_result(m_stmt); |
| | | } |
| | | int ret = 0; |
| | | do |
| | | { |
| | | ret = mysql_stmt_next_result(m_stmt); |
| | | if (ret > 0) |
| | | throw_exception(); |
| | | } while (ret == 0 && mysql_stmt_field_count(m_stmt) <= 0); |
| | | return ret == 0; |
| | | } |
| | | |
| | | bool reset() { return mysql_stmt_reset(m_stmt) != 0; } |
| | | }; |
| | | |
| | | /* |
| | | struct LocalInfile |
| | | { |
| | | mysql_free_result(m_result); |
| | | m_result=nullptr; |
| | | mysql_stmt_free_result(m_stmt); |
| | | } |
| | | int ret=0; |
| | | do |
| | | int read(char *buf, unsigned int buf_len); |
| | | void close(); |
| | | int error(char *error_msg, unsigned int error_msg_len); |
| | | }; |
| | | */ |
| | | template <typename LocalInfile> |
| | | struct local_infile_factory |
| | | { |
| | | ret=mysql_stmt_next_result(m_stmt); |
| | | if(ret>0) throw_exception(); |
| | | }while(ret==0 && mysql_stmt_field_count(m_stmt)<=0); |
| | | return ret==0; |
| | | } |
| | | static int local_infile_init(void **ptr, const char *filename, void *userdata) |
| | | { |
| | | LocalInfile *object = nullptr; |
| | | try |
| | | { |
| | | object = new LocalInfile(filename); |
| | | } |
| | | catch (...) |
| | | { |
| | | return -1; |
| | | } |
| | | return ptr ? 0 : -1; |
| | | } |
| | | |
| | | bool reset() { return mysql_stmt_reset(m_stmt)!=0; } |
| | | }; |
| | | static int local_infile_read(void *ptr, char *buf, unsigned int buf_len) |
| | | { |
| | | LocalInfile *object = reinterpret_cast<LocalInfile>(ptr); |
| | | return object->read(buf, buf_len); |
| | | } |
| | | |
| | | /* |
| | | struct LocalInfile |
| | | { |
| | | int read(char *buf, unsigned int buf_len); |
| | | void close(); |
| | | int error(char *error_msg, unsigned int error_msg_len); |
| | | }; |
| | | */ |
| | | template<typename LocalInfile> |
| | | struct local_infile_factory |
| | | { |
| | | static int local_infile_init(void **ptr, const char *filename, void *userdata) |
| | | { |
| | | LocalInfile* object = nullptr; |
| | | try |
| | | static void local_infile_end(void *ptr) |
| | | { |
| | | LocalInfile *object = reinterpret_cast<LocalInfile>(ptr); |
| | | object->close(); |
| | | delete object; |
| | | } |
| | | |
| | | static int local_infile_error(void *ptr, char *error_msg, unsigned int error_msg_len) |
| | | { |
| | | LocalInfile *object = reinterpret_cast<LocalInfile>(ptr); |
| | | return object->error(error_msg, error_msg_len); |
| | | } |
| | | }; |
| | | |
| | | class local_file |
| | | { |
| | | object = new LocalInfile(filename); |
| | | } |
| | | catch (...) |
| | | public: |
| | | local_file(const char *filename) |
| | | { |
| | | m_fp = fopen(filename, "rb"); |
| | | if (m_fp == nullptr) |
| | | throw std::system_error(std::make_error_code(std::errc(errno))); |
| | | } |
| | | int read(char *buf, unsigned int buf_len) |
| | | { |
| | | return fread(buf, 1, buf_len, m_fp); |
| | | } |
| | | void close() |
| | | { |
| | | fclose(m_fp); |
| | | } |
| | | int error(char *error_msg, unsigned int error_msg_len) |
| | | { |
| | | int errcode = errno; |
| | | memset(error_msg, 0, error_msg_len); |
| | | strncpy(error_msg, strerror(errcode), error_msg_len - 1); |
| | | return errcode; |
| | | } |
| | | |
| | | private: |
| | | FILE *m_fp; |
| | | }; |
| | | |
| | | class basic_database |
| | | { |
| | | return -1; |
| | | } |
| | | return ptr ? 0 : -1; |
| | | } |
| | | protected: |
| | | basic_database() |
| | | { |
| | | m_mysql = mysql_init(nullptr); |
| | | } |
| | | |
| | | static int local_infile_read(void *ptr, char *buf, unsigned int buf_len) |
| | | { |
| | | LocalInfile* object = reinterpret_cast<LocalInfile>(ptr); |
| | | return object->read(buf, buf_len); |
| | | } |
| | | public: |
| | | typedef mysql::error exception_type; |
| | | |
| | | static void local_infile_end(void *ptr) |
| | | { |
| | | LocalInfile* object = reinterpret_cast<LocalInfile>(ptr); |
| | | object->close(); |
| | | delete object; |
| | | } |
| | | ~basic_database() |
| | | { |
| | | if (m_mysql) |
| | | mysql_close(m_mysql); |
| | | } |
| | | basic_database(const basic_database &) = delete; |
| | | basic_database(basic_database &&src) |
| | | { |
| | | m_mysql = src.m_mysql; |
| | | src.m_mysql = nullptr; |
| | | } |
| | | basic_database &operator==(const basic_database &) = delete; |
| | | basic_database &operator==(basic_database &&src) |
| | | { |
| | | if (this != &src) |
| | | { |
| | | if (m_mysql) |
| | | mysql_close(m_mysql); |
| | | m_mysql = src.m_mysql; |
| | | src.m_mysql = nullptr; |
| | | } |
| | | return *this; |
| | | } |
| | | |
| | | static int local_infile_error(void *ptr, char *error_msg, unsigned int error_msg_len) |
| | | { |
| | | LocalInfile* object = reinterpret_cast<LocalInfile>(ptr); |
| | | return object->error(error_msg, error_msg_len); |
| | | } |
| | | }; |
| | | MYSQL *handle() { return m_mysql; } |
| | | |
| | | class local_file |
| | | { |
| | | public: |
| | | local_file(const char* filename) |
| | | { |
| | | m_fp = fopen(filename, "rb"); |
| | | if (m_fp == nullptr) |
| | | throw std::system_error(std::make_error_code(std::errc(errno))); |
| | | } |
| | | int read(char *buf, unsigned int buf_len) |
| | | { |
| | | return fread(buf, 1, buf_len, m_fp); |
| | | } |
| | | void close() |
| | | { |
| | | fclose(m_fp); |
| | | } |
| | | int error(char *error_msg, unsigned int error_msg_len) |
| | | { |
| | | int errcode = errno; |
| | | memset(error_msg, 0, error_msg_len); |
| | | strncpy(error_msg, strerror(errcode), error_msg_len - 1); |
| | | return errcode; |
| | | } |
| | | void options(enum mysql_option option, const void *arg) |
| | | { |
| | | if (mysql_options(m_mysql, option, arg) != 0) |
| | | throw_exception(); |
| | | } |
| | | void charset_name(const char *charset) |
| | | { |
| | | if (mysql_set_character_set(m_mysql, charset) != 0) |
| | | throw_exception(); |
| | | } |
| | | void protocol(mysql_protocol_type type) |
| | | { |
| | | return options(MYSQL_OPT_PROTOCOL, &type); |
| | | } |
| | | void reconnect(my_bool re) |
| | | { |
| | | return options(MYSQL_OPT_RECONNECT, &re); |
| | | } |
| | | |
| | | private: |
| | | FILE* m_fp; |
| | | }; |
| | | const char *current() const |
| | | { |
| | | return m_mysql->db; |
| | | } |
| | | |
| | | class basic_database |
| | | { |
| | | protected: |
| | | basic_database() |
| | | { |
| | | m_mysql = mysql_init(nullptr); |
| | | } |
| | | unsigned int error() const |
| | | { |
| | | return mysql_errno(m_mysql); |
| | | } |
| | | const char *errmsg() const |
| | | { |
| | | return mysql_error(m_mysql); |
| | | } |
| | | |
| | | public: |
| | | typedef mysql::error exception_type; |
| | | uint64_t affected_rows() |
| | | { |
| | | return mysql_affected_rows(m_mysql); |
| | | } |
| | | |
| | | ~basic_database() |
| | | { |
| | | if(m_mysql) |
| | | mysql_close(m_mysql); |
| | | } |
| | | basic_database(const basic_database&) = delete; |
| | | basic_database(basic_database&& src) |
| | | { |
| | | m_mysql=src.m_mysql; |
| | | src.m_mysql=nullptr; |
| | | } |
| | | basic_database& operator==(const basic_database&) = delete; |
| | | basic_database& operator==(basic_database&& src) |
| | | { |
| | | if(this!=&src) |
| | | unsigned int field_count() |
| | | { |
| | | return mysql_field_count(m_mysql); |
| | | } |
| | | |
| | | uint64_t insert_id() |
| | | { |
| | | return mysql_insert_id(m_mysql); |
| | | } |
| | | |
| | | protected: |
| | | MYSQL *m_mysql; |
| | | void throw_exception() { throw mysql::error(*this); } |
| | | }; |
| | | |
| | | #if MARIADB_VERSION_ID >= 050500 |
| | | |
| | | class async_connection; |
| | | |
| | | #endif // MariaDB |
| | | |
| | | class database : public basic_database, public qtl::base_database<database, statement> |
| | | { |
| | | if(m_mysql) |
| | | public: |
| | | database() = default; |
| | | |
| | | bool open(const char *host, const char *user, const char *passwd, const char *db, |
| | | unsigned long clientflag = 0, unsigned int port = 0, const char *unix_socket = nullptr) |
| | | { |
| | | if (m_mysql == nullptr) |
| | | m_mysql = mysql_init(nullptr); |
| | | return mysql_real_connect(m_mysql, host, user, passwd, db, port, unix_socket, clientflag) != nullptr; |
| | | } |
| | | void close() |
| | | { |
| | | mysql_close(m_mysql); |
| | | m_mysql=src.m_mysql; |
| | | src.m_mysql=nullptr; |
| | | } |
| | | return *this; |
| | | } |
| | | |
| | | MYSQL* handle() { return m_mysql; } |
| | | |
| | | void options(enum mysql_option option, const void *arg) |
| | | { |
| | | if(mysql_options(m_mysql, option, arg)!=0) |
| | | throw_exception(); |
| | | } |
| | | void charset_name(const char* charset) |
| | | { |
| | | if(mysql_set_character_set(m_mysql, charset)!=0) |
| | | throw_exception(); |
| | | } |
| | | void protocol(mysql_protocol_type type) |
| | | { |
| | | return options(MYSQL_OPT_PROTOCOL, &type); |
| | | } |
| | | void reconnect(my_bool re) |
| | | { |
| | | return options(MYSQL_OPT_RECONNECT, &re); |
| | | } |
| | | |
| | | const char* current() const |
| | | { |
| | | return m_mysql->db; |
| | | } |
| | | |
| | | unsigned int error() const |
| | | { |
| | | return mysql_errno(m_mysql); |
| | | } |
| | | const char* errmsg() const |
| | | { |
| | | return mysql_error(m_mysql); |
| | | } |
| | | |
| | | uint64_t affected_rows() |
| | | { |
| | | return mysql_affected_rows(m_mysql); |
| | | } |
| | | |
| | | unsigned int field_count() |
| | | { |
| | | return mysql_field_count(m_mysql); |
| | | } |
| | | |
| | | uint64_t insert_id() |
| | | { |
| | | return mysql_insert_id(m_mysql); |
| | | } |
| | | |
| | | protected: |
| | | MYSQL* m_mysql; |
| | | void throw_exception() { throw mysql::error(*this); } |
| | | }; |
| | | |
| | | #if MARIADB_VERSION_ID >= 050500 |
| | | |
| | | class async_connection; |
| | | |
| | | #endif //MariaDB |
| | | |
| | | class database : public basic_database, public qtl::base_database<database, statement> |
| | | { |
| | | public: |
| | | database() = default; |
| | | |
| | | bool open(const char *host, const char *user, const char *passwd, const char *db, |
| | | unsigned long clientflag = 0, unsigned int port = 0, const char *unix_socket = nullptr) |
| | | { |
| | | if (m_mysql == nullptr) m_mysql = mysql_init(nullptr); |
| | | return mysql_real_connect(m_mysql, host, user, passwd, db, port, unix_socket, clientflag) != nullptr; |
| | | } |
| | | void close() |
| | | { |
| | | mysql_close(m_mysql); |
| | | m_mysql = nullptr; |
| | | } |
| | | |
| | | statement open_command(const char* query_text, size_t text_length) |
| | | { |
| | | statement stmt(*this); |
| | | stmt.open(query_text, text_length); |
| | | return stmt; |
| | | } |
| | | statement open_command(const char* query_text) |
| | | { |
| | | return open_command(query_text, strlen(query_text)); |
| | | } |
| | | statement open_command(const std::string& query_text) |
| | | { |
| | | return open_command(query_text.data(), query_text.length()); |
| | | } |
| | | |
| | | void refresh(unsigned int options) |
| | | { |
| | | if (mysql_refresh(m_mysql, options) < 0) |
| | | throw_exception(); |
| | | } |
| | | |
| | | void select(const char* db) |
| | | { |
| | | if (mysql_select_db(m_mysql, db) != 0) |
| | | throw_exception(); |
| | | } |
| | | |
| | | void simple_execute(const char* query_text, uint64_t* paffected = nullptr) |
| | | { |
| | | if (mysql_query(m_mysql, query_text) != 0) |
| | | throw_exception(); |
| | | if (paffected) *paffected = affected_rows(); |
| | | } |
| | | void simple_execute(const char* query_text, unsigned long text_length, uint64_t* paffected = nullptr) |
| | | { |
| | | if (text_length == 0) text_length = (unsigned long)strlen(query_text); |
| | | if (mysql_real_query(m_mysql, query_text, text_length) != 0) |
| | | throw_exception(); |
| | | if (paffected) *paffected = affected_rows(); |
| | | } |
| | | |
| | | void auto_commit(bool on) |
| | | { |
| | | if (mysql_autocommit(m_mysql, on ? 1 : 0) != 0) |
| | | throw_exception(); |
| | | } |
| | | void begin_transaction() |
| | | { |
| | | auto_commit(false); |
| | | } |
| | | void rollback() |
| | | { |
| | | if (mysql_rollback(m_mysql) != 0) |
| | | throw_exception(); |
| | | auto_commit(true); |
| | | } |
| | | void commit() |
| | | { |
| | | if (mysql_commit(m_mysql) != 0) |
| | | throw_exception(); |
| | | auto_commit(true); |
| | | } |
| | | |
| | | bool is_alive() |
| | | { |
| | | return mysql_ping(m_mysql) == 0; |
| | | } |
| | | |
| | | template<typename Pred> |
| | | bool simple_query(const char* query, unsigned long length, Pred&& pred) |
| | | { |
| | | simple_execute(query, length); |
| | | |
| | | unsigned int fieldCount = mysql_field_count(m_mysql); |
| | | MYSQL_RES* result = mysql_store_result(m_mysql); |
| | | if (fieldCount > 0 && result) |
| | | { |
| | | MYSQL_RES* result = mysql_store_result(m_mysql); |
| | | MYSQL_ROW row; |
| | | while (row = mysql_fetch_row(result)) |
| | | { |
| | | if (!pred(*this, row, fieldCount)) |
| | | break; |
| | | m_mysql = nullptr; |
| | | } |
| | | mysql_free_result(result); |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | template<typename LocalInfile> |
| | | void set_local_infile_factory(local_infile_factory<LocalInfile>* factory) |
| | | { |
| | | typedef local_infile_factory<LocalInfile> factory_type; |
| | | if (factory == nullptr) |
| | | { |
| | | reset_local_infile(); |
| | | } |
| | | else |
| | | { |
| | | mysql_set_local_infile_handler(m_mysql, &factory_type::local_infile_init, |
| | | &factory_type::local_infile_read, |
| | | &factory_type::local_infile_end, |
| | | &factory_type::local_infile_error, factory); |
| | | } |
| | | } |
| | | void reset_local_infile() |
| | | { |
| | | mysql_set_local_infile_default(m_mysql); |
| | | } |
| | | statement open_command(const char *query_text, size_t text_length) |
| | | { |
| | | statement stmt(*this); |
| | | stmt.open(query_text, text_length); |
| | | return stmt; |
| | | } |
| | | statement open_command(const char *query_text) |
| | | { |
| | | return open_command(query_text, strlen(query_text)); |
| | | } |
| | | statement open_command(const std::string &query_text) |
| | | { |
| | | return open_command(query_text.data(), query_text.length()); |
| | | } |
| | | |
| | | void refresh(unsigned int options) |
| | | { |
| | | if (mysql_refresh(m_mysql, options) < 0) |
| | | throw_exception(); |
| | | } |
| | | |
| | | void select(const char *db) |
| | | { |
| | | if (mysql_select_db(m_mysql, db) != 0) |
| | | throw_exception(); |
| | | } |
| | | |
| | | void simple_execute(const char *query_text, uint64_t *paffected = nullptr) |
| | | { |
| | | if (mysql_query(m_mysql, query_text) != 0) |
| | | throw_exception(); |
| | | if (paffected) |
| | | *paffected = affected_rows(); |
| | | } |
| | | void simple_execute(const char *query_text, unsigned long text_length, uint64_t *paffected = nullptr) |
| | | { |
| | | if (text_length == 0) |
| | | text_length = (unsigned long)strlen(query_text); |
| | | if (mysql_real_query(m_mysql, query_text, text_length) != 0) |
| | | throw_exception(); |
| | | if (paffected) |
| | | *paffected = affected_rows(); |
| | | } |
| | | |
| | | void auto_commit(bool on) |
| | | { |
| | | if (mysql_autocommit(m_mysql, on ? 1 : 0) != 0) |
| | | throw_exception(); |
| | | } |
| | | void begin_transaction() |
| | | { |
| | | auto_commit(false); |
| | | } |
| | | void rollback() |
| | | { |
| | | if (mysql_rollback(m_mysql) != 0) |
| | | throw_exception(); |
| | | auto_commit(true); |
| | | } |
| | | void commit() |
| | | { |
| | | if (mysql_commit(m_mysql) != 0) |
| | | throw_exception(); |
| | | auto_commit(true); |
| | | } |
| | | |
| | | bool is_alive() |
| | | { |
| | | return mysql_ping(m_mysql) == 0; |
| | | } |
| | | |
| | | template <typename Pred> |
| | | bool simple_query(const char *query, unsigned long length, Pred &&pred) |
| | | { |
| | | simple_execute(query, length); |
| | | |
| | | unsigned int fieldCount = mysql_field_count(m_mysql); |
| | | MYSQL_RES *result = mysql_store_result(m_mysql); |
| | | if (fieldCount > 0 && result) |
| | | { |
| | | MYSQL_RES *result = mysql_store_result(m_mysql); |
| | | MYSQL_ROW row; |
| | | while (row = mysql_fetch_row(result)) |
| | | { |
| | | if (!pred(*this, row, fieldCount)) |
| | | break; |
| | | } |
| | | mysql_free_result(result); |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | template <typename LocalInfile> |
| | | void set_local_infile_factory(local_infile_factory<LocalInfile> *factory) |
| | | { |
| | | typedef local_infile_factory<LocalInfile> factory_type; |
| | | if (factory == nullptr) |
| | | { |
| | | reset_local_infile(); |
| | | } |
| | | else |
| | | { |
| | | mysql_set_local_infile_handler(m_mysql, &factory_type::local_infile_init, |
| | | &factory_type::local_infile_read, |
| | | &factory_type::local_infile_end, |
| | | &factory_type::local_infile_error, factory); |
| | | } |
| | | } |
| | | void reset_local_infile() |
| | | { |
| | | mysql_set_local_infile_default(m_mysql); |
| | | } |
| | | |
| | | #if MARIADB_VERSION_ID >= 050500 |
| | | |
| | | async_connection async_mode(); |
| | | async_connection async_mode(); |
| | | |
| | | #endif //MariaDB |
| | | |
| | | }; |
| | | #endif // MariaDB |
| | | }; |
| | | |
| | | #if MARIADB_VERSION_ID >= 100000 |
| | | |
| | | inline int event_flags(int status) NOEXCEPT |
| | | { |
| | | int flags = 0; |
| | | if (status&MYSQL_WAIT_READ) |
| | | flags |= event::ef_read; |
| | | if (status&MYSQL_WAIT_WRITE) |
| | | flags |= event::ef_write; |
| | | if (status&MYSQL_WAIT_EXCEPT) |
| | | flags |= event::ef_exception; |
| | | return flags; |
| | | } |
| | | |
| | | inline int mysql_status(int flags) NOEXCEPT |
| | | { |
| | | int status = 0; |
| | | if (flags&event::ef_read) |
| | | status |= MYSQL_WAIT_READ; |
| | | if (flags&event::ef_write) |
| | | status |= MYSQL_WAIT_WRITE; |
| | | if (flags&event::ef_exception) |
| | | status |= MYSQL_WAIT_EXCEPT; |
| | | if (flags&event::ef_timeout) |
| | | status |= MYSQL_WAIT_TIMEOUT; |
| | | return status; |
| | | } |
| | | |
| | | class async_connection; |
| | | |
| | | class async_statement : public base_statement |
| | | { |
| | | public: |
| | | async_statement() = default; |
| | | async_statement(async_connection& db); |
| | | async_statement(async_statement&& src) |
| | | : base_statement(std::move(src)) |
| | | { |
| | | m_event=src.m_event; |
| | | src.m_event=nullptr; |
| | | } |
| | | async_statement& operator=(async_statement&& src) |
| | | { |
| | | base_statement::operator =(std::move(src)); |
| | | m_event=src.m_event; |
| | | src.m_event=nullptr; |
| | | return *this; |
| | | } |
| | | ~async_statement() |
| | | { |
| | | close(); |
| | | } |
| | | |
| | | /* |
| | | Handler defiens as: |
| | | void handler(const qtl::mysql::error& e); |
| | | */ |
| | | template<typename Handler> |
| | | void open(Handler&& handler, const char *query_text, size_t text_length=0) |
| | | { |
| | | if(text_length==0) text_length=strlen(query_text); |
| | | if(m_stmt) |
| | | inline int event_flags(int status) NOEXCEPT |
| | | { |
| | | std::string text(query_text, text_length); |
| | | reset([this, text, handler](const mysql::error& e) mutable { |
| | | int flags = 0; |
| | | if (status & MYSQL_WAIT_READ) |
| | | flags |= event::ef_read; |
| | | if (status & MYSQL_WAIT_WRITE) |
| | | flags |= event::ef_write; |
| | | if (status & MYSQL_WAIT_EXCEPT) |
| | | flags |= event::ef_exception; |
| | | return flags; |
| | | } |
| | | |
| | | inline int mysql_status(int flags) NOEXCEPT |
| | | { |
| | | int status = 0; |
| | | if (flags & event::ef_read) |
| | | status |= MYSQL_WAIT_READ; |
| | | if (flags & event::ef_write) |
| | | status |= MYSQL_WAIT_WRITE; |
| | | if (flags & event::ef_exception) |
| | | status |= MYSQL_WAIT_EXCEPT; |
| | | if (flags & event::ef_timeout) |
| | | status |= MYSQL_WAIT_TIMEOUT; |
| | | return status; |
| | | } |
| | | |
| | | class async_connection; |
| | | |
| | | class async_statement : public base_statement |
| | | { |
| | | public: |
| | | async_statement() = default; |
| | | async_statement(async_connection &db); |
| | | async_statement(async_statement &&src) |
| | | : base_statement(std::move(src)) |
| | | { |
| | | m_event = src.m_event; |
| | | src.m_event = nullptr; |
| | | } |
| | | async_statement &operator=(async_statement &&src) |
| | | { |
| | | base_statement::operator=(std::move(src)); |
| | | m_event = src.m_event; |
| | | src.m_event = nullptr; |
| | | return *this; |
| | | } |
| | | ~async_statement() |
| | | { |
| | | close(); |
| | | } |
| | | |
| | | /* |
| | | Handler defiens as: |
| | | void handler(const qtl::mysql::error& e); |
| | | */ |
| | | template <typename Handler> |
| | | void open(Handler &&handler, const char *query_text, size_t text_length = 0) |
| | | { |
| | | if (text_length == 0) |
| | | text_length = strlen(query_text); |
| | | if (m_stmt) |
| | | { |
| | | std::string text(query_text, text_length); |
| | | reset([this, text, handler](const mysql::error &e) mutable |
| | | { |
| | | if(e) |
| | | { |
| | | handler(e); |
| | | return; |
| | | } |
| | | |
| | | prepare(text.data(), text.size(), std::forward<Handler>(handler)); |
| | | }); |
| | | } |
| | | else |
| | | { |
| | | prepare(query_text, text_length, std::forward<Handler>(handler)); |
| | | } |
| | | } |
| | | |
| | | /* |
| | | ExecuteHandler defiens as: |
| | | void handler(const qtl::mysql::error& e, uint64_t affected); |
| | | */ |
| | | template<typename ExecuteHandler> |
| | | void execute(ExecuteHandler&& handler) |
| | | { |
| | | resize_binders(0); |
| | | int ret=0; |
| | | int status = mysql_stmt_execute_start(&ret, m_stmt); |
| | | if(status) |
| | | wait_execute(status, std::forward<ExecuteHandler>(handler)); |
| | | else if(ret) |
| | | handler(mysql::error(*this), 0); |
| | | else |
| | | handler(mysql::error(), affetced_rows()); |
| | | } |
| | | |
| | | template<typename Types, typename Handler> |
| | | void execute(const Types& params, Handler&& handler) |
| | | { |
| | | unsigned long count=mysql_stmt_param_count(m_stmt); |
| | | if(count>0) |
| | | { |
| | | resize_binders(count); |
| | | qtl::bind_params(*this, params); |
| | | if(mysql_stmt_bind_param(m_stmt, &m_binders.front())) |
| | | throw_exception(); |
| | | for(size_t i=0; i!=count; i++) |
| | | { |
| | | if(m_binderAddins[i].m_after_fetch) |
| | | m_binderAddins[i].m_after_fetch(m_binders[i]); |
| | | } |
| | | } |
| | | execute(std::forward<Handler>(handler)); |
| | | } |
| | | |
| | | template<typename Types, typename RowHandler, typename FinishHandler> |
| | | void fetch(Types&& values, RowHandler&& row_handler, FinishHandler&& finish_handler) |
| | | { |
| | | if(m_result==nullptr) |
| | | { |
| | | unsigned long count=mysql_stmt_field_count(m_stmt); |
| | | if(count>0) |
| | | { |
| | | m_result=mysql_stmt_result_metadata(m_stmt); |
| | | if(m_result==nullptr) throw_exception(); |
| | | resize_binders(count); |
| | | qtl::bind_record(*this, std::forward<Types>(values)); |
| | | set_binders(); |
| | | if(mysql_stmt_bind_result(m_stmt, m_binders.data())!=0) |
| | | prepare(text.data(), text.size(), std::forward<Handler>(handler)); }); |
| | | } |
| | | else |
| | | { |
| | | finish_handler(mysql::error(*this)); |
| | | return; |
| | | prepare(query_text, text_length, std::forward<Handler>(handler)); |
| | | } |
| | | } |
| | | } |
| | | fetch(std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler)); |
| | | } |
| | | |
| | | template<typename RowHandler, typename FinishHandler> |
| | | void fetch(RowHandler&& row_handler, FinishHandler&& finish_handler) |
| | | { |
| | | int ret = 0; |
| | | int status = start_fetch(&ret); |
| | | if (status == 0) |
| | | status = after_fetch(status, ret, std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler)); |
| | | if (status) |
| | | wait_fetch(status, std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler)); |
| | | } |
| | | |
| | | template<typename Handler> |
| | | void reset(Handler&& handler) |
| | | { |
| | | my_bool ret=false; |
| | | int status = mysql_stmt_reset_start(&ret, m_stmt); |
| | | if(status) |
| | | wait_operation<my_bool>(status, &mysql_stmt_reset_cont, handler); |
| | | else |
| | | handler( (ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | |
| | | void close() |
| | | { |
| | | base_statement::close(); |
| | | } |
| | | |
| | | template<typename Handler> |
| | | void close(Handler&& handler) |
| | | { |
| | | if(m_result) |
| | | { |
| | | my_bool ret=0; |
| | | int status = mysql_stmt_free_result_start(&ret, m_stmt); |
| | | if(status) |
| | | /* |
| | | ExecuteHandler defiens as: |
| | | void handler(const qtl::mysql::error& e, uint64_t affected); |
| | | */ |
| | | template <typename ExecuteHandler> |
| | | void execute(ExecuteHandler &&handler) |
| | | { |
| | | this->wait_free_result(status, [this, handler](const mysql::error& e) mutable { |
| | | m_result=nullptr; |
| | | if (e) |
| | | handler(e); |
| | | else |
| | | close(handler); |
| | | }); |
| | | } |
| | | else if(ret) |
| | | handler(mysql::error(*this)); |
| | | else |
| | | m_result=nullptr; |
| | | } |
| | | if(m_stmt) |
| | | { |
| | | my_bool ret=0; |
| | | int status = mysql_stmt_close_start(&ret, m_stmt); |
| | | if(status) |
| | | { |
| | | wait_operation<my_bool>(status, &mysql_stmt_close_cont, [this, handler](const mysql::error& e) mutable { |
| | | m_stmt=nullptr; |
| | | handler(e); |
| | | });; |
| | | } |
| | | else |
| | | { |
| | | m_stmt = nullptr; |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | template<typename Handler> |
| | | void next_result(Handler&& handler) |
| | | { |
| | | if(m_result) |
| | | { |
| | | mysql_free_result(m_result); |
| | | m_result = nullptr; |
| | | } |
| | | int ret=0; |
| | | do |
| | | { |
| | | int status = mysql_stmt_next_result_start(&ret, m_stmt); |
| | | if (status) |
| | | { |
| | | wait_next_result(status, std::forward<Handler>(handler)); |
| | | return; |
| | | } |
| | | }while(ret==0 && mysql_stmt_field_count(m_stmt)<=0); |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | |
| | | |
| | | private: |
| | | event* m_event; |
| | | |
| | | template<typename Handler> |
| | | void prepare(const char *query_text, size_t text_length, Handler&& handler) |
| | | { |
| | | int ret; |
| | | int status=mysql_stmt_prepare_start(&ret, m_stmt, query_text, (unsigned long)text_length); |
| | | if(status) |
| | | wait_operation<int>(status, &mysql_stmt_prepare_cont, handler); |
| | | else |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | |
| | | template<typename Ret, typename Func, typename Handler> |
| | | void wait_operation(int status, Func func, Handler&& handler) |
| | | { |
| | | m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql), |
| | | [this, func, handler](int flags) mutable { |
| | | Ret ret = 0; |
| | | int status = func(&ret, m_stmt, mysql_status(flags)); |
| | | if (status) |
| | | wait_operation<Ret>(status, func, handler); |
| | | else |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | }); |
| | | } |
| | | |
| | | template<typename ExecuteHandler> |
| | | void wait_execute(int status, ExecuteHandler&& handler) |
| | | { |
| | | m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql), |
| | | [this, handler](int flags) mutable { |
| | | resize_binders(0); |
| | | int ret = 0; |
| | | int status = mysql_stmt_execute_cont(&ret, m_stmt, mysql_status(flags)); |
| | | int status = mysql_stmt_execute_start(&ret, m_stmt); |
| | | if (status) |
| | | wait_execute(status, handler); |
| | | else if(ret) |
| | | wait_execute(status, std::forward<ExecuteHandler>(handler)); |
| | | else if (ret) |
| | | handler(mysql::error(*this), 0); |
| | | else |
| | | handler(mysql::error(), affetced_rows()); |
| | | }); |
| | | } |
| | | } |
| | | |
| | | template<typename RowHandler, typename FinishHandler> |
| | | void wait_fetch(int status, RowHandler&& row_handler, FinishHandler&& finish_handler) |
| | | { |
| | | m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql), |
| | | [this, row_handler, finish_handler](int flags) mutable { |
| | | template <typename Types, typename Handler> |
| | | void execute(const Types ¶ms, Handler &&handler) |
| | | { |
| | | unsigned long count = mysql_stmt_param_count(m_stmt); |
| | | if (count > 0) |
| | | { |
| | | resize_binders(count); |
| | | qtl::bind_params(*this, params); |
| | | if (mysql_stmt_bind_param(m_stmt, &m_binders.front())) |
| | | throw_exception(); |
| | | for (size_t i = 0; i != count; i++) |
| | | { |
| | | if (m_binderAddins[i].m_after_fetch) |
| | | m_binderAddins[i].m_after_fetch(m_binders[i]); |
| | | } |
| | | } |
| | | execute(std::forward<Handler>(handler)); |
| | | } |
| | | |
| | | template <typename Types, typename RowHandler, typename FinishHandler> |
| | | void fetch(Types &&values, RowHandler &&row_handler, FinishHandler &&finish_handler) |
| | | { |
| | | if (m_result == nullptr) |
| | | { |
| | | unsigned long count = mysql_stmt_field_count(m_stmt); |
| | | if (count > 0) |
| | | { |
| | | m_result = mysql_stmt_result_metadata(m_stmt); |
| | | if (m_result == nullptr) |
| | | throw_exception(); |
| | | resize_binders(count); |
| | | qtl::bind_record(*this, std::forward<Types>(values)); |
| | | set_binders(); |
| | | if (mysql_stmt_bind_result(m_stmt, m_binders.data()) != 0) |
| | | { |
| | | finish_handler(mysql::error(*this)); |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | fetch(std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler)); |
| | | } |
| | | |
| | | template <typename RowHandler, typename FinishHandler> |
| | | void fetch(RowHandler &&row_handler, FinishHandler &&finish_handler) |
| | | { |
| | | int ret = 0; |
| | | int status = mysql_stmt_fetch_cont(&ret, m_stmt, mysql_status(flags)); |
| | | int status = start_fetch(&ret); |
| | | if (status == 0) |
| | | status = after_fetch(status, ret, std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler)); |
| | | if (status) |
| | | wait_fetch(status, std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler)); |
| | | }); |
| | | } |
| | | } |
| | | |
| | | int start_fetch(int* ret) |
| | | { |
| | | for (size_t i = 0; i != m_binders.size(); i++) |
| | | { |
| | | if (m_binderAddins[i].m_before_fetch) |
| | | m_binderAddins[i].m_before_fetch(m_binders[i]); |
| | | } |
| | | return mysql_stmt_fetch_start(ret, m_stmt); |
| | | } |
| | | template <typename Handler> |
| | | void reset(Handler &&handler) |
| | | { |
| | | my_bool ret = false; |
| | | int status = mysql_stmt_reset_start(&ret, m_stmt); |
| | | if (status) |
| | | wait_operation<my_bool>(status, &mysql_stmt_reset_cont, handler); |
| | | else |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | |
| | | template<typename RowHandler, typename FinishHandler> |
| | | int after_fetch(int status, int ret, RowHandler&& row_handler, FinishHandler&& finish_handler) |
| | | { |
| | | while (status == 0) |
| | | { |
| | | if (ret == 0 || ret == MYSQL_DATA_TRUNCATED) |
| | | void close() |
| | | { |
| | | base_statement::close(); |
| | | } |
| | | |
| | | template <typename Handler> |
| | | void close(Handler &&handler) |
| | | { |
| | | if (m_result) |
| | | { |
| | | my_bool ret = 0; |
| | | int status = mysql_stmt_free_result_start(&ret, m_stmt); |
| | | if (status) |
| | | { |
| | | this->wait_free_result(status, [this, handler](const mysql::error &e) mutable |
| | | { |
| | | m_result=nullptr; |
| | | if (e) |
| | | handler(e); |
| | | else |
| | | close(handler); }); |
| | | } |
| | | else if (ret) |
| | | handler(mysql::error(*this)); |
| | | else |
| | | m_result = nullptr; |
| | | } |
| | | if (m_stmt) |
| | | { |
| | | my_bool ret = 0; |
| | | int status = mysql_stmt_close_start(&ret, m_stmt); |
| | | if (status) |
| | | { |
| | | wait_operation<my_bool>(status, &mysql_stmt_close_cont, [this, handler](const mysql::error &e) mutable |
| | | { |
| | | m_stmt=nullptr; |
| | | handler(e); }); |
| | | ; |
| | | } |
| | | else |
| | | { |
| | | m_stmt = nullptr; |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | template <typename Handler> |
| | | void next_result(Handler &&handler) |
| | | { |
| | | if (m_result) |
| | | { |
| | | mysql_free_result(m_result); |
| | | m_result = nullptr; |
| | | } |
| | | int ret = 0; |
| | | do |
| | | { |
| | | int status = mysql_stmt_next_result_start(&ret, m_stmt); |
| | | if (status) |
| | | { |
| | | wait_next_result(status, std::forward<Handler>(handler)); |
| | | return; |
| | | } |
| | | } while (ret == 0 && mysql_stmt_field_count(m_stmt) <= 0); |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | |
| | | private: |
| | | event *m_event; |
| | | |
| | | template <typename Handler> |
| | | void prepare(const char *query_text, size_t text_length, Handler &&handler) |
| | | { |
| | | int ret; |
| | | int status = mysql_stmt_prepare_start(&ret, m_stmt, query_text, (unsigned long)text_length); |
| | | if (status) |
| | | wait_operation<int>(status, &mysql_stmt_prepare_cont, handler); |
| | | else |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | |
| | | template <typename Ret, typename Func, typename Handler> |
| | | void wait_operation(int status, Func func, Handler &&handler) |
| | | { |
| | | m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql), |
| | | [this, func, handler](int flags) mutable |
| | | { |
| | | Ret ret = 0; |
| | | int status = func(&ret, m_stmt, mysql_status(flags)); |
| | | if (status) |
| | | wait_operation<Ret>(status, func, handler); |
| | | else |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | }); |
| | | } |
| | | |
| | | template <typename ExecuteHandler> |
| | | void wait_execute(int status, ExecuteHandler &&handler) |
| | | { |
| | | m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql), |
| | | [this, handler](int flags) mutable |
| | | { |
| | | int ret = 0; |
| | | int status = mysql_stmt_execute_cont(&ret, m_stmt, mysql_status(flags)); |
| | | if (status) |
| | | wait_execute(status, handler); |
| | | else if (ret) |
| | | handler(mysql::error(*this), 0); |
| | | else |
| | | handler(mysql::error(), affetced_rows()); |
| | | }); |
| | | } |
| | | |
| | | template <typename RowHandler, typename FinishHandler> |
| | | void wait_fetch(int status, RowHandler &&row_handler, FinishHandler &&finish_handler) |
| | | { |
| | | m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql), |
| | | [this, row_handler, finish_handler](int flags) mutable |
| | | { |
| | | int ret = 0; |
| | | int status = mysql_stmt_fetch_cont(&ret, m_stmt, mysql_status(flags)); |
| | | if (status == 0) |
| | | status = after_fetch(status, ret, std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler)); |
| | | if (status) |
| | | wait_fetch(status, std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler)); |
| | | }); |
| | | } |
| | | |
| | | int start_fetch(int *ret) |
| | | { |
| | | for (size_t i = 0; i != m_binders.size(); i++) |
| | | { |
| | | m_binderAddins[i].is_truncated = (ret == MYSQL_DATA_TRUNCATED); |
| | | if (m_binderAddins[i].m_after_fetch) |
| | | m_binderAddins[i].m_after_fetch(m_binders[i]); |
| | | if (m_binderAddins[i].m_before_fetch) |
| | | m_binderAddins[i].m_before_fetch(m_binders[i]); |
| | | } |
| | | if (row_handler()) |
| | | status = start_fetch(&ret); |
| | | else |
| | | return mysql_stmt_fetch_start(ret, m_stmt); |
| | | } |
| | | |
| | | template <typename RowHandler, typename FinishHandler> |
| | | int after_fetch(int status, int ret, RowHandler &&row_handler, FinishHandler &&finish_handler) |
| | | { |
| | | while (status == 0) |
| | | { |
| | | finish_handler(mysql::error()); |
| | | break; |
| | | } |
| | | if (ret == 0 || ret == MYSQL_DATA_TRUNCATED) |
| | | { |
| | | for (size_t i = 0; i != m_binders.size(); i++) |
| | | { |
| | | m_binderAddins[i].is_truncated = (ret == MYSQL_DATA_TRUNCATED); |
| | | if (m_binderAddins[i].m_after_fetch) |
| | | m_binderAddins[i].m_after_fetch(m_binders[i]); |
| | | } |
| | | if (row_handler()) |
| | | status = start_fetch(&ret); |
| | | else |
| | | { |
| | | finish_handler(mysql::error()); |
| | | break; |
| | | } |
| | | } |
| | | else if (ret == 1) |
| | | { |
| | | finish_handler(mysql::error(*this)); |
| | | break; |
| | | } |
| | | else |
| | | { |
| | | finish_handler(mysql::error()); |
| | | break; |
| | | } |
| | | }; |
| | | return status; |
| | | } |
| | | else if (ret == 1) |
| | | |
| | | template <typename ResultHandler> |
| | | void wait_free_result(int status, ResultHandler &&handler) NOEXCEPT |
| | | { |
| | | finish_handler(mysql::error(*this)); |
| | | break; |
| | | m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql), |
| | | [this, handler](int flags) mutable |
| | | { |
| | | my_bool ret = false; |
| | | int status = mysql_stmt_free_result_cont(&ret, m_stmt, mysql_status(flags)); |
| | | if (status) |
| | | wait_free_result(status, handler); |
| | | else |
| | | handler(mysql::error()); |
| | | }); |
| | | } |
| | | else |
| | | |
| | | template <typename Handler> |
| | | void wait_next_result(int status, Handler &&handler) |
| | | { |
| | | finish_handler(mysql::error()); |
| | | break; |
| | | m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql), |
| | | [this, handler](int flags) mutable |
| | | { |
| | | int ret = 0; |
| | | int status = mysql_stmt_next_result_cont(&ret, m_stmt, mysql_status(flags)); |
| | | if (status) |
| | | wait_next_result(status, handler); |
| | | else if (ret) |
| | | handler(mysql::error(*this)); |
| | | else if (mysql_stmt_field_count(m_stmt) > 0) |
| | | handler(mysql::error()); |
| | | else |
| | | next_result(std::forward<Handler>(handler)); |
| | | }); |
| | | } |
| | | }; |
| | | return status; |
| | | } |
| | | |
| | | template<typename ResultHandler> |
| | | void wait_free_result(int status, ResultHandler&& handler) NOEXCEPT |
| | | { |
| | | m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql), |
| | | [this, handler](int flags) mutable { |
| | | my_bool ret = false; |
| | | int status = mysql_stmt_free_result_cont(&ret, m_stmt, mysql_status(flags)); |
| | | class async_connection : public basic_database, public qtl::async_connection<async_connection, async_statement> |
| | | { |
| | | public: |
| | | async_connection() = default; |
| | | |
| | | /* |
| | | OpenHandler defines as: |
| | | void handler(const qtl::mysql::error& e) NOEXCEPT; |
| | | */ |
| | | template <typename EventLoop, typename OpenHandler> |
| | | void open(EventLoop &ev, OpenHandler &&handler, const char *host, const char *user, const char *passwd, const char *db, |
| | | unsigned long clientflag = 0, unsigned int port = 0, const char *unix_socket = nullptr) |
| | | { |
| | | if (m_mysql == nullptr) |
| | | m_mysql = mysql_init(nullptr); |
| | | |
| | | mysql_options(m_mysql, MYSQL_OPT_NONBLOCK, 0); |
| | | |
| | | MYSQL *ret = nullptr; |
| | | int status = mysql_real_connect_start(&ret, m_mysql, host, user, passwd, db, port, unix_socket, clientflag); |
| | | bind(ev); |
| | | if (status) |
| | | wait_free_result(status, handler); |
| | | wait_connect(status, std::forward<OpenHandler>(handler)); |
| | | else |
| | | handler(mysql::error()); |
| | | }); |
| | | } |
| | | handler((ret == nullptr) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | |
| | | template<typename Handler> |
| | | void wait_next_result(int status, Handler&& handler) |
| | | { |
| | | m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql), |
| | | [this, handler](int flags) mutable { |
| | | int ret = 0; |
| | | int status = mysql_stmt_next_result_cont(&ret, m_stmt, mysql_status(flags)); |
| | | if (status) |
| | | wait_next_result(status, handler); |
| | | else if (ret) |
| | | handler(mysql::error(*this)); |
| | | else if (mysql_stmt_field_count(m_stmt) > 0) |
| | | handler(mysql::error()); |
| | | else |
| | | next_result(std::forward<Handler>(handler)); |
| | | }); |
| | | } |
| | | |
| | | }; |
| | | |
| | | class async_connection : public basic_database, public qtl::async_connection<async_connection, async_statement> |
| | | { |
| | | public: |
| | | async_connection() = default; |
| | | |
| | | /* |
| | | OpenHandler defines as: |
| | | void handler(const qtl::mysql::error& e) NOEXCEPT; |
| | | */ |
| | | template<typename EventLoop, typename OpenHandler> |
| | | void open(EventLoop& ev, OpenHandler&& handler, const char *host, const char *user, const char *passwd, const char *db, |
| | | unsigned long clientflag = 0, unsigned int port = 0, const char *unix_socket = nullptr) |
| | | { |
| | | if (m_mysql == nullptr) |
| | | m_mysql = mysql_init(nullptr); |
| | | |
| | | mysql_options(m_mysql, MYSQL_OPT_NONBLOCK, 0); |
| | | |
| | | MYSQL* ret = nullptr; |
| | | int status = mysql_real_connect_start(&ret, m_mysql, host, user, passwd, db, port, unix_socket, clientflag); |
| | | bind(ev); |
| | | if (status) |
| | | wait_connect(status, std::forward<OpenHandler>(handler)); |
| | | else |
| | | handler((ret == nullptr) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | |
| | | /* |
| | | CloseHandler defines as: |
| | | void handler() NOEXCEPT; |
| | | */ |
| | | template<typename CloseHandler > |
| | | void close(CloseHandler&& handler) NOEXCEPT |
| | | { |
| | | int status = mysql_close_start(m_mysql); |
| | | if (status) |
| | | { |
| | | wait_close(status, [this, handler]() mutable { |
| | | /* |
| | | CloseHandler defines as: |
| | | void handler() NOEXCEPT; |
| | | */ |
| | | template <typename CloseHandler> |
| | | void close(CloseHandler &&handler) NOEXCEPT |
| | | { |
| | | int status = mysql_close_start(m_mysql); |
| | | if (status) |
| | | { |
| | | wait_close(status, [this, handler]() mutable |
| | | { |
| | | handler(); |
| | | m_mysql = nullptr; |
| | | }); |
| | | } |
| | | else |
| | | { |
| | | handler(); |
| | | m_mysql = nullptr; |
| | | } |
| | | } |
| | | m_mysql = nullptr; }); |
| | | } |
| | | else |
| | | { |
| | | handler(); |
| | | m_mysql = nullptr; |
| | | } |
| | | } |
| | | |
| | | /* |
| | | Handler defines as: |
| | | void handler(const qtl::mysql::error& e) NOEXCEPT; |
| | | */ |
| | | template<typename Handler> |
| | | void refresh(Handler&& handler, unsigned int options) NOEXCEPT |
| | | { |
| | | int ret = 0; |
| | | int status = mysql_refresh_start(&ret, m_mysql, options); |
| | | if (status) |
| | | wait_operation(status, &mysql_refresh_cont, std::forward<Handler>(handler)); |
| | | else |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | /* |
| | | Handler defines as: |
| | | void handler(const qtl::mysql::error& e) NOEXCEPT; |
| | | */ |
| | | template <typename Handler> |
| | | void refresh(Handler &&handler, unsigned int options) NOEXCEPT |
| | | { |
| | | int ret = 0; |
| | | int status = mysql_refresh_start(&ret, m_mysql, options); |
| | | if (status) |
| | | wait_operation(status, &mysql_refresh_cont, std::forward<Handler>(handler)); |
| | | else |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | |
| | | template<typename Handler> |
| | | void select(Handler&& handler, const char* db) NOEXCEPT |
| | | { |
| | | int ret = 0; |
| | | int status = mysql_select_db_start(&ret, m_mysql, db); |
| | | if (status) |
| | | wait_operation(status, &mysql_select_db_cont, std::forward<Handler>(handler)); |
| | | else |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | template <typename Handler> |
| | | void select(Handler &&handler, const char *db) NOEXCEPT |
| | | { |
| | | int ret = 0; |
| | | int status = mysql_select_db_start(&ret, m_mysql, db); |
| | | if (status) |
| | | wait_operation(status, &mysql_select_db_cont, std::forward<Handler>(handler)); |
| | | else |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | |
| | | template<typename Handler> |
| | | void is_alive(Handler&& handler) NOEXCEPT |
| | | { |
| | | int ret = 0; |
| | | int status = mysql_ping_start(&ret, m_mysql); |
| | | if (status) |
| | | wait_operation(status, &mysql_ping_cont, std::forward<Handler>(handler)); |
| | | else |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | template <typename Handler> |
| | | void is_alive(Handler &&handler) NOEXCEPT |
| | | { |
| | | int ret = 0; |
| | | int status = mysql_ping_start(&ret, m_mysql); |
| | | if (status) |
| | | wait_operation(status, &mysql_ping_cont, std::forward<Handler>(handler)); |
| | | else |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | |
| | | /* |
| | | Handler defines as: |
| | | void handler(const qtl::mysql::error& e, uint64_t affected) NOEXCEPT; |
| | | */ |
| | | template<typename ExecuteHandler> |
| | | void simple_execute(ExecuteHandler&& handler, const char* query_text) NOEXCEPT |
| | | { |
| | | int ret = 0; |
| | | int status = mysql_query_start(&ret, m_mysql, query_text); |
| | | if (status) |
| | | { |
| | | wait_operation(status, &mysql_query_cont, [this, handler](const mysql::error& e) { |
| | | /* |
| | | Handler defines as: |
| | | void handler(const qtl::mysql::error& e, uint64_t affected) NOEXCEPT; |
| | | */ |
| | | template <typename ExecuteHandler> |
| | | void simple_execute(ExecuteHandler &&handler, const char *query_text) NOEXCEPT |
| | | { |
| | | int ret = 0; |
| | | int status = mysql_query_start(&ret, m_mysql, query_text); |
| | | if (status) |
| | | { |
| | | wait_operation(status, &mysql_query_cont, [this, handler](const mysql::error &e) |
| | | { |
| | | uint64_t affected = 0; |
| | | if (!e) affected = affected_rows(); |
| | | handler(e, affected); |
| | | }); |
| | | } |
| | | else |
| | | { |
| | | uint64_t affected = 0; |
| | | if (ret >= 0) affected = affected_rows(); |
| | | handler((ret) ? mysql::error(*this) : mysql::error(), affected); |
| | | } |
| | | } |
| | | handler(e, affected); }); |
| | | } |
| | | else |
| | | { |
| | | uint64_t affected = 0; |
| | | if (ret >= 0) |
| | | affected = affected_rows(); |
| | | handler((ret) ? mysql::error(*this) : mysql::error(), affected); |
| | | } |
| | | } |
| | | |
| | | template<typename ExecuteHandler> |
| | | void simple_execute(ExecuteHandler&& handler, const char* query_text, unsigned long text_length) NOEXCEPT |
| | | { |
| | | int ret = 0; |
| | | int status = mysql_real_query_start(&ret, m_mysql, query_text, text_length); |
| | | if (status) |
| | | { |
| | | wait_operation(status, &mysql_real_query_cont, [this, handler](const mysql::error& e) mutable { |
| | | template <typename ExecuteHandler> |
| | | void simple_execute(ExecuteHandler &&handler, const char *query_text, unsigned long text_length) NOEXCEPT |
| | | { |
| | | int ret = 0; |
| | | int status = mysql_real_query_start(&ret, m_mysql, query_text, text_length); |
| | | if (status) |
| | | { |
| | | wait_operation(status, &mysql_real_query_cont, [this, handler](const mysql::error &e) mutable |
| | | { |
| | | uint64_t affected = 0; |
| | | if (!e) affected = affected_rows(); |
| | | handler(e, affected); |
| | | }); |
| | | } |
| | | else |
| | | { |
| | | uint64_t affected = 0; |
| | | if (ret >= 0) affected = affected_rows(); |
| | | handler((ret) ? mysql::error(*this) : mysql::error(), affected); |
| | | } |
| | | } |
| | | handler(e, affected); }); |
| | | } |
| | | else |
| | | { |
| | | uint64_t affected = 0; |
| | | if (ret >= 0) |
| | | affected = affected_rows(); |
| | | handler((ret) ? mysql::error(*this) : mysql::error(), affected); |
| | | } |
| | | } |
| | | |
| | | template<typename Handler> |
| | | void auto_commit(Handler&& handler, bool on) NOEXCEPT |
| | | { |
| | | my_bool ret; |
| | | int status = mysql_autocommit_start(&ret, m_mysql, on ? 1 : 0); |
| | | if(status) |
| | | wait_operation(status, &mysql_autocommit_cont, std::forward<Handler>(handler)); |
| | | else |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | template <typename Handler> |
| | | void auto_commit(Handler &&handler, bool on) NOEXCEPT |
| | | { |
| | | my_bool ret; |
| | | int status = mysql_autocommit_start(&ret, m_mysql, on ? 1 : 0); |
| | | if (status) |
| | | wait_operation(status, &mysql_autocommit_cont, std::forward<Handler>(handler)); |
| | | else |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | |
| | | template<typename Handler> |
| | | void begin_transaction(Handler&& handler) NOEXCEPT |
| | | { |
| | | auto_commit(handler, false); |
| | | } |
| | | template <typename Handler> |
| | | void begin_transaction(Handler &&handler) NOEXCEPT |
| | | { |
| | | auto_commit(handler, false); |
| | | } |
| | | |
| | | template<typename Handler> |
| | | void rollback(Handler&& handler) NOEXCEPT |
| | | { |
| | | my_bool ret; |
| | | int status = mysql_rollback_start(&ret, m_mysql); |
| | | if (status) |
| | | wait_operation(status, &mysql_rollback_cont, std::forward<Handler>(handler)); |
| | | else |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | template <typename Handler> |
| | | void rollback(Handler &&handler) NOEXCEPT |
| | | { |
| | | my_bool ret; |
| | | int status = mysql_rollback_start(&ret, m_mysql); |
| | | if (status) |
| | | wait_operation(status, &mysql_rollback_cont, std::forward<Handler>(handler)); |
| | | else |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | |
| | | template<typename Handler> |
| | | void commit(Handler&& handler) NOEXCEPT |
| | | { |
| | | my_bool ret; |
| | | int status = mysql_commit_start(&ret, m_mysql); |
| | | if (status) |
| | | wait_operation(status, &mysql_commit_cont, std::forward<Handler>(handler)); |
| | | else |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | template <typename Handler> |
| | | void commit(Handler &&handler) NOEXCEPT |
| | | { |
| | | my_bool ret; |
| | | int status = mysql_commit_start(&ret, m_mysql); |
| | | if (status) |
| | | wait_operation(status, &mysql_commit_cont, std::forward<Handler>(handler)); |
| | | else |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | } |
| | | |
| | | /* |
| | | RowHandler defines as: |
| | | bool row_handler(MYSQL_ROW row, int field_count) NOEXCEPT; |
| | | ResultHandler defines as: |
| | | void result_handler(const qtl::mysql::error& e, size_t row_count) NOEXCEPT; |
| | | */ |
| | | template<typename RowHandler, typename ResultHandler> |
| | | void simple_query(const char* query, unsigned long length, RowHandler&& row_handler, ResultHandler&& result_handler) NOEXCEPT |
| | | { |
| | | simple_execute([this, row_handler, result_handler](const mysql::error& e, uint64_t affected) mutable { |
| | | /* |
| | | RowHandler defines as: |
| | | bool row_handler(MYSQL_ROW row, int field_count) NOEXCEPT; |
| | | ResultHandler defines as: |
| | | void result_handler(const qtl::mysql::error& e, size_t row_count) NOEXCEPT; |
| | | */ |
| | | template <typename RowHandler, typename ResultHandler> |
| | | void simple_query(const char *query, unsigned long length, RowHandler &&row_handler, ResultHandler &&result_handler) NOEXCEPT |
| | | { |
| | | simple_execute([this, row_handler, result_handler](const mysql::error &e, uint64_t affected) mutable |
| | | { |
| | | if (e) |
| | | { |
| | | result_handler(e, 0); |
| | |
| | | else |
| | | { |
| | | result_handler(mysql::error(), 0); |
| | | } }, query, length); |
| | | } |
| | | }, query, length); |
| | | |
| | | template <typename Handler> |
| | | void open_command(const char *query_text, size_t text_length, Handler &&handler) |
| | | { |
| | | std::shared_ptr<async_statement> stmt = std::make_shared<async_statement>(*this); |
| | | stmt->open([stmt, handler](const mysql::error &e) mutable |
| | | { handler(e, stmt); }, query_text, (unsigned long)text_length); |
| | | } |
| | | |
| | | socket_type socket() const NOEXCEPT { return mysql_get_socket(m_mysql); } |
| | | |
| | | private: |
| | | template <typename OpenHandler> |
| | | void wait_connect(int status, OpenHandler &&handler) NOEXCEPT |
| | | { |
| | | m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql), |
| | | [this, handler](int flags) mutable |
| | | { |
| | | MYSQL *ret = nullptr; |
| | | int status = mysql_real_connect_cont(&ret, m_mysql, mysql_status(flags)); |
| | | if (status) |
| | | wait_connect(status, handler); |
| | | else |
| | | handler((ret == nullptr) ? mysql::error(*this) : mysql::error()); |
| | | }); |
| | | } |
| | | |
| | | template <typename CloseHandler> |
| | | void wait_close(int status, CloseHandler &&handler) NOEXCEPT |
| | | { |
| | | m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql), |
| | | [this, handler](int flags) mutable |
| | | { |
| | | MYSQL *ret = nullptr; |
| | | int status = mysql_close_cont(m_mysql, mysql_status(flags)); |
| | | if (status) |
| | | wait_close(status, handler); |
| | | else |
| | | handler(); |
| | | }); |
| | | } |
| | | |
| | | template <typename Func, typename Handler> |
| | | void wait_operation(int status, Func func, Handler &&handler) NOEXCEPT |
| | | { |
| | | m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql), |
| | | [this, func, handler](int flags) mutable |
| | | { |
| | | int ret = 0; |
| | | int status = func(&ret, m_mysql, mysql_status(flags)); |
| | | if (status) |
| | | wait_operation(status, func, handler); |
| | | else |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | }); |
| | | } |
| | | |
| | | template <typename RowHandler, typename ResultHandler> |
| | | void wait_query(int status, int field_count, RowHandler &&row_handler, ResultHandler &&result_handler) NOEXCEPT |
| | | { |
| | | m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql), |
| | | [this, field_count, row_handler, result_handler](int flags) mutable |
| | | { |
| | | MYSQL_RES *result = 0; |
| | | int status = mysql_store_result_cont(&result, m_mysql, mysql_status(flags)); |
| | | if (status) |
| | | wait_query(status, field_count, row_handler, result_handler); |
| | | else if (result) |
| | | fetch_rows(result, field_count, 0, row_handler, result_handler); |
| | | else |
| | | result_handler(mysql::error(*this), 0); |
| | | }); |
| | | } |
| | | |
| | | template <typename RowHandler, typename ResultHandler> |
| | | void fetch_rows(MYSQL_RES *result, int field_count, size_t row_count, RowHandler &&row_handler, ResultHandler &&result_handler) NOEXCEPT |
| | | { |
| | | MYSQL_ROW row; |
| | | int status = mysql_fetch_row_start(&row, result); |
| | | if (status) |
| | | wait_fetch(status, result, field_count, row_count, row_handler, result_handler); |
| | | else if (row && row_handler(row, field_count)) |
| | | fetch_rows(result, field_count, row_count + 1, row_handler, result_handler); |
| | | else |
| | | free_result(result, row_count, result_handler); |
| | | } |
| | | |
| | | template <typename ResultHandler> |
| | | void free_result(MYSQL_RES *result, size_t row_count, ResultHandler &&result_handler) NOEXCEPT |
| | | { |
| | | int status = mysql_free_result_start(result); |
| | | if (status) |
| | | wait_free_result(status, result, row_count, result_handler); |
| | | else |
| | | result_handler(mysql::error(), row_count); |
| | | } |
| | | |
| | | template <typename RowHandler, typename ResultHandler> |
| | | void wait_fetch(int status, MYSQL_RES *result, int field_count, size_t row_count, RowHandler &&row_handler, ResultHandler &&result_handler) |
| | | { |
| | | m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql), |
| | | [this, result, field_count, row_count, row_handler, result_handler](int flags) mutable |
| | | { |
| | | MYSQL_ROW row; |
| | | int status = mysql_fetch_row_cont(&row, result, mysql_status(flags)); |
| | | if (status) |
| | | wait_fetch(status, result, field_count, row_count, row_handler, result_handler); |
| | | else if (result && row_handler(row, field_count)) |
| | | fetch_rows(result, field_count, row_count + 1, row_handler, result_handler); |
| | | else |
| | | free_result(result, row_count, result_handler); |
| | | }); |
| | | } |
| | | |
| | | template <typename ResultHandler> |
| | | void wait_free_result(int status, MYSQL_RES *result, size_t row_count, ResultHandler &&handler) NOEXCEPT |
| | | { |
| | | m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql), |
| | | [this, result, row_count, handler](int flags) mutable |
| | | { |
| | | MYSQL *ret = nullptr; |
| | | int status = mysql_free_result_cont(result, mysql_status(flags)); |
| | | if (status) |
| | | wait_free_result(status, result, row_count, handler); |
| | | else |
| | | handler(mysql::error(), row_count); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | inline async_statement::async_statement(async_connection &db) |
| | | : base_statement(static_cast<basic_database &>(db)) |
| | | { |
| | | m_event = db.event(); |
| | | } |
| | | |
| | | #endif // MariaDB 10.0 |
| | | |
| | | typedef qtl::transaction<database> transaction; |
| | | |
| | | template <typename Record> |
| | | using query_iterator = qtl::query_iterator<statement, Record>; |
| | | |
| | | template <typename Record> |
| | | using query_result = qtl::query_result<statement, Record>; |
| | | |
| | | template <typename Params> |
| | | inline statement &operator<<(statement &stmt, const Params ¶ms) |
| | | { |
| | | stmt.reset(); |
| | | stmt.execute(params); |
| | | return stmt; |
| | | } |
| | | |
| | | inline error::error(const base_statement &stmt) |
| | | { |
| | | const char *errmsg = stmt.errmsg(); |
| | | m_error = stmt.error(); |
| | | if (errmsg) |
| | | m_errmsg = errmsg; |
| | | } |
| | | |
| | | inline error::error(const basic_database &db) |
| | | { |
| | | const char *errmsg = db.errmsg(); |
| | | m_error = db.error(); |
| | | if (errmsg) |
| | | m_errmsg = errmsg; |
| | | } |
| | | |
| | | inline base_statement::base_statement(basic_database &db) |
| | | { |
| | | m_stmt = mysql_stmt_init(db.handle()); |
| | | m_result = nullptr; |
| | | } |
| | | |
| | | } |
| | | |
| | | template<typename Handler> |
| | | void open_command(const char* query_text, size_t text_length, Handler&& handler) |
| | | { |
| | | std::shared_ptr<async_statement> stmt=std::make_shared<async_statement>(*this); |
| | | stmt->open([stmt, handler](const mysql::error& e) mutable { |
| | | handler(e, stmt); |
| | | }, query_text, (unsigned long)text_length); |
| | | } |
| | | |
| | | socket_type socket() const NOEXCEPT { return mysql_get_socket(m_mysql); } |
| | | |
| | | private: |
| | | template<typename OpenHandler> |
| | | void wait_connect(int status, OpenHandler&& handler) NOEXCEPT |
| | | { |
| | | m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql), |
| | | [this, handler](int flags) mutable { |
| | | MYSQL* ret = nullptr; |
| | | int status = mysql_real_connect_cont(&ret, m_mysql, mysql_status(flags)); |
| | | if (status) |
| | | wait_connect(status, handler); |
| | | else |
| | | handler((ret == nullptr) ? mysql::error(*this) : mysql::error()); |
| | | }); |
| | | } |
| | | |
| | | template<typename CloseHandler> |
| | | void wait_close(int status, CloseHandler&& handler) NOEXCEPT |
| | | { |
| | | m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql), |
| | | [this, handler](int flags) mutable { |
| | | MYSQL* ret = nullptr; |
| | | int status = mysql_close_cont(m_mysql, mysql_status(flags)); |
| | | if (status) |
| | | wait_close(status, handler); |
| | | else |
| | | handler(); |
| | | }); |
| | | } |
| | | |
| | | template<typename Func, typename Handler> |
| | | void wait_operation(int status, Func func, Handler&& handler) NOEXCEPT |
| | | { |
| | | m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql), |
| | | [this, func, handler](int flags) mutable { |
| | | int ret = 0; |
| | | int status = func(&ret, m_mysql, mysql_status(flags)); |
| | | if (status) |
| | | wait_operation(status, func, handler); |
| | | else |
| | | handler((ret) ? mysql::error(*this) : mysql::error()); |
| | | }); |
| | | } |
| | | |
| | | template<typename RowHandler, typename ResultHandler> |
| | | void wait_query(int status, int field_count, RowHandler&& row_handler, ResultHandler&& result_handler) NOEXCEPT |
| | | { |
| | | m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql), |
| | | [this, field_count, row_handler, result_handler](int flags) mutable { |
| | | MYSQL_RES* result = 0; |
| | | int status = mysql_store_result_cont(&result, m_mysql, mysql_status(flags)); |
| | | if (status) |
| | | wait_query(status, field_count, row_handler, result_handler); |
| | | else if (result) |
| | | fetch_rows(result, field_count, 0, row_handler, result_handler); |
| | | else |
| | | result_handler(mysql::error(*this), 0); |
| | | }); |
| | | } |
| | | |
| | | template<typename RowHandler, typename ResultHandler> |
| | | void fetch_rows(MYSQL_RES* result, int field_count, size_t row_count, RowHandler&& row_handler, ResultHandler&& result_handler) NOEXCEPT |
| | | { |
| | | MYSQL_ROW row; |
| | | int status = mysql_fetch_row_start(&row, result); |
| | | if (status) |
| | | wait_fetch(status, result, field_count, row_count, row_handler, result_handler); |
| | | else if(row && row_handler(row, field_count)) |
| | | fetch_rows(result, field_count, row_count + 1, row_handler, result_handler); |
| | | else |
| | | free_result(result, row_count, result_handler); |
| | | } |
| | | |
| | | template<typename ResultHandler> |
| | | void free_result(MYSQL_RES* result, size_t row_count, ResultHandler&& result_handler) NOEXCEPT |
| | | { |
| | | int status = mysql_free_result_start(result); |
| | | if (status) |
| | | wait_free_result(status, result, row_count, result_handler); |
| | | else |
| | | result_handler(mysql::error(), row_count); |
| | | } |
| | | |
| | | template<typename RowHandler, typename ResultHandler> |
| | | void wait_fetch(int status, MYSQL_RES* result, int field_count, size_t row_count, RowHandler&& row_handler, ResultHandler&& result_handler) |
| | | { |
| | | m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql), |
| | | [this, result, field_count, row_count, row_handler, result_handler](int flags) mutable { |
| | | MYSQL_ROW row; |
| | | int status = mysql_fetch_row_cont(&row, result, mysql_status(flags)); |
| | | if (status) |
| | | wait_fetch(status, result, field_count, row_count, row_handler, result_handler); |
| | | else if (result && row_handler(row, field_count)) |
| | | fetch_rows(result, field_count, row_count+1, row_handler, result_handler); |
| | | else |
| | | free_result(result, row_count, result_handler); |
| | | }); |
| | | } |
| | | |
| | | template<typename ResultHandler> |
| | | void wait_free_result(int status, MYSQL_RES* result, size_t row_count, ResultHandler&& handler) NOEXCEPT |
| | | { |
| | | m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql), |
| | | [this, result, row_count, handler](int flags) mutable { |
| | | MYSQL* ret = nullptr; |
| | | int status = mysql_free_result_cont(result, mysql_status(flags)); |
| | | if (status) |
| | | wait_free_result(status, result, row_count, handler); |
| | | else |
| | | handler(mysql::error(), row_count); |
| | | }); |
| | | } |
| | | |
| | | }; |
| | | |
| | | inline async_statement::async_statement(async_connection& db) |
| | | : base_statement(static_cast<basic_database&>(db)) |
| | | { |
| | | m_event=db.event(); |
| | | } |
| | | |
| | | #endif //MariaDB 10.0 |
| | | |
| | | typedef qtl::transaction<database> transaction; |
| | | |
| | | template<typename Record> |
| | | using query_iterator = qtl::query_iterator<statement, Record>; |
| | | |
| | | template<typename Record> |
| | | using query_result = qtl::query_result<statement, Record>; |
| | | |
| | | template<typename Params> |
| | | inline statement& operator<<(statement& stmt, const Params& params) |
| | | { |
| | | stmt.reset(); |
| | | stmt.execute(params); |
| | | return stmt; |
| | | } |
| | | |
| | | inline error::error(const base_statement& stmt) |
| | | { |
| | | const char* errmsg=stmt.errmsg(); |
| | | m_error=stmt.error(); |
| | | if(errmsg) m_errmsg=errmsg; |
| | | } |
| | | |
| | | inline error::error(const basic_database& db) |
| | | { |
| | | const char* errmsg=db.errmsg(); |
| | | m_error=db.error(); |
| | | if(errmsg) m_errmsg=errmsg; |
| | | } |
| | | |
| | | inline base_statement::base_statement(basic_database& db) |
| | | { |
| | | m_stmt=mysql_stmt_init(db.handle()); |
| | | m_result=nullptr; |
| | | } |
| | | |
| | | } |
| | | |
| | | } |
| | | |