Ferenc Szontágh
2024-07-01 abf49b44cc47f39d6cceb83866f915bc03b7d900
include/qtl_mysql.hpp
@@ -18,1722 +18,1760 @@
#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, &param](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, &param](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 &param)
         {
            m_binders[index].bind(nullptr, 0, MYSQL_TYPE_LONG_BLOB);
            m_binderAddins[index].m_after_fetch = [this, index, &param](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 &param)
         {
            m_binders[index].bind(nullptr, 0, MYSQL_TYPE_LONG_BLOB);
            m_binderAddins[index].m_after_fetch = [this, index, &param](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, 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([&params](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 &params)
         {
            execute_custom([&params](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 &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)
                  {
                     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);
@@ -1755,180 +1793,185 @@
         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 &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;
      }
   }
   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;
}
}
}