Ferenc Szontágh
2024-07-01 abf49b44cc47f39d6cceb83866f915bc03b7d900
include/qtl_postgres.hpp
@@ -31,10 +31,9 @@
#include <catalog/pg_type.h>
}
#ifdef open
#undef open
#endif //open
#endif // open
#ifdef vsnprintf
#undef vsnprintf
@@ -65,15 +64,15 @@
#ifdef _M_IX86
#define _WS2_32_WINSOCK_SWAP_LONGLONG(l)            \
            ( ( ((l) >> 56) & 0x00000000000000FFLL ) |       \
              ( ((l) >> 40) & 0x000000000000FF00LL ) |       \
              ( ((l) >> 24) & 0x0000000000FF0000LL ) |       \
              ( ((l) >>  8) & 0x00000000FF000000LL ) |       \
              ( ((l) <<  8) & 0x000000FF00000000LL ) |       \
              ( ((l) << 24) & 0x0000FF0000000000LL ) |       \
              ( ((l) << 40) & 0x00FF000000000000LL ) |       \
              ( ((l) << 56) & 0xFF00000000000000LL ) )
#define _WS2_32_WINSOCK_SWAP_LONGLONG(l)    \
   ((((l) >> 56) & 0x00000000000000FFLL) | \
    (((l) >> 40) & 0x000000000000FF00LL) | \
    (((l) >> 24) & 0x0000000000FF0000LL) | \
    (((l) >> 8) & 0x00000000FF000000LL) |  \
    (((l) << 8) & 0x000000FF00000000LL) |  \
    (((l) << 24) & 0x0000FF0000000000LL) | \
    (((l) << 40) & 0x00FF000000000000LL) | \
    (((l) << 56) & 0xFF00000000000000LL))
#ifndef htonll
__inline unsigned __int64 htonll(unsigned __int64 Value)
@@ -98,2243 +97,2306 @@
namespace qtl
{
namespace postgres
{
   namespace postgres
   {
namespace detail
{
      namespace detail
      {
   inline int16_t ntoh(int16_t v)
   {
      return static_cast<int16_t>(ntohs(v));
   }
   inline uint16_t ntoh(uint16_t v)
   {
      return ntohs(v);
   }
   inline int32_t ntoh(int32_t v)
   {
      return static_cast<int32_t>(ntohl(v));
   }
   inline uint32_t ntoh(uint32_t v)
   {
      return ntohl(v);
   }
   inline uint64_t ntoh(uint64_t v)
   {
         inline int16_t ntoh(int16_t v)
         {
            return static_cast<int16_t>(ntohs(v));
         }
         inline uint16_t ntoh(uint16_t v)
         {
            return ntohs(v);
         }
         inline int32_t ntoh(int32_t v)
         {
            return static_cast<int32_t>(ntohl(v));
         }
         inline uint32_t ntoh(uint32_t v)
         {
            return ntohl(v);
         }
         inline uint64_t ntoh(uint64_t v)
         {
#ifdef _WIN32
      return ntohll(v);
            return ntohll(v);
#else
      return be64toh(v);
            return be64toh(v);
#endif
   }
   inline int64_t ntoh(int64_t v)
   {
      return ntoh(static_cast<uint64_t>(v));
   }
         }
         inline int64_t ntoh(int64_t v)
         {
            return ntoh(static_cast<uint64_t>(v));
         }
   template<typename T, typename = typename std::enable_if<std::is_integral<T>::value && !std::is_const<T>::value>::type>
   inline T& ntoh_inplace(T& v)
   {
      v = ntoh(v);
      return v;
   }
         template <typename T, typename = typename std::enable_if<std::is_integral<T>::value && !std::is_const<T>::value>::type>
         inline T &ntoh_inplace(T &v)
         {
            v = ntoh(v);
            return v;
         }
   inline int16_t hton(int16_t v)
   {
      return static_cast<int16_t>(htons(v));
   }
   inline uint16_t hton(uint16_t v)
   {
      return htons(v);
   }
   inline int32_t hton(int32_t v)
   {
      return static_cast<int32_t>(htonl(v));
   }
   inline uint32_t hton(uint32_t v)
   {
      return htonl(v);
   }
   inline uint64_t hton(uint64_t v)
   {
         inline int16_t hton(int16_t v)
         {
            return static_cast<int16_t>(htons(v));
         }
         inline uint16_t hton(uint16_t v)
         {
            return htons(v);
         }
         inline int32_t hton(int32_t v)
         {
            return static_cast<int32_t>(htonl(v));
         }
         inline uint32_t hton(uint32_t v)
         {
            return htonl(v);
         }
         inline uint64_t hton(uint64_t v)
         {
#ifdef _WIN32
      return htonll(v);
            return htonll(v);
#else
      return htobe64(v);
            return htobe64(v);
#endif
   }
   inline int64_t hton(int64_t v)
   {
      return hton(static_cast<uint64_t>(v));
   }
   template<typename T, typename = typename std::enable_if<std::is_integral<T>::value && !std::is_const<T>::value>::type>
   inline T& hton_inplace(T& v)
   {
      v = hton(v);
      return v;
   }
   template<typename T, typename = typename std::enable_if<std::is_integral<T>::value && !std::is_const<T>::value>::type>
   std::pair<std::vector<char>::iterator, size_t> push(std::vector<char>& buffer, T v)
   {
      v = hton_inplace(v);
      char* data = reinterpret_cast<char*>(&v);
      auto it = buffer.insert(buffer.end(), data, data + sizeof(T));
      return std::make_pair(it, sizeof(T));
   }
   template<typename T, typename = typename std::enable_if<std::is_integral<T>::value && !std::is_const<T>::value>::type>
   const char* pop(const char* data, T& v)
   {
      v = ntoh(*reinterpret_cast<const T*>(data));
      return data + sizeof(T);
   }
}
class base_database;
class result;
class error : public std::exception
{
public:
   error() : m_errmsg() { }
   explicit error(PGconn* conn, PGVerbosity verbosity = PQERRORS_DEFAULT, PGContextVisibility show_context = PQSHOW_CONTEXT_ERRORS)
   {
      //PQsetErrorVerbosity(conn, verbosity);
      //PQsetErrorContextVisibility(conn, show_context);
      const char* errmsg = PQerrorMessage(conn);
      if (errmsg) m_errmsg = errmsg;
      else m_errmsg.clear();
   }
   explicit error(PGresult* res)
   {
      const char* errmsg = PQresultErrorMessage(res);
      if (errmsg) m_errmsg = errmsg;
      else m_errmsg.clear();
   }
   explicit error(const char* errmsg) : m_errmsg(errmsg) { }
   virtual const char* what() const NOEXCEPT override { return m_errmsg.data(); }
   operator bool() const { return !m_errmsg.empty(); }
protected:
   std::string m_errmsg;
};
class timeout : public error
{
public:
   timeout()
   {
      m_errmsg = "timeout";
   }
};
inline void verify_pgtypes_error(int ret)
{
   if(ret && errno != 0)
      throw std::system_error(std::error_code(errno, std::generic_category()));
}
struct interval
{
   ::interval* value;
   interval()
   {
      value = PGTYPESinterval_new();
   }
   explicit interval(char* str)
   {
       value = PGTYPESinterval_from_asc(str, nullptr);
   }
   interval(const interval& src) : interval()
   {
      verify_pgtypes_error(PGTYPESinterval_copy(src.value, value));
   }
   interval(interval&& src)
   {
      value = src.value;
      src.value = PGTYPESinterval_new();
   }
   ~interval()
   {
      PGTYPESinterval_free(value);
   }
   std::string to_string() const
   {
      return PGTYPESinterval_to_asc(value);
   }
   interval& operator=(const interval& src)
   {
      if(&src!=this)
         verify_pgtypes_error(PGTYPESinterval_copy(src.value, value));
      return *this;
   }
};
struct timestamp
{
   ::timestamp value;
   timestamp() = default;
   static timestamp now()
   {
      timestamp result;
      PGTYPEStimestamp_current(&result.value);
      return result;
   }
   explicit timestamp(char* str)
   {
      value = PGTYPEStimestamp_from_asc(str, nullptr);
      verify_pgtypes_error(1);
   }
   int format(char* str, int n, const char* format) const
   {
      timestamp temp = *this;
      return PGTYPEStimestamp_fmt_asc(&temp.value, str, n, format);
   }
   static timestamp parse(char* str, const char* format)
   {
      timestamp result;
      verify_pgtypes_error(PGTYPEStimestamp_defmt_asc(str, format, &result.value));
      return result;
   }
   std::string to_string() const
   {
      char* str = PGTYPEStimestamp_to_asc(value);
      std::string result = str;
      PGTYPESchar_free(str);
      return result;
   }
   timestamp& operator += (const interval& span)
   {
      verify_pgtypes_error(PGTYPEStimestamp_add_interval(&value, span.value, &value));
      return *this;
   }
   timestamp& operator -= (const interval& span)
   {
      verify_pgtypes_error(PGTYPEStimestamp_sub_interval(&value, span.value, &value));
      return *this;
   }
};
inline timestamp operator+(const timestamp& a, const interval& b)
{
   timestamp result=a;
   return result+=b;
}
inline timestamp operator-(const timestamp& a, const interval& b)
{
   timestamp result=a;
   result -= b;
   return result;
}
struct timestamptz
{
   ::TimestampTz value;
   /*
   timestamptz() = default;
   explicit timestamptz(pg_time_t v)
   {
      value = (TimestampTz)v -
         ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
      value *= USECS_PER_SEC;
   }
   static timestamptz now()
   {
      timestamptz result;
      auto tp = std::chrono::system_clock::now();
      int sec = tp.time_since_epoch().count()*std::nano::num/std::nano::den;
      int usec = tp.time_since_epoch().count()*std::nano::num % std::nano::den;
      result.value = (TimestampTz)sec -
         ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
      result.value = (result.value * USECS_PER_SEC) + usec;
      return result;
   }
   */
};
struct date
{
   ::date value;
   date() = default;
   explicit date(timestamp dt)
   {
      value = PGTYPESdate_from_timestamp(dt.value);
   }
   explicit date(char* str)
   {
      value = PGTYPESdate_from_asc(str, nullptr);
      verify_pgtypes_error(1);
   }
   explicit date(int year, int month, int day)
   {
      int mdy[3] = { month, day, year };
      PGTYPESdate_mdyjul(mdy, &value);
   }
   std::string to_string() const
   {
      char* str = PGTYPESdate_to_asc(value);
      std::string result = str;
      PGTYPESchar_free(str);
      return str;
   }
   static date now()
   {
      date result;
      PGTYPESdate_today(&result.value);
      return result;
   }
   static date parse(char* str, const char* format)
   {
      date result;
      verify_pgtypes_error(PGTYPESdate_defmt_asc(&result.value, format, str));
      return result;
   }
   std::string format(const char* format)
   {
      std::string result;
      result.resize(128);
      verify_pgtypes_error(PGTYPESdate_fmt_asc(value, format, const_cast<char*>(result.data())));
      result.resize(strlen(result.data()));
      return result;
   }
   std::tuple<int, int, int> get_date()
   {
      int mdy[3];
      PGTYPESdate_julmdy(value, mdy);
      return std::make_tuple(mdy[2], mdy[0], mdy[1]);
   }
   int dayofweek()
   {
      return PGTYPESdate_dayofweek(value);
   }
};
struct decimal
{
   ::decimal value;
};
struct numeric
{
   ::numeric* value;
   numeric()
   {
      value = PGTYPESnumeric_new();
   }
   numeric(int v) : numeric()
   {
      verify_pgtypes_error(PGTYPESnumeric_from_int(v, value));
   }
   numeric(long v) : numeric()
   {
      verify_pgtypes_error(PGTYPESnumeric_from_long(v, value));
   }
   numeric(double v) : numeric()
   {
      verify_pgtypes_error(PGTYPESnumeric_from_double(v, value));
   }
   numeric(const decimal& v) : numeric()
   {
      verify_pgtypes_error(PGTYPESnumeric_from_decimal(const_cast<::decimal*>(&v.value), value));
   }
   numeric(const numeric& src) : numeric()
   {
      verify_pgtypes_error(PGTYPESnumeric_copy(src.value, value));
   }
   explicit numeric(const char* str)
   {
      value = PGTYPESnumeric_from_asc(const_cast<char*>(str), nullptr);
   }
   ~numeric()
   {
      PGTYPESnumeric_free(value);
   }
   operator double() const
   {
      double result;
      verify_pgtypes_error(PGTYPESnumeric_to_double(value, &result));
      return result;
   }
   operator int() const
   {
      int result;
      verify_pgtypes_error(PGTYPESnumeric_to_int(value, &result));
      return result;
   }
   operator long() const
   {
      long result;
      verify_pgtypes_error(PGTYPESnumeric_to_long(value, &result));
      return result;
   }
   operator decimal() const
   {
      decimal result;
      verify_pgtypes_error(PGTYPESnumeric_to_decimal(value, &result.value));
      return result;
   }
   int compare(const numeric& other) const
   {
      return PGTYPESnumeric_cmp(value, other.value);
   }
   inline numeric& operator+=(const numeric& b)
   {
      verify_pgtypes_error(PGTYPESnumeric_add(value, b.value, value));
      return *this;
   }
   inline numeric& operator-=(const numeric& b)
   {
      verify_pgtypes_error(PGTYPESnumeric_sub(value, b.value, value));
      return *this;
   }
   inline numeric& operator*=(const numeric& b)
   {
      verify_pgtypes_error(PGTYPESnumeric_mul(value, b.value, value));
      return *this;
   }
   inline numeric& operator/=(const numeric& b)
   {
      verify_pgtypes_error(PGTYPESnumeric_div(value, b.value, value));
      return *this;
   }
   std::string to_string(int dscale=-1) const
   {
      char* str = PGTYPESnumeric_to_asc(value, dscale);
      std::string result = str;
      PGTYPESchar_free(str);
      return result;
   }
};
inline numeric operator+(const numeric& a, const numeric& b)
{
   numeric result;
   verify_pgtypes_error(PGTYPESnumeric_add(a.value, b.value, result.value));
   return result;
}
inline numeric operator-(const numeric& a, const numeric& b)
{
   numeric result;
   verify_pgtypes_error(PGTYPESnumeric_sub(a.value, b.value, result.value));
   return result;
}
inline numeric operator*(const numeric& a, const numeric& b)
{
   numeric result;
   verify_pgtypes_error(PGTYPESnumeric_mul(a.value, b.value, result.value));
   return result;
}
inline numeric operator/(const numeric& a, const numeric& b)
{
   numeric result;
   verify_pgtypes_error(PGTYPESnumeric_div(a.value, b.value, result.value));
   return result;
}
inline bool operator==(const numeric& a, const numeric& b)
{
   return a.compare(b) == 0;
}
inline bool operator<(const numeric& a, const numeric& b)
{
   return a.compare(b) < 0;
}
inline bool operator>(const numeric& a, const numeric& b)
{
   return a.compare(b) > 0;
}
inline bool operator<=(const numeric& a, const numeric& b)
{
   return a.compare(b) <= 0;
}
inline bool operator>=(const numeric& a, const numeric& b)
{
   return a.compare(b) >= 0;
}
inline bool operator!=(const numeric& a, const numeric& b)
{
   return a.compare(b) != 0;
}
class large_object : public qtl::blobbuf
{
public:
   large_object() : m_conn(nullptr), m_id(InvalidOid), m_fd(-1) { }
   large_object(PGconn* conn, Oid loid, std::ios_base::openmode mode)
   {
      open(conn, loid, mode);
   }
   large_object(const large_object&) = delete;
   large_object(large_object&& src)
   {
      swap(src);
      src.m_conn = nullptr;
      src.m_fd = -1;
   }
   ~large_object()
   {
      close();
   }
   static large_object create(PGconn* conn, Oid loid = InvalidOid)
   {
      Oid oid = lo_create(conn, loid);
      if (oid == InvalidOid)
         throw error(conn);
      return large_object(conn, oid, std::ios::in|std::ios::out|std::ios::binary);
   }
   static large_object load(PGconn* conn, const char* filename, Oid loid = InvalidOid)
   {
      Oid oid = lo_import_with_oid(conn, filename, loid);
      if (oid == InvalidOid)
         throw error(conn);
      return large_object(conn, oid, std::ios::in | std::ios::out | std::ios::binary);
   }
   void save(const char* filename) const
   {
      if (lo_export(m_conn, m_id, filename) < 0)
         throw error(m_conn);
   }
   void unlink()
   {
      close();
      if (lo_unlink(m_conn, m_id) < 0)
         throw error(m_conn);
   }
   large_object& operator=(const large_object&) = delete;
   large_object& operator=(large_object&& src)
   {
      if (this != &src)
      {
         swap(src);
         src.close();
      }
      return *this;
   }
   bool is_open() const { return m_fd >= 0; }
   Oid oid() const { return m_id; }
   void open(PGconn* conn, Oid loid, std::ios_base::openmode mode)
   {
      int lomode = 0;
      if (mode&std::ios_base::in)
         lomode |= INV_READ;
      if (mode&std::ios_base::out)
         lomode |= INV_WRITE;
      m_conn = conn;
      m_id = loid;
      m_fd = lo_open(m_conn, loid, lomode);
      if (m_fd < 0)
         throw error(m_conn);
      m_size = size();
      init_buffer(mode);
      if (mode&std::ios_base::trunc)
      {
         if (lo_truncate(m_conn, m_fd, 0) < 0)
            throw error(m_conn);
      }
   }
   void close()
   {
      if (m_fd >= 0)
      {
         overflow();
         if (lo_close(m_conn, m_fd) < 0)
            throw error(m_conn);
         m_fd = -1;
      }
   }
   void flush()
   {
      if (m_fd >= 0)
         overflow();
   }
   size_t size() const
   {
      pg_int64 size = 0;
      if (m_fd >= 0)
      {
         pg_int64 org = lo_tell64(m_conn, m_fd);
         size = lo_lseek64(m_conn, m_fd, 0, SEEK_END);
         lo_lseek64(m_conn, m_fd, org, SEEK_SET);
      }
      return size;
   }
   void resize(size_t n)
   {
      if (m_fd >= 0 && lo_truncate64(m_conn, m_fd, n) < 0)
         throw error(m_conn);
   }
   void swap(large_object& other)
   {
      std::swap(m_conn, other.m_conn);
      std::swap(m_id, other.m_id);
      std::swap(m_fd, other.m_fd);
      qtl::blobbuf::swap(other);
   }
protected:
   enum { default_buffer_size = 4096 };
   virtual bool read_blob(char* buffer, off_type& count, pos_type position) override
   {
      return lo_lseek64(m_conn, m_fd, position, SEEK_SET) >= 0 && lo_read(m_conn, m_fd, buffer, count) > 0;
   }
   virtual void write_blob(const char* buffer, size_t count) override
   {
      if (lo_write(m_conn, m_fd, buffer, count) < 0)
         throw error(m_conn);
   }
private:
   PGconn* m_conn;
   Oid m_id;
   int m_fd;
};
struct array_header
{
   int32_t ndim;
   int32_t flags;
   int32_t elemtype;
   struct dimension {
      int32_t length;
      int32_t lower_bound;
   } dims[1];
};
/*
   template<typename T>
   struct oid_traits
   {
      typedef T value_type;
      static Oid type_id;
      static Oid array_type_id; //optional
      static const char* get(value_type& result, const char* begin, const char* end);
      static std::pair<const char*, size_t> data(const T& v, std::vector<char*>& buffer);
   };
*/
template<typename T, Oid id>
struct base_object_traits
{
   typedef T value_type;
   enum { type_id = id };
   static bool is_match(Oid v)
   {
      return v == type_id;
   }
};
template<typename T>
struct object_traits;
#define QTL_POSTGRES_SIMPLE_TRAITS(T, oid, array_oid) \
template<> struct object_traits<T> : public base_object_traits<T, oid> { \
   enum { array_type_id = array_oid }; \
   static const char* get(value_type& result, const char* data, const char* end) \
   { \
      result = *reinterpret_cast<const value_type*>(data); \
      return data+sizeof(value_type); \
   } \
   static std::pair<const char*, size_t> data(const T& v, std::vector<char>& /*data*/) { \
      return std::make_pair(reinterpret_cast<const char*>(&v), sizeof(T)); \
   } \
};
QTL_POSTGRES_SIMPLE_TRAITS(bool, BOOLOID, 1000)
QTL_POSTGRES_SIMPLE_TRAITS(char, CHAROID, 1002)
QTL_POSTGRES_SIMPLE_TRAITS(float, FLOAT4OID, FLOAT4ARRAYOID)
QTL_POSTGRES_SIMPLE_TRAITS(double, FLOAT8OID, 1022)
template<typename T, Oid id, Oid array_id>
struct integral_traits : public base_object_traits<T, id>
{
   enum { array_type_id  = array_id };
   typedef typename base_object_traits<T, id>::value_type value_type;
   static const char* get(value_type& v, const char* data, const char* end)
   {
      return detail::pop(data, v);
   }
   static std::pair<const char*, size_t> data(value_type v, std::vector<char>& buffer)
   {
      size_t n = buffer.size();
      detail::push(buffer, v);
      return std::make_pair(buffer.data()+n, buffer.size()-n);
   }
};
template<> struct object_traits<int16_t> : public integral_traits<int16_t, INT2OID, INT2ARRAYOID>
{
};
template<> struct object_traits<int32_t> : public integral_traits<int32_t, INT4OID, INT4ARRAYOID>
{
};
template<> struct object_traits<int64_t> : public integral_traits<int64_t, INT8OID, 1016>
{
};
template<> struct object_traits<Oid> : public integral_traits<Oid, OIDOID, OIDARRAYOID>
{
};
template<typename T>
struct text_traits : public base_object_traits<T, TEXTOID>
{
   enum { array_type_id = TEXTARRAYOID };
};
template<> struct object_traits<const char*> : public text_traits<const char*>
{
   static bool is_match(Oid v)
   {
      return v == TEXTOID || v == VARCHAROID || v == BPCHAROID;
   }
   static const char* get(const char*& result, const char* data, const char* end)
   {
      result = data;
      return end;
   }
   static std::pair<const char*, size_t> data(const char* v, std::vector<char>& /*buffer*/)
   {
      return std::make_pair(v, strlen(v));
   }
};
template<> struct object_traits<char*> : public object_traits<const char*>
{
};
template<> struct object_traits<std::string> : public text_traits<std::string>
{
   static bool is_match(Oid v)
   {
      return v == TEXTOID || v == VARCHAROID || v == BPCHAROID;
   }
   static const char* get(value_type& result, const char* data, const char* end)
   {
      result.assign(data, end);
      return end;
   }
   static std::pair<const char*, size_t> data(const std::string& v, std::vector<char>& /*buffer*/)
   {
      return std::make_pair(v.data(), v.size());
   }
};
template<> struct object_traits<timestamp> : public base_object_traits<timestamp, TIMESTAMPOID>
{
   enum { array_type_id = TIMESTAMPOID+1 };
   static const char* get(value_type& result, const char* data, const char* end)
   {
      result = *reinterpret_cast<const timestamp*>(data);
      result.value = detail::ntoh(result.value);
      return data+sizeof(timestamp);
   }
   static std::pair<const char*, size_t> data(const timestamp& v, std::vector<char>& buffer)
   {
      size_t n = buffer.size();
      detail::push(buffer, v.value);
      return std::make_pair(buffer.data()+n, buffer.size()-n);
   }
};
template<> struct object_traits<timestamptz> : public base_object_traits<timestamptz, TIMESTAMPTZOID>
{
   enum { array_type_id = TIMESTAMPTZOID+1 };
   static const char* get(value_type& result, const char* data, const char* end)
   {
      result = *reinterpret_cast<const timestamptz*>(data);
      result.value = detail::ntoh(result.value);
      return data+sizeof(timestamptz);
   }
   static std::pair<const char*, size_t> data(const timestamptz& v, std::vector<char>& buffer)
   {
      size_t n = buffer.size();
      detail::push(buffer, v.value);
      return std::make_pair(buffer.data() + n, buffer.size() - n);
   }
};
template<> struct object_traits<interval> : public base_object_traits<interval, INTERVALOID>
{
   enum { array_type_id = INTERVALOID+1 };
   static const char* get(value_type& result, const char* data, const char* end)
   {
      const ::interval* value = reinterpret_cast<const ::interval*>(data);
      result.value->time = detail::ntoh(value->time);
      result.value->month = detail::ntoh(value->month);
      return data+sizeof(interval);
   }
   static std::pair<const char*, size_t> data(const interval& v, std::vector<char>& buffer)
   {
      size_t n = buffer.size();
      detail::push(buffer, v.value->time);
      detail::push(buffer, v.value->month);
      return std::make_pair(buffer.data()+n, buffer.size()-n);
   }
};
template<> struct object_traits<date> : public base_object_traits<date, DATEOID>
{
   enum { array_type_id = 1182 };
   static const char* get(value_type& result, const char* data, const char* end)
   {
      result = *reinterpret_cast<const date*>(data);
      result.value = detail::ntoh(result.value);
      return data+sizeof(date);
   }
   static std::pair<const char*, size_t> data(const date& v, std::vector<char>& buffer)
   {
      size_t n=buffer.size();
      detail::push(buffer, v.value);
      return std::make_pair(buffer.data()+n, buffer.size()-n);
   }
};
template<typename T>
struct bytea_traits : public base_object_traits<T, BYTEAOID>
{
   enum { array_type_id = 1001 };
};
template<> struct object_traits<qtl::const_blob_data> : public bytea_traits<qtl::const_blob_data>
{
   static const char* get(value_type& result, const char* data, const char* end)
   {
      result.data = data;
      result.size = end-data;
      return end;
   }
   static std::pair<const char*, size_t> data(const qtl::const_blob_data& v, std::vector<char>& /*buffer*/)
   {
      assert(v.size <= UINT32_MAX);
      return std::make_pair(static_cast<const char*>(v.data), v.size);
   }
};
template<> struct object_traits<qtl::blob_data> : public bytea_traits<qtl::blob_data>
{
   static const char* get(qtl::blob_data& value, const char* data, const char* end)
   {
      if (value.size < end-data)
         throw std::out_of_range("no enough buffer to receive blob data.");
      memcpy(value.data, data, end-data);
      return end;
   }
   static std::pair<const char*, size_t> data(const qtl::blob_data& v, std::vector<char>& /*buffer*/)
   {
      assert(v.size <= UINT32_MAX);
      return std::make_pair(static_cast<char*>(v.data), v.size);
   }
};
template<> struct object_traits<std::vector<uint8_t>> : public bytea_traits<std::vector<uint8_t>>
{
   static const char* get(value_type& result, const char* data, const char* end)
   {
      result.assign(data, end);
      return end;
   }
   static std::pair<const char*, size_t> data(const std::vector<uint8_t>& v, std::vector<char>& /*buffer*/)
   {
      assert(v.size() <= UINT32_MAX);
      return std::make_pair(reinterpret_cast<const char*>(v.data()), v.size());
   }
};
template<> struct object_traits<large_object> : public base_object_traits<large_object, OIDOID>
{
   enum { array_type_id = OIDARRAYOID };
   static value_type get(PGconn* conn, const char* data, const char* end)
   {
      int32_t oid;
      object_traits<int32_t>::get(oid, data, end);
      return large_object(conn, oid, std::ios::in | std::ios::out | std::ios::binary);
   }
   static std::pair<const char*, size_t> data(const large_object& v, std::vector<char>& buffer)
   {
      return object_traits<int32_t>::data(v.oid(), buffer);
   }
};
template<typename T, Oid id>
struct vector_traits : public base_object_traits<std::vector<T>, id>
{
   typedef typename base_object_traits<std::vector<T>, id>::value_type value_type;
   static const char* get(value_type& result, const char* data, const char* end)
   {
      if (end - data < sizeof(array_header))
         throw std::overflow_error("insufficient data left in message");
      array_header header = *reinterpret_cast<const array_header*>(data);
      detail::ntoh_inplace(header.ndim);
      detail::ntoh_inplace(header.flags);
      detail::ntoh_inplace(header.elemtype);
      detail::ntoh_inplace(header.dims[0].length);
      detail::ntoh_inplace(header.dims[0].lower_bound);
      if (header.ndim != 1 || !object_traits<T>::is_match(header.elemtype))
         throw std::bad_cast();
      data += sizeof(array_header);
      result.reserve(header.dims[0].length);
      for (int32_t i = 0; i != header.dims[0].length; i++)
      {
         int32_t size;
         T value;
         data = detail::pop(data, size);
         if (end - data < size)
            throw std::overflow_error("insufficient data left in message");
         data = object_traits<T>::get(value, data, data + size);
         if (data > end)
            throw std::overflow_error("insufficient data left in message");
         result.push_back(value);
      }
      return data;
   }
   static std::pair<const char*, size_t> data(const std::vector<T>& v, std::vector<char>& buffer)
   {
      assert(v.size() <= INT32_MAX);
      size_t n = buffer.size();
      buffer.resize(n+sizeof(array_header));
      array_header* header = reinterpret_cast<array_header*>(buffer.data()+n);
      header->ndim = detail::hton(1);
      header->flags = detail::hton(0);
      header->elemtype = detail::hton(static_cast<int32_t>(object_traits<T>::type_id));
      header->dims[0].length = detail::hton(static_cast<int32_t>(v.size()));
      header->dims[0].lower_bound = detail::hton(1);
      std::vector<char> temp;
      for (const T& e : v)
      {
         std::pair<const char*, size_t> blob = object_traits<T>::data(e, temp);
         detail::push(buffer, static_cast<int32_t>(blob.second));
         buffer.insert(buffer.end(), blob.first, blob.first + blob.second);
      }
      return std::make_pair(buffer.data()+n, buffer.size()-n);
   }
};
template<typename Iterator, Oid id>
struct iterator_traits : public base_object_traits<Iterator, id>
{
   static const char* get(Iterator first, Iterator last, const char* data, const char* end)
   {
      if (end - data < sizeof(array_header))
         throw std::overflow_error("insufficient data left in message");
      array_header header = *reinterpret_cast<const array_header*>(data);
      detail::ntoh_inplace(header.ndim);
      detail::ntoh_inplace(header.flags);
      detail::ntoh_inplace(header.elemtype);
      detail::ntoh_inplace(header.dims[0].length);
      detail::ntoh_inplace(header.dims[0].lower_bound);
      if (header.ndim != 1 || !object_traits<typename std::iterator_traits<Iterator>::value_type>::is_match(header.elemtype))
         throw std::bad_cast();
      data += sizeof(array_header);
      if (std::distance(first, last) < header.dims[0].length)
         throw std::out_of_range("length of array out of range");
      Iterator it = first;
      for (int32_t i = 0; i != header.dims[0].length; i++, it++)
      {
         int32_t size;
         data = detail::pop(data, size);
         if (end - data < size)
            throw std::overflow_error("insufficient data left in message");
         data = object_traits<typename std::iterator_traits<Iterator>::value_type>::get(*it, data, data + size);
         if (data >= end)
            throw std::overflow_error("insufficient data left in message");
      }
      return data;
   }
   static std::pair<const char*, size_t> data(Iterator first, Iterator last, std::vector<char>& buffer)
   {
      assert(std::distance(first, last) <= INT32_MAX);
      size_t n = buffer.size();
      buffer.resize(n + sizeof(array_header));
      array_header* header = reinterpret_cast<array_header*>(buffer.data() + n);
      header->ndim = detail::hton(1);
      header->flags = detail::hton(0);
      header->elemtype = detail::hton(static_cast<int32_t>(object_traits<typename std::iterator_traits<Iterator>::value_type>::type_id));
      header->dims[0].length = detail::hton(static_cast<int32_t>(std::distance(first, last)));
      header->dims[0].lower_bound = detail::hton(1);
      std::vector<char> temp;
      for (Iterator it=first; it!=last; it++)
      {
         std::pair<const char*, size_t> blob = object_traits<typename std::iterator_traits<Iterator>::value_type>::data(*it, temp);
         detail::push(buffer, static_cast<int32_t>(blob.second));
         buffer.insert(buffer.end(), blob.first, blob.first + blob.second);
      }
      return std::make_pair(buffer.data() + n, buffer.size() - n);
   }
};
template<typename Iterator, Oid id>
struct range_traits : public base_object_traits<std::pair<Iterator, Iterator>, id>
{
   static const char* get(std::pair<Iterator, Iterator>& result, const char* data, const char* end)
   {
      return iterator_traits<Iterator, id>::get(result.first, result.second, data, end);
   }
   static std::pair<const char*, size_t> data(const std::pair<Iterator, Iterator>& v, std::vector<char>& buffer)
   {
      return iterator_traits<Iterator, id>::data(v.first, v.second, buffer);
   }
};
template<typename T>
struct object_traits<std::vector<T>> : public vector_traits<T, object_traits<T>::array_type_id>
{
};
template<typename Iterator>
struct object_traits<std::pair<typename std::enable_if<std::is_object<typename std::iterator_traits<Iterator>::value_type>::value, Iterator>::type, Iterator>> :
   public range_traits<Iterator, object_traits<typename std::iterator_traits<Iterator>::value_type>::array_type_id>
{
};
template<typename T, size_t N, Oid id>
struct carray_traits : public base_object_traits<T(&)[N], id>
{
   static const char* get(T (&result)[N], const char* data, const char* end)
   {
      return iterator_traits<T*, id>::get(std::begin(result), std::end(result), data, end);
   }
   static std::pair<const char*, size_t> data(const T (&v)[N], std::vector<char>& buffer)
   {
      return iterator_traits<const T*, id>::data(std::begin(v), std::end(v), buffer);
   }
};
template<typename T, size_t N, Oid id>
struct array_traits : public base_object_traits<std::array<T, N>, id>
{
   static const char* get(std::array<T, N>& result, const char* data, const char* end)
   {
      return iterator_traits<T*, id>::get(std::begin(result), std::end(result), data, end);
   }
   static std::pair<const char*, size_t> data(const std::array<T, N>& v, std::vector<char>& buffer)
   {
      return iterator_traits<T*, id>::data(std::begin(v), std::end(v), buffer);
   }
};
template<typename T, size_t N> struct object_traits<T (&)[N]> : public carray_traits<T, N, object_traits<T>::array_type_id>
{
};
template<typename T, size_t N> struct object_traits<std::array<T, N>> : public array_traits<T, N, object_traits<T>::array_type_id>
{
};
namespace detail
{
   struct field_header
   {
      Oid type;
      int32_t length;
   };
   template<typename Type>
   static const char* get_field(Type& field, const char* data, const char* end)
   {
      field_header header = *reinterpret_cast<const field_header*>(data);
      detail::ntoh_inplace(header.type);
      detail::ntoh_inplace(header.length);
      data += sizeof(field_header);
      if (end - data < header.length)
         throw std::overflow_error("insufficient data left in message");
      return object_traits<Type>::get(field, data, data + header.length);
   }
   template<typename Tuple, size_t N>
   struct get_field_helper
   {
      const char* operator()(Tuple& result, const char* data, const char* end)
      {
         if (end - data < sizeof(field_header))
            throw std::overflow_error("insufficient data left in message");
         auto& field = std::get<std::tuple_size<Tuple>::value - N>(result);
         data = get_field(field, data, end);
         get_field_helper<Tuple, N - 1>()(result, data, end);
         return data;
      }
   };
   template<typename Tuple>
   struct get_field_helper<Tuple, 1>
   {
      const char* operator()(Tuple& result, const char* data, const char* end)
      {
         if (end - data < sizeof(field_header))
            throw std::overflow_error("insufficient data left in message");
         auto& field = std::get<std::tuple_size<Tuple>::value - 1>(result);
         return get_field(field, data, end);
      }
   };
   template<typename Type>
   static void push_field(const Type& field, std::vector<char>& buffer)
   {
      std::vector<char> temp;
      detail::push(buffer, static_cast<int32_t>(object_traits<Type>::type_id));
      auto result = object_traits<Type>::data(field, temp);
      detail::push(buffer, static_cast<int32_t>(result.second));
      buffer.insert(buffer.end(), result.first, result.first + result.second);
   }
   template<typename Tuple, size_t N>
   struct push_field_helper
   {
      void operator()(const Tuple& data, std::vector<char>& buffer)
      {
         const auto& field = std::get<std::tuple_size<Tuple>::value - N>(data);
         push_field(field, buffer);
         push_field_helper<Tuple, N - 1>()(data, buffer);
      }
   };
   template<typename Tuple>
   struct push_field_helper<Tuple, 1>
   {
      void operator()(const Tuple& data, std::vector<char>& buffer)
      {
         const auto& field = std::get<std::tuple_size<Tuple>::value - 1>(data);
         push_field(field, buffer);
      }
   };
   template<typename Tuple>
   static const char* get_fields(Tuple& result, const char* data, const char* end)
   {
      return get_field_helper<Tuple, std::tuple_size<Tuple>::value>()(result, data, end);
   }
   template<typename Tuple>
   static void push_fields(const Tuple& data, std::vector<char>& buffer)
   {
      push_field_helper<Tuple, std::tuple_size<Tuple>::value>()(data, buffer);
   }
}
template<typename Tuple, Oid id>
struct tuple_traits : public base_object_traits<Tuple, id>
{
   typedef typename base_object_traits<Tuple, id>::value_type value_type;
   static const char* get(value_type& result, const char* data, const char* end)
   {
      int32_t count;
      data = detail::pop(data, count);
      if (data >= end)
         throw std::overflow_error("insufficient data left in message");
      if (std::tuple_size<Tuple>::value != count)
         throw std::bad_cast();
      return detail::get_fields(result, data, end);
   }
   static std::pair<const char*, size_t> data(const value_type& v, std::vector<char>& buffer)
   {
      size_t n = buffer.size();
      detail::push(buffer, static_cast<int32_t>(std::tuple_size<value_type>::value));
      detail::push_fields(v, buffer);
      return std::make_pair(buffer.data()+n, buffer.size()-n);
   }
};
template<typename... Types>
struct object_traits<std::tuple<Types...>> : public tuple_traits<std::tuple<Types...>, InvalidOid>
{
};
template<typename T1, typename T2>
struct object_traits<std::pair<T1, T2>> : public tuple_traits<std::pair<T1, T2>, InvalidOid>
{
};
struct binder
{
   binder() = default;
   template<typename T>
   explicit binder(const T& v)
   {
      m_type = object_traits<T>::value();
      auto pair = object_traits<T>::data(v);
      m_value = pair.first;
      m_length = pair.second;
   }
   binder(const char* data, size_t n, Oid oid)
   {
      m_type = oid;
      m_value = data;
      m_length = n;
   }
   Oid constexpr type() const { return m_type; }
   size_t length() const { return m_length; }
   const char* value() const { return m_value; }
   template<typename T>
   T get()
   {
      if (!object_traits<T>::is_match(m_type))
         throw std::bad_cast();
      T v;
      object_traits<T>::get(v, m_value, m_value + m_length);
      return v;
   }
   template<typename T>
   T get(PGconn* conn)
   {
      if (!object_traits<T>::is_match(m_type))
         throw std::bad_cast();
      return object_traits<T>::get(conn, m_value, m_value + m_length);
   }
   template<typename T>
   void get(T& v)
   {
      if (object_traits<T>::type_id!= InvalidOid && !object_traits<T>::is_match(m_type))
         throw std::bad_cast();
      object_traits<T>::get(v, m_value, m_value + m_length);
   }
   void bind(std::nullptr_t)
   {
      m_value = nullptr;
      m_length = 0;
   }
   void bind(qtl::null)
   {
      bind(nullptr);
   }
   template<typename T, typename = typename std::enable_if<!std::is_array<T>::value>::type>
   void bind(const T& v)
   {
      typedef typename std::decay<T>::type param_type;
      if (m_type!=0 && !object_traits<param_type>::is_match(m_type))
         throw std::bad_cast();
      auto pair = object_traits<param_type>::data(v, m_data);
      m_value = pair.first;
      m_length = pair.second;
   }
   void bind(const char* data, size_t length=0)
   {
      m_value = data;
      if(length>0) m_length = length;
      else m_length = strlen(data);
   }
   template<typename T, size_t N>
   void bind(const T(&v)[N])
   {
      if (m_type != 0 && !object_traits<T(&)[N]>::is_match(m_type))
         throw std::bad_cast();
      auto pair = object_traits<T(&)[N]>::data(v, m_data);
      m_value = pair.first;
      m_length = pair.second;
   }
private:
   Oid m_type;
   const char* m_value;
   size_t m_length;
   std::vector<char> m_data;
};
template<size_t N, size_t I, typename Arg, typename... Other>
inline void make_binder_list_helper(std::array<binder, N>& binders, Arg&& arg, Other&&... other)
{
   binders[I]=binder(arg);
   make_binder_list_helper<N, I+1>(binders, std::forward<Other>(other)...);
}
template<typename... Args>
inline std::array<binder, sizeof...(Args)> make_binder_list(Args&&... args)
{
   std::array<binder, sizeof...(Args)> binders;
   binders.reserve(sizeof...(Args));
   make_binder_list_helper<sizeof...(Args), 0>(binders, std::forward<Args>(args)...);
   return binders;
}
template<typename T>
inline bool in_impl(const T& from, const T& to)
{
   return std::equal_to<T>()(from, to);
}
template<typename T, typename... Ts >
inline bool in_impl(const T& from, const T& to, const Ts&... other)
{
   return std::equal_to<T>()(from, to) ||  in_impl(from, other...);
}
template<typename T, T... values>
inline bool in(const T& v)
{
   return in_impl(v, values...);
}
class result
{
public:
   result(PGresult* res) : m_res(res) { }
   result(const result&) = delete;
   result(result&& src)
   {
      m_res = src.m_res;
      src.m_res = nullptr;
   }
   result& operator=(const result&) = delete;
   result& operator=(result&& src)
   {
      if (this != &src)
      {
         clear();
         m_res = src.m_res;
         src.m_res = nullptr;
      }
      return *this;
   }
   ~result()
   {
      clear();
   }
   PGresult* handle() const { return m_res; }
   operator bool() const { return m_res != nullptr; }
   ExecStatusType status() const
   {
      return PQresultStatus(m_res);
   }
   long long affected_rows() const
   {
      char* result = PQcmdTuples(m_res);
      if (result)
         return strtoll(result, nullptr, 10);
      else
         return 0LL;
   }
   unsigned int get_column_count() const { return PQnfields(m_res); }
   int get_param_count() const
   {
      return PQnparams(m_res);
   }
   Oid get_param_type(int col) const
   {
      return PQparamtype(m_res, col);
   }
   const char* get_column_name(int col) const
   {
      return PQfname(m_res, col);
   }
   int get_column_index(const char* name) const
   {
      return PQfnumber(m_res, name);
   }
   int get_column_length(int col) const
   {
      return PQfsize(m_res, col);
   }
   Oid get_column_type(int col) const
   {
      return PQftype(m_res, col);
   }
   const char* get_value(int row, int col) const
   {
      return PQgetvalue(m_res, row, col);
   }
   bool is_null(int row, int col) const
   {
      return PQgetisnull(m_res, row, col);
   }
   int length(int row, int col) const
   {
      return PQgetlength(m_res, row, col);
   }
   Oid insert_oid() const
   {
      return PQoidValue(m_res);
   }
   template<ExecStatusType... Excepted>
   void verify_error()
   {
      if (m_res)
      {
         ExecStatusType got = status();
         if (! in<ExecStatusType, Excepted...>(got))
            throw error(m_res);
      }
   }
   template<ExecStatusType... Excepted>
   void verify_error(error& e)
   {
      if (m_res)
      {
         ExecStatusType got = status();
         if (!in<ExecStatusType, Excepted...>(got))
            e = error(m_res);
      }
   }
   void clear()
   {
      if (m_res)
      {
         PQclear(m_res);
         m_res = nullptr;
      }
   }
private:
   PGresult* m_res;
};
class base_statement
{
   friend class error;
public:
    explicit base_statement(base_database& db);
    ~base_statement()
    {
    }
    base_statement(const base_statement&) = delete;
    base_statement(base_statement&& src)
       : m_conn(src.m_conn), m_binders(std::move(src.m_binders)), m_res(std::move(src.m_res)), _name(std::move(src._name))
    {
    }
    base_statement& operator=(const base_statement&) = delete;
    base_statement& operator=(base_statement&& src)
    {
       if (this != &src)
       {
          close();
          m_conn = src.m_conn;
          m_binders = std::move(src.m_binders);
          m_res = std::move(src.m_res);
       }
       return *this;
    }
   result& get_result() { return m_res; }
   void close()
   {
      m_res = nullptr;
   }
   uint64_t affetced_rows() const
   {
      return m_res.affected_rows();
   }
   void bind_param(size_t index, const char* param, size_t length)
   {
      m_binders[index].bind(param, length);
   }
   template<class Param>
   void bind_param(size_t index, const Param& param)
   {
      m_binders[index].bind(param);
   }
   template<class Type>
   void bind_field(size_t index, Type&& value)
   {
      if (m_res.is_null(0, static_cast<int>(index)))
         value = Type();
      else
         value = m_binders[index].get<typename std::remove_const<Type>::type>();
   }
   void bind_field(size_t index, char* value, size_t length)
   {
      memcpy(value, m_binders[index].value(), std::min<size_t>(length, m_binders[index].length()));
   }
   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)
   {
      value.assign(m_binders[index].value(), m_binders[index].length());
   }
   template<typename Type>
   void bind_field(size_t index, indicator<Type>&& value)
   {
      if (m_res)
      {
         qtl::bind_field(*this, index, value.data);
         value.is_null = m_res.is_null(0, static_cast<int>(index));
         value.length = m_res.length(0, static_cast<int>(index));
         value.is_truncated = m_binders[index].length() < value.length;
      }
   }
   void bind_field(size_t index, large_object&& value)
   {
      if (m_res.is_null(0, static_cast<int>(index)))
         value.close();
      else
         value = m_binders[index].get<large_object>(m_conn);
   }
   void bind_field(size_t index, blob_data&& value)
   {
      if (m_res.is_null(0, static_cast<int>(index)))
      {
         value.data = nullptr;
         value.size = 0;
      }
      else
      {
         m_binders[index].get(value);
      }
   }
   template<typename... Types>
   void bind_field(size_t index, std::tuple<Types...>&& value)
   {
      if (m_res.is_null(0, static_cast<int>(index)))
         value = std::tuple<Types...>();
      else
         m_binders[index].get(value);
   }
#ifdef _QTL_ENABLE_CPP17
   template<typename T>
   inline void bind_field(size_t index, std::optional<T>&& value)
   {
      if (m_res.is_null(0, static_cast<int>(index)))
      {
         value.reset();
      }
      else
      {
         T v;
         bind_field(index, v);
         value = std::move(v);
      }
   }
   void bind_field(size_t index, std::any&& value)
   {
      if (m_res.is_null(0, static_cast<int>(index)))
      {
         value = nullptr;
      }
      else
      {
         Oid oid = m_res.get_column_type(index);
         switch (oid)
         }
         inline int64_t hton(int64_t v)
         {
         case object_traits<bool>::type_id:
            value = field_cast<bool>(index);
            break;
         case object_traits<char>::type_id:
            value = field_cast<char>(index);
            break;
         case object_traits<float>::type_id:
            value = field_cast<float>(index);
            break;
         case object_traits<double>::type_id:
            value = field_cast<double>(index);
            break;
         case object_traits<int16_t>::type_id:
            value = field_cast<int16_t>(index);
            break;
         case object_traits<int32_t>::type_id:
            value = field_cast<int32_t>(index);
            break;
         case object_traits<int64_t>::type_id:
            value = field_cast<int64_t>(index);
            break;
         case object_traits<Oid>::type_id:
            value = field_cast<Oid>(index);
            break;
         case object_traits<std::string>::type_id:
            value = field_cast<std::string>(index);
            break;
         case object_traits<timestamp>::type_id:
            value = field_cast<timestamp>(index);
            break;
         case object_traits<interval>::type_id:
            value = field_cast<interval>(index);
            break;
         case object_traits<date>::type_id:
            value = field_cast<date>(index);
            break;
         case object_traits<std::vector<uint8_t>>::type_id:
            value = field_cast<std::vector<uint8_t>>(index);
            break;
         case object_traits<bool>::array_type_id:
            value = field_cast<std::vector<bool>>(index);
            break;
         case object_traits<char>::array_type_id:
            value = field_cast<std::vector<char>>(index);
            break;
         case object_traits<float>::array_type_id:
            value = field_cast<std::vector<float>>(index);
            break;
         case object_traits<double>::array_type_id:
            value = field_cast<std::vector<double>>(index);
            break;
         case object_traits<int16_t>::array_type_id:
            value = field_cast<std::vector<int16_t>>(index);
            break;
         case object_traits<int32_t>::array_type_id:
            value = field_cast<std::vector<int32_t>>(index);
            break;
         case object_traits<int64_t>::array_type_id:
            value = field_cast<std::vector<int64_t>>(index);
            break;
         case object_traits<Oid>::array_type_id:
            value = field_cast<std::vector<Oid>>(index);
            break;
         case object_traits<std::string>::array_type_id:
            value = field_cast<std::vector<std::string>>(index);
            break;
         case object_traits<timestamp>::array_type_id:
            value = field_cast<std::vector<timestamp>>(index);
            break;
         case object_traits<interval>::array_type_id:
            value = field_cast<std::vector<interval>>(index);
            break;
         case object_traits<date>::array_type_id:
            value = field_cast<std::vector<date>>(index);
            break;
         default:
            throw postgres::error("Unsupported field type");
            return hton(static_cast<uint64_t>(v));
         }
         template <typename T, typename = typename std::enable_if<std::is_integral<T>::value && !std::is_const<T>::value>::type>
         inline T &hton_inplace(T &v)
         {
            v = hton(v);
            return v;
         }
         template <typename T, typename = typename std::enable_if<std::is_integral<T>::value && !std::is_const<T>::value>::type>
         std::pair<std::vector<char>::iterator, size_t> push(std::vector<char> &buffer, T v)
         {
            v = hton_inplace(v);
            char *data = reinterpret_cast<char *>(&v);
            auto it = buffer.insert(buffer.end(), data, data + sizeof(T));
            return std::make_pair(it, sizeof(T));
         }
         template <typename T, typename = typename std::enable_if<std::is_integral<T>::value && !std::is_const<T>::value>::type>
         const char *pop(const char *data, T &v)
         {
            v = ntoh(*reinterpret_cast<const T *>(data));
            return data + sizeof(T);
         }
      }
   }
#endif // C++17
      class base_database;
      class result;
protected:
   PGconn* m_conn;
   result m_res;
   std::string _name;
   std::vector<binder> m_binders;
   template<ExecStatusType... Excepted>
   void verify_error()
   {
      if (m_res)
         m_res.verify_error<Excepted...>();
      else
         throw error(m_conn);
   }
   void finish(result& res)
   {
      while (res)
      class error : public std::exception
      {
         res = PQgetResult(m_conn);
      }
   }
   template<typename T>
   T field_cast(size_t index)
   {
      T v;
      m_binders[index].get(v);
      return v;
   }
};
class statement : public base_statement
{
public:
   explicit statement(base_database& db) : base_statement(db)
   {
   }
   statement(const statement&) = delete;
   statement(statement&& src) : base_statement(std::move(src))
   {
   }
   ~statement()
   {
      finish(m_res);
      if (!_name.empty())
      {
         std::ostringstream oss;
         oss << "DEALLOCATE " << _name << ";";
         result res = PQexec(m_conn, oss.str().data());
         error e(res.handle());
      }
   }
   void open(const char* command, int nParams=0, const Oid *paramTypes=nullptr)
   {
      _name.resize(sizeof(intptr_t) * 2+1);
      int n = sprintf(const_cast<char*>(_name.data()), "q%p", this);
      _name.resize(n);
      std::transform(_name.begin(), _name.end(), _name.begin(), tolower);
      result res = PQprepare(m_conn, _name.data(), command, nParams, paramTypes);
      res.verify_error<PGRES_COMMAND_OK>();
   }
   template<typename... Types>
   void open(const char* command)
   {
      auto binder_list = make_binder_list(Types()...);
      std::array<Oid, sizeof...(Types)> types;
      std::transform(binder_list.begin(), binder_list.end(), types.begin(), [](const binder& b) {
         return b.type();
      });
      open(command, types.size(), types.data());
   }
   void attach(const char* name)
   {
      result res = PQdescribePrepared(m_conn, name);
      res.verify_error<PGRES_COMMAND_OK>();
      _name = name;
   }
   void execute()
   {
      if(!PQsendQueryPrepared(m_conn, _name.data(), 0, nullptr, nullptr, nullptr, 1))
         throw error(m_conn);
      if (!PQsetSingleRowMode(m_conn))
         throw error(m_conn);
      m_res = PQgetResult(m_conn);
      verify_error<PGRES_COMMAND_OK, PGRES_SINGLE_TUPLE>();
   }
   template<typename Types>
   void execute(const Types& params)
   {
      const size_t count = qtl::params_binder<statement, Types>::size;
      if (count > 0)
      {
         m_binders.resize(count);
         qtl::bind_params(*this, params);
         std::array<const char*, count> values;
         std::array<int, count> lengths;
         std::array<int, count> formats;
         for (size_t i = 0; i != m_binders.size(); i++)
      public:
         error() : m_errmsg() {}
         explicit error(PGconn *conn, PGVerbosity verbosity = PQERRORS_DEFAULT, PGContextVisibility show_context = PQSHOW_CONTEXT_ERRORS)
         {
            values[i] = m_binders[i].value();
            lengths[i] = static_cast<int>(m_binders[i].length());
            formats[i] = 1;
            // PQsetErrorVerbosity(conn, verbosity);
            // PQsetErrorContextVisibility(conn, show_context);
            const char *errmsg = PQerrorMessage(conn);
            if (errmsg)
               m_errmsg = errmsg;
            else
               m_errmsg.clear();
         }
         if (!PQsendQueryPrepared(m_conn, _name.data(), static_cast<int>(m_binders.size()), values.data(), lengths.data(), formats.data(), 1))
            throw error(m_conn);
      }
      else
      {
         if (!PQsendQueryPrepared(m_conn, _name.data(), 0, nullptr, nullptr, nullptr, 1))
            throw error(m_conn);
      }
      if (!PQsetSingleRowMode(m_conn))
         throw error(m_conn);
      m_res = PQgetResult(m_conn);
      verify_error<PGRES_COMMAND_OK, PGRES_SINGLE_TUPLE, PGRES_TUPLES_OK>();
   }
   template<typename Types>
   bool fetch(Types&& values)
   {
      if (m_res)
      {
         ExecStatusType status = m_res.status();
         if (status == PGRES_SINGLE_TUPLE)
         explicit error(PGresult *res)
         {
            int count = m_res.get_column_count();
            if (count > 0)
            const char *errmsg = PQresultErrorMessage(res);
            if (errmsg)
               m_errmsg = errmsg;
            else
               m_errmsg.clear();
         }
         explicit error(const char *errmsg) : m_errmsg(errmsg) {}
         virtual const char *what() const NOEXCEPT override { return m_errmsg.data(); }
         operator bool() const { return !m_errmsg.empty(); }
      protected:
         std::string m_errmsg;
      };
      class timeout : public error
      {
      public:
         timeout()
         {
            m_errmsg = "timeout";
         }
      };
      inline void verify_pgtypes_error(int ret)
      {
         if (ret && errno != 0)
            throw std::system_error(std::error_code(errno, std::generic_category()));
      }
      struct interval
      {
         ::interval *value;
         interval()
         {
            value = PGTYPESinterval_new();
         }
         explicit interval(char *str)
         {
            value = PGTYPESinterval_from_asc(str, nullptr);
         }
         interval(const interval &src) : interval()
         {
            verify_pgtypes_error(PGTYPESinterval_copy(src.value, value));
         }
         interval(interval &&src)
         {
            value = src.value;
            src.value = PGTYPESinterval_new();
         }
         ~interval()
         {
            PGTYPESinterval_free(value);
         }
         std::string to_string() const
         {
            return PGTYPESinterval_to_asc(value);
         }
         interval &operator=(const interval &src)
         {
            if (&src != this)
               verify_pgtypes_error(PGTYPESinterval_copy(src.value, value));
            return *this;
         }
      };
      struct timestamp
      {
         ::timestamp value;
         timestamp() = default;
         static timestamp now()
         {
            timestamp result;
            PGTYPEStimestamp_current(&result.value);
            return result;
         }
         explicit timestamp(char *str)
         {
            value = PGTYPEStimestamp_from_asc(str, nullptr);
            verify_pgtypes_error(1);
         }
         int format(char *str, int n, const char *format) const
         {
            timestamp temp = *this;
            return PGTYPEStimestamp_fmt_asc(&temp.value, str, n, format);
         }
         static timestamp parse(char *str, const char *format)
         {
            timestamp result;
            verify_pgtypes_error(PGTYPEStimestamp_defmt_asc(str, format, &result.value));
            return result;
         }
         std::string to_string() const
         {
            char *str = PGTYPEStimestamp_to_asc(value);
            std::string result = str;
            PGTYPESchar_free(str);
            return result;
         }
         timestamp &operator+=(const interval &span)
         {
            verify_pgtypes_error(PGTYPEStimestamp_add_interval(&value, span.value, &value));
            return *this;
         }
         timestamp &operator-=(const interval &span)
         {
            verify_pgtypes_error(PGTYPEStimestamp_sub_interval(&value, span.value, &value));
            return *this;
         }
      };
      inline timestamp operator+(const timestamp &a, const interval &b)
      {
         timestamp result = a;
         return result += b;
      }
      inline timestamp operator-(const timestamp &a, const interval &b)
      {
         timestamp result = a;
         result -= b;
         return result;
      }
      struct timestamptz
      {
         ::TimestampTz value;
         /*
         timestamptz() = default;
         explicit timestamptz(pg_time_t v)
         {
            value = (TimestampTz)v -
               ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
            value *= USECS_PER_SEC;
         }
         static timestamptz now()
         {
            timestamptz result;
            auto tp = std::chrono::system_clock::now();
            int sec = tp.time_since_epoch().count()*std::nano::num/std::nano::den;
            int usec = tp.time_since_epoch().count()*std::nano::num % std::nano::den;
            result.value = (TimestampTz)sec -
               ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
            result.value = (result.value * USECS_PER_SEC) + usec;
            return result;
         }
         */
      };
      struct date
      {
         ::date value;
         date() = default;
         explicit date(timestamp dt)
         {
            value = PGTYPESdate_from_timestamp(dt.value);
         }
         explicit date(char *str)
         {
            value = PGTYPESdate_from_asc(str, nullptr);
            verify_pgtypes_error(1);
         }
         explicit date(int year, int month, int day)
         {
            int mdy[3] = {month, day, year};
            PGTYPESdate_mdyjul(mdy, &value);
         }
         std::string to_string() const
         {
            char *str = PGTYPESdate_to_asc(value);
            std::string result = str;
            PGTYPESchar_free(str);
            return str;
         }
         static date now()
         {
            date result;
            PGTYPESdate_today(&result.value);
            return result;
         }
         static date parse(char *str, const char *format)
         {
            date result;
            verify_pgtypes_error(PGTYPESdate_defmt_asc(&result.value, format, str));
            return result;
         }
         std::string format(const char *format)
         {
            std::string result;
            result.resize(128);
            verify_pgtypes_error(PGTYPESdate_fmt_asc(value, format, const_cast<char *>(result.data())));
            result.resize(strlen(result.data()));
            return result;
         }
         std::tuple<int, int, int> get_date()
         {
            int mdy[3];
            PGTYPESdate_julmdy(value, mdy);
            return std::make_tuple(mdy[2], mdy[0], mdy[1]);
         }
         int dayofweek()
         {
            return PGTYPESdate_dayofweek(value);
         }
      };
      struct decimal
      {
         ::decimal value;
      };
      struct numeric
      {
         ::numeric *value;
         numeric()
         {
            value = PGTYPESnumeric_new();
         }
         numeric(int v) : numeric()
         {
            verify_pgtypes_error(PGTYPESnumeric_from_int(v, value));
         }
         numeric(long v) : numeric()
         {
            verify_pgtypes_error(PGTYPESnumeric_from_long(v, value));
         }
         numeric(double v) : numeric()
         {
            verify_pgtypes_error(PGTYPESnumeric_from_double(v, value));
         }
         numeric(const decimal &v) : numeric()
         {
            verify_pgtypes_error(PGTYPESnumeric_from_decimal(const_cast<::decimal *>(&v.value), value));
         }
         numeric(const numeric &src) : numeric()
         {
            verify_pgtypes_error(PGTYPESnumeric_copy(src.value, value));
         }
         explicit numeric(const char *str)
         {
            value = PGTYPESnumeric_from_asc(const_cast<char *>(str), nullptr);
         }
         ~numeric()
         {
            PGTYPESnumeric_free(value);
         }
         operator double() const
         {
            double result;
            verify_pgtypes_error(PGTYPESnumeric_to_double(value, &result));
            return result;
         }
         operator int() const
         {
            int result;
            verify_pgtypes_error(PGTYPESnumeric_to_int(value, &result));
            return result;
         }
         operator long() const
         {
            long result;
            verify_pgtypes_error(PGTYPESnumeric_to_long(value, &result));
            return result;
         }
         operator decimal() const
         {
            decimal result;
            verify_pgtypes_error(PGTYPESnumeric_to_decimal(value, &result.value));
            return result;
         }
         int compare(const numeric &other) const
         {
            return PGTYPESnumeric_cmp(value, other.value);
         }
         inline numeric &operator+=(const numeric &b)
         {
            verify_pgtypes_error(PGTYPESnumeric_add(value, b.value, value));
            return *this;
         }
         inline numeric &operator-=(const numeric &b)
         {
            verify_pgtypes_error(PGTYPESnumeric_sub(value, b.value, value));
            return *this;
         }
         inline numeric &operator*=(const numeric &b)
         {
            verify_pgtypes_error(PGTYPESnumeric_mul(value, b.value, value));
            return *this;
         }
         inline numeric &operator/=(const numeric &b)
         {
            verify_pgtypes_error(PGTYPESnumeric_div(value, b.value, value));
            return *this;
         }
         std::string to_string(int dscale = -1) const
         {
            char *str = PGTYPESnumeric_to_asc(value, dscale);
            std::string result = str;
            PGTYPESchar_free(str);
            return result;
         }
      };
      inline numeric operator+(const numeric &a, const numeric &b)
      {
         numeric result;
         verify_pgtypes_error(PGTYPESnumeric_add(a.value, b.value, result.value));
         return result;
      }
      inline numeric operator-(const numeric &a, const numeric &b)
      {
         numeric result;
         verify_pgtypes_error(PGTYPESnumeric_sub(a.value, b.value, result.value));
         return result;
      }
      inline numeric operator*(const numeric &a, const numeric &b)
      {
         numeric result;
         verify_pgtypes_error(PGTYPESnumeric_mul(a.value, b.value, result.value));
         return result;
      }
      inline numeric operator/(const numeric &a, const numeric &b)
      {
         numeric result;
         verify_pgtypes_error(PGTYPESnumeric_div(a.value, b.value, result.value));
         return result;
      }
      inline bool operator==(const numeric &a, const numeric &b)
      {
         return a.compare(b) == 0;
      }
      inline bool operator<(const numeric &a, const numeric &b)
      {
         return a.compare(b) < 0;
      }
      inline bool operator>(const numeric &a, const numeric &b)
      {
         return a.compare(b) > 0;
      }
      inline bool operator<=(const numeric &a, const numeric &b)
      {
         return a.compare(b) <= 0;
      }
      inline bool operator>=(const numeric &a, const numeric &b)
      {
         return a.compare(b) >= 0;
      }
      inline bool operator!=(const numeric &a, const numeric &b)
      {
         return a.compare(b) != 0;
      }
      class large_object : public qtl::blobbuf
      {
      public:
         large_object() : m_conn(nullptr), m_id(InvalidOid), m_fd(-1) {}
         large_object(PGconn *conn, Oid loid, std::ios_base::openmode mode)
         {
            open(conn, loid, mode);
         }
         large_object(const large_object &) = delete;
         large_object(large_object &&src)
         {
            swap(src);
            src.m_conn = nullptr;
            src.m_fd = -1;
         }
         ~large_object()
         {
            close();
         }
         static large_object create(PGconn *conn, Oid loid = InvalidOid)
         {
            Oid oid = lo_create(conn, loid);
            if (oid == InvalidOid)
               throw error(conn);
            return large_object(conn, oid, std::ios::in | std::ios::out | std::ios::binary);
         }
         static large_object load(PGconn *conn, const char *filename, Oid loid = InvalidOid)
         {
            Oid oid = lo_import_with_oid(conn, filename, loid);
            if (oid == InvalidOid)
               throw error(conn);
            return large_object(conn, oid, std::ios::in | std::ios::out | std::ios::binary);
         }
         void save(const char *filename) const
         {
            if (lo_export(m_conn, m_id, filename) < 0)
               throw error(m_conn);
         }
         void unlink()
         {
            close();
            if (lo_unlink(m_conn, m_id) < 0)
               throw error(m_conn);
         }
         large_object &operator=(const large_object &) = delete;
         large_object &operator=(large_object &&src)
         {
            if (this != &src)
            {
               m_binders.resize(count);
               for (int i = 0; i != count; i++)
               {
                  m_binders[i]=binder(m_res.get_value(0, i), m_res.length(0, i),
                     m_res.get_column_type(i));
               }
               qtl::bind_record(*this, std::forward<Types>(values));
               swap(src);
               src.close();
            }
            m_res = PQgetResult(m_conn);
            return true;
            return *this;
         }
         else
         bool is_open() const { return m_fd >= 0; }
         Oid oid() const { return m_id; }
         void open(PGconn *conn, Oid loid, std::ios_base::openmode mode)
         {
            verify_error<PGRES_TUPLES_OK>();
         }
      }
      return false;
   }
            int lomode = 0;
            if (mode & std::ios_base::in)
               lomode |= INV_READ;
            if (mode & std::ios_base::out)
               lomode |= INV_WRITE;
            m_conn = conn;
            m_id = loid;
            m_fd = lo_open(m_conn, loid, lomode);
            if (m_fd < 0)
               throw error(m_conn);
   bool next_result()
   {
      m_res = PQgetResult(m_conn);
      return m_res && m_res.status() == PGRES_SINGLE_TUPLE;
   }
   void reset()
   {
      finish(m_res);
      m_res.clear();
   }
};
class base_database
{
protected:
   base_database()
   {
      m_conn = nullptr;
   }
public:
   typedef postgres::error exception_type;
   base_database(const base_database&) = delete;
   base_database(base_database&& src)
   {
      m_conn = src.m_conn;
      src.m_conn = nullptr;
   }
   ~base_database()
   {
      if (m_conn)
         PQfinish(m_conn);
   }
   base_database& operator=(const base_database&) = delete;
   base_database& operator=(base_database&& src)
   {
      if (this != &src)
      {
         if (m_conn)
            PQfinish(m_conn);
         m_conn = src.m_conn;
         src.m_conn = nullptr;
      }
      return *this;
   }
   const char* errmsg() const
   {
      return PQerrorMessage(m_conn);
   }
   PGconn* handle() { return m_conn; }
   const char* encoding() const
   {
      int encoding = PQclientEncoding(m_conn);
      return (encoding >= 0) ? pg_encoding_to_char(encoding) : nullptr;
   }
   void encoding(const char* encoding)
   {
      if (PQsetClientEncoding(m_conn, encoding))
         throw error(m_conn);
   }
   void trace(FILE* stream)
   {
      PQtrace(m_conn, stream);
   }
   void untrace()
   {
      PQuntrace(m_conn);
   }
   const char* current() const
   {
      return PQdb(m_conn);
   }
   const char* user() const
   {
      return PQuser(m_conn);
   }
   const char* host() const
   {
      return PQhost(m_conn);
   }
   const char* password() const
   {
      return PQpass(m_conn);
   }
   const char* port() const
   {
      return PQport(m_conn);
   }
   const char* options() const
   {
      return PQoptions(m_conn);
   }
   ConnStatusType status() const
   {
      return PQstatus(m_conn);
   }
   PGTransactionStatusType transactionStatus() const
   {
      return PQtransactionStatus(m_conn);
   }
   const char* parameterStatus(const char *paramName) const
   {
      return PQparameterStatus(m_conn, paramName);
   }
   void reset()
   {
      if(status() == CONNECTION_BAD)
         PQreset(m_conn);
   }
   void close()
   {
      PQfinish(m_conn);
      m_conn = nullptr;
   }
protected:
   PGconn* m_conn;
   void throw_exception() { throw postgres::error(m_conn); }
};
class simple_statment : public base_statement
{
public:
   simple_statment(base_database& db, qtl::postgres::result&& res) : base_statement(db)
   {
      m_res = std::move(res);
   }
   template<typename ValueProc>
   void fetch_all(ValueProc& proc)
   {
      int row_count = PQntuples(m_res.handle());
      if (row_count > 0)
      {
         int col_count = m_res.get_column_count();
         m_binders.resize(col_count);
         auto values = qtl::detail::make_values(proc);
         for (int i = 0; i != row_count; i++)
         {
            for (int j = 0; j != col_count; j++)
            m_size = size();
            init_buffer(mode);
            if (mode & std::ios_base::trunc)
            {
               m_binders[j] = binder(m_res.get_value(i, j), m_res.length(i, j),
                  m_res.get_column_type(j));
            }
            qtl::bind_record(*this, std::forward<decltype(values)>(values));
            qtl::detail::apply(proc, std::forward<decltype(values)>(values));
         }
      }
   }
};
class database : public base_database, public qtl::base_database<database, statement>
{
public:
   database() = default;
   bool open(const std::map<std::string, std::string>& params, bool expand_dbname = false)
   {
      std::vector<const char*> keywords(params.size()+1);
      std::vector<const char*> values(params.size()+1);
      for (auto& param : params)
      {
         keywords.push_back(param.first.data());
         values.push_back(param.second.data());
      }
      keywords.push_back(nullptr);
      values.push_back(nullptr);
      m_conn = PQconnectdbParams(keywords.data(), values.data(), expand_dbname);
      return m_conn != nullptr && status()== CONNECTION_OK;
   }
   bool open(const char * conninfo)
   {
      m_conn = PQconnectdb(conninfo);
      return m_conn != nullptr && status() == CONNECTION_OK;
   }
   bool open(const char* host, const char* user, const char* password,
      unsigned short port = 5432, const char* db = "postgres", const char* options = nullptr)
   {
      char port_text[16];
      sprintf(port_text, "%u", port);
      m_conn = PQsetdbLogin(host, port_text, options, nullptr, db, user, password);
      return m_conn != nullptr && status() == CONNECTION_OK;
   }
   statement open_command(const char* query_text, size_t /*text_length*/)
   {
      statement stmt(*this);
      stmt.open(query_text);
      return stmt;
   }
   statement open_command(const char* query_text)
   {
      return open_command(query_text, 0);
   }
   statement open_command(const std::string& query_text)
   {
      return open_command(query_text.data());
   }
   void simple_execute(const char* query_text, uint64_t* paffected = nullptr)
   {
      qtl::postgres::result res(PQexec(m_conn, query_text));
      if (!res) throw_exception();
      res.verify_error<PGRES_COMMAND_OK, PGRES_TUPLES_OK>();
      if (paffected) *paffected = res.affected_rows();
   }
   template<typename ValueProc>
   void simple_query(const char* query_text, ValueProc&& proc)
   {
      qtl::postgres::result res(PQexec(m_conn, query_text));
      if (!res) throw_exception();
      res.verify_error<PGRES_COMMAND_OK, PGRES_TUPLES_OK>();
      if (res.status() == PGRES_TUPLES_OK)
      {
         simple_statment stmt(*this, std::move(res));
         stmt.fetch_all(std::forward<ValueProc>(proc));
      }
   }
   void auto_commit(bool on)
   {
      if(on)
         simple_execute("SET AUTOCOMMIT TO ON");
      else
         simple_execute("SET AUTOCOMMIT TO OFF");
   }
   void begin_transaction()
   {
      simple_execute("BEGIN");
   }
   void rollback()
   {
      simple_execute("ROLLBACK");
   }
   void commit()
   {
      simple_execute("COMMIT");
   }
   bool is_alive()
   {
      qtl::postgres::result res(PQexec(m_conn, ""));
      return res && res.status() == PGRES_COMMAND_OK;
   }
};
inline int event_flags(PostgresPollingStatusType status)
{
   int flags = 0;
   if (status == PGRES_POLLING_READING)
      flags |= event::ef_read;
   else if (status == PGRES_POLLING_WRITING)
      flags |= event::ef_write;
   else if (status == PGRES_POLLING_FAILED)
      flags |= event::ef_exception;
   return flags;
}
class async_connection;
template<typename Handler>
inline void async_wait(qtl::event* event, PGconn* conn, int timeout, Handler&& handler)
{
   int flushed = PQflush(conn);
   if (flushed < 0)
   {
      handler(error(conn));
      return;
   }
   if (flushed == 1)
   {
      event->set_io_handler(qtl::event::ef_read | qtl::event::ef_write, timeout,
         [event, conn, timeout, handler](int flags) mutable {
         if (flags&qtl::event::ef_timeout)
         {
            handler(postgres::timeout());
            return;
         }
         if (flags&qtl::event::ef_read)
         {
            if (!PQconsumeInput(conn))
            {
               handler(error(conn));
               return;
               if (lo_truncate(m_conn, m_fd, 0) < 0)
                  throw error(m_conn);
            }
         }
         if (flags&(qtl::event::ef_read | qtl::event::ef_write | event::ef_exception))
            async_wait(event, conn, timeout, handler);
      });
   }
   else
   {
      event->set_io_handler(qtl::event::ef_read, 10,
         [event, conn, timeout, handler](int flags) mutable {
         if (flags&qtl::event::ef_timeout)
         void close()
         {
            handler(postgres::timeout());
         }
         else if (flags&(qtl::event::ef_read | qtl::event::ef_exception))
         {
            if (PQconsumeInput(conn))
            if (m_fd >= 0)
            {
               if (!PQisBusy(conn))
                  handler(postgres::error());
               else
                  async_wait(event, conn, timeout, handler);
               overflow();
               if (lo_close(m_conn, m_fd) < 0)
                  throw error(m_conn);
               m_fd = -1;
            }
         }
         void flush()
         {
            if (m_fd >= 0)
               overflow();
         }
         size_t size() const
         {
            pg_int64 size = 0;
            if (m_fd >= 0)
            {
               pg_int64 org = lo_tell64(m_conn, m_fd);
               size = lo_lseek64(m_conn, m_fd, 0, SEEK_END);
               lo_lseek64(m_conn, m_fd, org, SEEK_SET);
            }
            return size;
         }
         void resize(size_t n)
         {
            if (m_fd >= 0 && lo_truncate64(m_conn, m_fd, n) < 0)
               throw error(m_conn);
         }
         void swap(large_object &other)
         {
            std::swap(m_conn, other.m_conn);
            std::swap(m_id, other.m_id);
            std::swap(m_fd, other.m_fd);
            qtl::blobbuf::swap(other);
         }
      protected:
         enum
         {
            default_buffer_size = 4096
         };
         virtual bool read_blob(char *buffer, off_type &count, pos_type position) override
         {
            return lo_lseek64(m_conn, m_fd, position, SEEK_SET) >= 0 && lo_read(m_conn, m_fd, buffer, count) > 0;
         }
         virtual void write_blob(const char *buffer, size_t count) override
         {
            if (lo_write(m_conn, m_fd, buffer, count) < 0)
               throw error(m_conn);
         }
      private:
         PGconn *m_conn;
         Oid m_id;
         int m_fd;
      };
      struct array_header
      {
         int32_t ndim;
         int32_t flags;
         int32_t elemtype;
         struct dimension
         {
            int32_t length;
            int32_t lower_bound;
         } dims[1];
      };
      /*
         template<typename T>
         struct oid_traits
         {
            typedef T value_type;
            static Oid type_id;
            static Oid array_type_id; //optional
            static const char* get(value_type& result, const char* begin, const char* end);
            static std::pair<const char*, size_t> data(const T& v, std::vector<char*>& buffer);
         };
      */
      template <typename T, Oid id>
      struct base_object_traits
      {
         typedef T value_type;
         enum
         {
            type_id = id
         };
         static bool is_match(Oid v)
         {
            return v == type_id;
         }
      };
      template <typename T>
      struct object_traits;
#define QTL_POSTGRES_SIMPLE_TRAITS(T, oid, array_oid)                                         \
   template <>                                                                               \
   struct object_traits<T> : public base_object_traits<T, oid>                               \
   {                                                                                         \
      enum                                                                                  \
      {                                                                                     \
         array_type_id = array_oid                                                         \
      };                                                                                    \
      static const char *get(value_type &result, const char *data, const char *end)         \
      {                                                                                     \
         result = *reinterpret_cast<const value_type *>(data);                             \
         return data + sizeof(value_type);                                                 \
      }                                                                                     \
      static std::pair<const char *, size_t> data(const T &v, std::vector<char> & /*data*/) \
      {                                                                                     \
         return std::make_pair(reinterpret_cast<const char *>(&v), sizeof(T));             \
      }                                                                                     \
   };
      QTL_POSTGRES_SIMPLE_TRAITS(bool, BOOLOID, 1000)
      QTL_POSTGRES_SIMPLE_TRAITS(char, CHAROID, 1002)
      QTL_POSTGRES_SIMPLE_TRAITS(float, FLOAT4OID, FLOAT4ARRAYOID)
      QTL_POSTGRES_SIMPLE_TRAITS(double, FLOAT8OID, 1022)
      template <typename T, Oid id, Oid array_id>
      struct integral_traits : public base_object_traits<T, id>
      {
         enum
         {
            array_type_id = array_id
         };
         typedef typename base_object_traits<T, id>::value_type value_type;
         static const char *get(value_type &v, const char *data, const char *end)
         {
            return detail::pop(data, v);
         }
         static std::pair<const char *, size_t> data(value_type v, std::vector<char> &buffer)
         {
            size_t n = buffer.size();
            detail::push(buffer, v);
            return std::make_pair(buffer.data() + n, buffer.size() - n);
         }
      };
      template <>
      struct object_traits<int16_t> : public integral_traits<int16_t, INT2OID, INT2ARRAYOID>
      {
      };
      template <>
      struct object_traits<int32_t> : public integral_traits<int32_t, INT4OID, INT4ARRAYOID>
      {
      };
      template <>
      struct object_traits<int64_t> : public integral_traits<int64_t, INT8OID, 1016>
      {
      };
      template <>
      struct object_traits<Oid> : public integral_traits<Oid, OIDOID, OIDARRAYOID>
      {
      };
      template <typename T>
      struct text_traits : public base_object_traits<T, TEXTOID>
      {
         enum
         {
            array_type_id = TEXTARRAYOID
         };
      };
      template <>
      struct object_traits<const char *> : public text_traits<const char *>
      {
         static bool is_match(Oid v)
         {
            return v == TEXTOID || v == VARCHAROID || v == BPCHAROID;
         }
         static const char *get(const char *&result, const char *data, const char *end)
         {
            result = data;
            return end;
         }
         static std::pair<const char *, size_t> data(const char *v, std::vector<char> & /*buffer*/)
         {
            return std::make_pair(v, strlen(v));
         }
      };
      template <>
      struct object_traits<char *> : public object_traits<const char *>
      {
      };
      template <>
      struct object_traits<std::string> : public text_traits<std::string>
      {
         static bool is_match(Oid v)
         {
            return v == TEXTOID || v == VARCHAROID || v == BPCHAROID;
         }
         static const char *get(value_type &result, const char *data, const char *end)
         {
            result.assign(data, end);
            return end;
         }
         static std::pair<const char *, size_t> data(const std::string &v, std::vector<char> & /*buffer*/)
         {
            return std::make_pair(v.data(), v.size());
         }
      };
      template <>
      struct object_traits<timestamp> : public base_object_traits<timestamp, TIMESTAMPOID>
      {
         enum
         {
            array_type_id = TIMESTAMPOID + 1
         };
         static const char *get(value_type &result, const char *data, const char *end)
         {
            result = *reinterpret_cast<const timestamp *>(data);
            result.value = detail::ntoh(result.value);
            return data + sizeof(timestamp);
         }
         static std::pair<const char *, size_t> data(const timestamp &v, std::vector<char> &buffer)
         {
            size_t n = buffer.size();
            detail::push(buffer, v.value);
            return std::make_pair(buffer.data() + n, buffer.size() - n);
         }
      };
      template <>
      struct object_traits<timestamptz> : public base_object_traits<timestamptz, TIMESTAMPTZOID>
      {
         enum
         {
            array_type_id = TIMESTAMPTZOID + 1
         };
         static const char *get(value_type &result, const char *data, const char *end)
         {
            result = *reinterpret_cast<const timestamptz *>(data);
            result.value = detail::ntoh(result.value);
            return data + sizeof(timestamptz);
         }
         static std::pair<const char *, size_t> data(const timestamptz &v, std::vector<char> &buffer)
         {
            size_t n = buffer.size();
            detail::push(buffer, v.value);
            return std::make_pair(buffer.data() + n, buffer.size() - n);
         }
      };
      template <>
      struct object_traits<interval> : public base_object_traits<interval, INTERVALOID>
      {
         enum
         {
            array_type_id = INTERVALOID + 1
         };
         static const char *get(value_type &result, const char *data, const char *end)
         {
            const ::interval *value = reinterpret_cast<const ::interval *>(data);
            result.value->time = detail::ntoh(value->time);
            result.value->month = detail::ntoh(value->month);
            return data + sizeof(interval);
         }
         static std::pair<const char *, size_t> data(const interval &v, std::vector<char> &buffer)
         {
            size_t n = buffer.size();
            detail::push(buffer, v.value->time);
            detail::push(buffer, v.value->month);
            return std::make_pair(buffer.data() + n, buffer.size() - n);
         }
      };
      template <>
      struct object_traits<date> : public base_object_traits<date, DATEOID>
      {
         enum
         {
            array_type_id = 1182
         };
         static const char *get(value_type &result, const char *data, const char *end)
         {
            result = *reinterpret_cast<const date *>(data);
            result.value = detail::ntoh(result.value);
            return data + sizeof(date);
         }
         static std::pair<const char *, size_t> data(const date &v, std::vector<char> &buffer)
         {
            size_t n = buffer.size();
            detail::push(buffer, v.value);
            return std::make_pair(buffer.data() + n, buffer.size() - n);
         }
      };
      template <typename T>
      struct bytea_traits : public base_object_traits<T, BYTEAOID>
      {
         enum
         {
            array_type_id = 1001
         };
      };
      template <>
      struct object_traits<qtl::const_blob_data> : public bytea_traits<qtl::const_blob_data>
      {
         static const char *get(value_type &result, const char *data, const char *end)
         {
            result.data = data;
            result.size = end - data;
            return end;
         }
         static std::pair<const char *, size_t> data(const qtl::const_blob_data &v, std::vector<char> & /*buffer*/)
         {
            assert(v.size <= UINT32_MAX);
            return std::make_pair(static_cast<const char *>(v.data), v.size);
         }
      };
      template <>
      struct object_traits<qtl::blob_data> : public bytea_traits<qtl::blob_data>
      {
         static const char *get(qtl::blob_data &value, const char *data, const char *end)
         {
            if (value.size < end - data)
               throw std::out_of_range("no enough buffer to receive blob data.");
            memcpy(value.data, data, end - data);
            return end;
         }
         static std::pair<const char *, size_t> data(const qtl::blob_data &v, std::vector<char> & /*buffer*/)
         {
            assert(v.size <= UINT32_MAX);
            return std::make_pair(static_cast<char *>(v.data), v.size);
         }
      };
      template <>
      struct object_traits<std::vector<uint8_t>> : public bytea_traits<std::vector<uint8_t>>
      {
         static const char *get(value_type &result, const char *data, const char *end)
         {
            result.assign(data, end);
            return end;
         }
         static std::pair<const char *, size_t> data(const std::vector<uint8_t> &v, std::vector<char> & /*buffer*/)
         {
            assert(v.size() <= UINT32_MAX);
            return std::make_pair(reinterpret_cast<const char *>(v.data()), v.size());
         }
      };
      template <>
      struct object_traits<large_object> : public base_object_traits<large_object, OIDOID>
      {
         enum
         {
            array_type_id = OIDARRAYOID
         };
         static value_type get(PGconn *conn, const char *data, const char *end)
         {
            int32_t oid;
            object_traits<int32_t>::get(oid, data, end);
            return large_object(conn, oid, std::ios::in | std::ios::out | std::ios::binary);
         }
         static std::pair<const char *, size_t> data(const large_object &v, std::vector<char> &buffer)
         {
            return object_traits<int32_t>::data(v.oid(), buffer);
         }
      };
      template <typename T, Oid id>
      struct vector_traits : public base_object_traits<std::vector<T>, id>
      {
         typedef typename base_object_traits<std::vector<T>, id>::value_type value_type;
         static const char *get(value_type &result, const char *data, const char *end)
         {
            if (end - data < sizeof(array_header))
               throw std::overflow_error("insufficient data left in message");
            array_header header = *reinterpret_cast<const array_header *>(data);
            detail::ntoh_inplace(header.ndim);
            detail::ntoh_inplace(header.flags);
            detail::ntoh_inplace(header.elemtype);
            detail::ntoh_inplace(header.dims[0].length);
            detail::ntoh_inplace(header.dims[0].lower_bound);
            if (header.ndim != 1 || !object_traits<T>::is_match(header.elemtype))
               throw std::bad_cast();
            data += sizeof(array_header);
            result.reserve(header.dims[0].length);
            for (int32_t i = 0; i != header.dims[0].length; i++)
            {
               int32_t size;
               T value;
               data = detail::pop(data, size);
               if (end - data < size)
                  throw std::overflow_error("insufficient data left in message");
               data = object_traits<T>::get(value, data, data + size);
               if (data > end)
                  throw std::overflow_error("insufficient data left in message");
               result.push_back(value);
            }
            return data;
         }
         static std::pair<const char *, size_t> data(const std::vector<T> &v, std::vector<char> &buffer)
         {
            assert(v.size() <= INT32_MAX);
            size_t n = buffer.size();
            buffer.resize(n + sizeof(array_header));
            array_header *header = reinterpret_cast<array_header *>(buffer.data() + n);
            header->ndim = detail::hton(1);
            header->flags = detail::hton(0);
            header->elemtype = detail::hton(static_cast<int32_t>(object_traits<T>::type_id));
            header->dims[0].length = detail::hton(static_cast<int32_t>(v.size()));
            header->dims[0].lower_bound = detail::hton(1);
            std::vector<char> temp;
            for (const T &e : v)
            {
               std::pair<const char *, size_t> blob = object_traits<T>::data(e, temp);
               detail::push(buffer, static_cast<int32_t>(blob.second));
               buffer.insert(buffer.end(), blob.first, blob.first + blob.second);
            }
            return std::make_pair(buffer.data() + n, buffer.size() - n);
         }
      };
      template <typename Iterator, Oid id>
      struct iterator_traits : public base_object_traits<Iterator, id>
      {
         static const char *get(Iterator first, Iterator last, const char *data, const char *end)
         {
            if (end - data < sizeof(array_header))
               throw std::overflow_error("insufficient data left in message");
            array_header header = *reinterpret_cast<const array_header *>(data);
            detail::ntoh_inplace(header.ndim);
            detail::ntoh_inplace(header.flags);
            detail::ntoh_inplace(header.elemtype);
            detail::ntoh_inplace(header.dims[0].length);
            detail::ntoh_inplace(header.dims[0].lower_bound);
            if (header.ndim != 1 || !object_traits<typename std::iterator_traits<Iterator>::value_type>::is_match(header.elemtype))
               throw std::bad_cast();
            data += sizeof(array_header);
            if (std::distance(first, last) < header.dims[0].length)
               throw std::out_of_range("length of array out of range");
            Iterator it = first;
            for (int32_t i = 0; i != header.dims[0].length; i++, it++)
            {
               int32_t size;
               data = detail::pop(data, size);
               if (end - data < size)
                  throw std::overflow_error("insufficient data left in message");
               data = object_traits<typename std::iterator_traits<Iterator>::value_type>::get(*it, data, data + size);
               if (data >= end)
                  throw std::overflow_error("insufficient data left in message");
            }
            return data;
         }
         static std::pair<const char *, size_t> data(Iterator first, Iterator last, std::vector<char> &buffer)
         {
            assert(std::distance(first, last) <= INT32_MAX);
            size_t n = buffer.size();
            buffer.resize(n + sizeof(array_header));
            array_header *header = reinterpret_cast<array_header *>(buffer.data() + n);
            header->ndim = detail::hton(1);
            header->flags = detail::hton(0);
            header->elemtype = detail::hton(static_cast<int32_t>(object_traits<typename std::iterator_traits<Iterator>::value_type>::type_id));
            header->dims[0].length = detail::hton(static_cast<int32_t>(std::distance(first, last)));
            header->dims[0].lower_bound = detail::hton(1);
            std::vector<char> temp;
            for (Iterator it = first; it != last; it++)
            {
               std::pair<const char *, size_t> blob = object_traits<typename std::iterator_traits<Iterator>::value_type>::data(*it, temp);
               detail::push(buffer, static_cast<int32_t>(blob.second));
               buffer.insert(buffer.end(), blob.first, blob.first + blob.second);
            }
            return std::make_pair(buffer.data() + n, buffer.size() - n);
         }
      };
      template <typename Iterator, Oid id>
      struct range_traits : public base_object_traits<std::pair<Iterator, Iterator>, id>
      {
         static const char *get(std::pair<Iterator, Iterator> &result, const char *data, const char *end)
         {
            return iterator_traits<Iterator, id>::get(result.first, result.second, data, end);
         }
         static std::pair<const char *, size_t> data(const std::pair<Iterator, Iterator> &v, std::vector<char> &buffer)
         {
            return iterator_traits<Iterator, id>::data(v.first, v.second, buffer);
         }
      };
      template <typename T>
      struct object_traits<std::vector<T>> : public vector_traits<T, object_traits<T>::array_type_id>
      {
      };
      template <typename Iterator>
      struct object_traits<std::pair<typename std::enable_if<std::is_object<typename std::iterator_traits<Iterator>::value_type>::value, Iterator>::type, Iterator>> : public range_traits<Iterator, object_traits<typename std::iterator_traits<Iterator>::value_type>::array_type_id>
      {
      };
      template <typename T, size_t N, Oid id>
      struct carray_traits : public base_object_traits<T (&)[N], id>
      {
         static const char *get(T (&result)[N], const char *data, const char *end)
         {
            return iterator_traits<T *, id>::get(std::begin(result), std::end(result), data, end);
         }
         static std::pair<const char *, size_t> data(const T (&v)[N], std::vector<char> &buffer)
         {
            return iterator_traits<const T *, id>::data(std::begin(v), std::end(v), buffer);
         }
      };
      template <typename T, size_t N, Oid id>
      struct array_traits : public base_object_traits<std::array<T, N>, id>
      {
         static const char *get(std::array<T, N> &result, const char *data, const char *end)
         {
            return iterator_traits<T *, id>::get(std::begin(result), std::end(result), data, end);
         }
         static std::pair<const char *, size_t> data(const std::array<T, N> &v, std::vector<char> &buffer)
         {
            return iterator_traits<T *, id>::data(std::begin(v), std::end(v), buffer);
         }
      };
      template <typename T, size_t N>
      struct object_traits<T (&)[N]> : public carray_traits<T, N, object_traits<T>::array_type_id>
      {
      };
      template <typename T, size_t N>
      struct object_traits<std::array<T, N>> : public array_traits<T, N, object_traits<T>::array_type_id>
      {
      };
      namespace detail
      {
         struct field_header
         {
            Oid type;
            int32_t length;
         };
         template <typename Type>
         static const char *get_field(Type &field, const char *data, const char *end)
         {
            field_header header = *reinterpret_cast<const field_header *>(data);
            detail::ntoh_inplace(header.type);
            detail::ntoh_inplace(header.length);
            data += sizeof(field_header);
            if (end - data < header.length)
               throw std::overflow_error("insufficient data left in message");
            return object_traits<Type>::get(field, data, data + header.length);
         }
         template <typename Tuple, size_t N>
         struct get_field_helper
         {
            const char *operator()(Tuple &result, const char *data, const char *end)
            {
               if (end - data < sizeof(field_header))
                  throw std::overflow_error("insufficient data left in message");
               auto &field = std::get<std::tuple_size<Tuple>::value - N>(result);
               data = get_field(field, data, end);
               get_field_helper<Tuple, N - 1>()(result, data, end);
               return data;
            }
         };
         template <typename Tuple>
         struct get_field_helper<Tuple, 1>
         {
            const char *operator()(Tuple &result, const char *data, const char *end)
            {
               if (end - data < sizeof(field_header))
                  throw std::overflow_error("insufficient data left in message");
               auto &field = std::get<std::tuple_size<Tuple>::value - 1>(result);
               return get_field(field, data, end);
            }
         };
         template <typename Type>
         static void push_field(const Type &field, std::vector<char> &buffer)
         {
            std::vector<char> temp;
            detail::push(buffer, static_cast<int32_t>(object_traits<Type>::type_id));
            auto result = object_traits<Type>::data(field, temp);
            detail::push(buffer, static_cast<int32_t>(result.second));
            buffer.insert(buffer.end(), result.first, result.first + result.second);
         }
         template <typename Tuple, size_t N>
         struct push_field_helper
         {
            void operator()(const Tuple &data, std::vector<char> &buffer)
            {
               const auto &field = std::get<std::tuple_size<Tuple>::value - N>(data);
               push_field(field, buffer);
               push_field_helper<Tuple, N - 1>()(data, buffer);
            }
         };
         template <typename Tuple>
         struct push_field_helper<Tuple, 1>
         {
            void operator()(const Tuple &data, std::vector<char> &buffer)
            {
               const auto &field = std::get<std::tuple_size<Tuple>::value - 1>(data);
               push_field(field, buffer);
            }
         };
         template <typename Tuple>
         static const char *get_fields(Tuple &result, const char *data, const char *end)
         {
            return get_field_helper<Tuple, std::tuple_size<Tuple>::value>()(result, data, end);
         }
         template <typename Tuple>
         static void push_fields(const Tuple &data, std::vector<char> &buffer)
         {
            push_field_helper<Tuple, std::tuple_size<Tuple>::value>()(data, buffer);
         }
      }
      template <typename Tuple, Oid id>
      struct tuple_traits : public base_object_traits<Tuple, id>
      {
         typedef typename base_object_traits<Tuple, id>::value_type value_type;
         static const char *get(value_type &result, const char *data, const char *end)
         {
            int32_t count;
            data = detail::pop(data, count);
            if (data >= end)
               throw std::overflow_error("insufficient data left in message");
            if (std::tuple_size<Tuple>::value != count)
               throw std::bad_cast();
            return detail::get_fields(result, data, end);
         }
         static std::pair<const char *, size_t> data(const value_type &v, std::vector<char> &buffer)
         {
            size_t n = buffer.size();
            detail::push(buffer, static_cast<int32_t>(std::tuple_size<value_type>::value));
            detail::push_fields(v, buffer);
            return std::make_pair(buffer.data() + n, buffer.size() - n);
         }
      };
      template <typename... Types>
      struct object_traits<std::tuple<Types...>> : public tuple_traits<std::tuple<Types...>, InvalidOid>
      {
      };
      template <typename T1, typename T2>
      struct object_traits<std::pair<T1, T2>> : public tuple_traits<std::pair<T1, T2>, InvalidOid>
      {
      };
      struct binder
      {
         binder() = default;
         template <typename T>
         explicit binder(const T &v)
         {
            m_type = object_traits<T>::value();
            auto pair = object_traits<T>::data(v);
            m_value = pair.first;
            m_length = pair.second;
         }
         binder(const char *data, size_t n, Oid oid)
         {
            m_type = oid;
            m_value = data;
            m_length = n;
         }
         Oid constexpr type() const { return m_type; }
         size_t length() const { return m_length; }
         const char *value() const { return m_value; }
         template <typename T>
         T get()
         {
            if (!object_traits<T>::is_match(m_type))
               throw std::bad_cast();
            T v;
            object_traits<T>::get(v, m_value, m_value + m_length);
            return v;
         }
         template <typename T>
         T get(PGconn *conn)
         {
            if (!object_traits<T>::is_match(m_type))
               throw std::bad_cast();
            return object_traits<T>::get(conn, m_value, m_value + m_length);
         }
         template <typename T>
         void get(T &v)
         {
            if (object_traits<T>::type_id != InvalidOid && !object_traits<T>::is_match(m_type))
               throw std::bad_cast();
            object_traits<T>::get(v, m_value, m_value + m_length);
         }
         void bind(std::nullptr_t)
         {
            m_value = nullptr;
            m_length = 0;
         }
         void bind(qtl::null)
         {
            bind(nullptr);
         }
         template <typename T, typename = typename std::enable_if<!std::is_array<T>::value>::type>
         void bind(const T &v)
         {
            typedef typename std::decay<T>::type param_type;
            if (m_type != 0 && !object_traits<param_type>::is_match(m_type))
               throw std::bad_cast();
            auto pair = object_traits<param_type>::data(v, m_data);
            m_value = pair.first;
            m_length = pair.second;
         }
         void bind(const char *data, size_t length = 0)
         {
            m_value = data;
            if (length > 0)
               m_length = length;
            else
               m_length = strlen(data);
         }
         template <typename T, size_t N>
         void bind(const T (&v)[N])
         {
            if (m_type != 0 && !object_traits<T(&)[N]>::is_match(m_type))
               throw std::bad_cast();
            auto pair = object_traits<T(&)[N]>::data(v, m_data);
            m_value = pair.first;
            m_length = pair.second;
         }
      private:
         Oid m_type;
         const char *m_value;
         size_t m_length;
         std::vector<char> m_data;
      };
      template <size_t N, size_t I, typename Arg, typename... Other>
      inline void make_binder_list_helper(std::array<binder, N> &binders, Arg &&arg, Other &&...other)
      {
         binders[I] = binder(arg);
         make_binder_list_helper<N, I + 1>(binders, std::forward<Other>(other)...);
      }
      template <typename... Args>
      inline std::array<binder, sizeof...(Args)> make_binder_list(Args &&...args)
      {
         std::array<binder, sizeof...(Args)> binders;
         binders.reserve(sizeof...(Args));
         make_binder_list_helper<sizeof...(Args), 0>(binders, std::forward<Args>(args)...);
         return binders;
      }
      template <typename T>
      inline bool in_impl(const T &from, const T &to)
      {
         return std::equal_to<T>()(from, to);
      }
      template <typename T, typename... Ts>
      inline bool in_impl(const T &from, const T &to, const Ts &...other)
      {
         return std::equal_to<T>()(from, to) || in_impl(from, other...);
      }
      template <typename T, T... values>
      inline bool in(const T &v)
      {
         return in_impl(v, values...);
      }
      class result
      {
      public:
         result(PGresult *res) : m_res(res) {}
         result(const result &) = delete;
         result(result &&src)
         {
            m_res = src.m_res;
            src.m_res = nullptr;
         }
         result &operator=(const result &) = delete;
         result &operator=(result &&src)
         {
            if (this != &src)
            {
               clear();
               m_res = src.m_res;
               src.m_res = nullptr;
            }
            return *this;
         }
         ~result()
         {
            clear();
         }
         PGresult *handle() const { return m_res; }
         operator bool() const { return m_res != nullptr; }
         ExecStatusType status() const
         {
            return PQresultStatus(m_res);
         }
         long long affected_rows() const
         {
            char *result = PQcmdTuples(m_res);
            if (result)
               return strtoll(result, nullptr, 10);
            else
               return 0LL;
         }
         unsigned int get_column_count() const { return PQnfields(m_res); }
         int get_param_count() const
         {
            return PQnparams(m_res);
         }
         Oid get_param_type(int col) const
         {
            return PQparamtype(m_res, col);
         }
         const char *get_column_name(int col) const
         {
            return PQfname(m_res, col);
         }
         int get_column_index(const char *name) const
         {
            return PQfnumber(m_res, name);
         }
         int get_column_length(int col) const
         {
            return PQfsize(m_res, col);
         }
         Oid get_column_type(int col) const
         {
            return PQftype(m_res, col);
         }
         const char *get_value(int row, int col) const
         {
            return PQgetvalue(m_res, row, col);
         }
         bool is_null(int row, int col) const
         {
            return PQgetisnull(m_res, row, col);
         }
         int length(int row, int col) const
         {
            return PQgetlength(m_res, row, col);
         }
         Oid insert_oid() const
         {
            return PQoidValue(m_res);
         }
         template <ExecStatusType... Excepted>
         void verify_error()
         {
            if (m_res)
            {
               ExecStatusType got = status();
               if (!in<ExecStatusType, Excepted...>(got))
                  throw error(m_res);
            }
         }
         template <ExecStatusType... Excepted>
         void verify_error(error &e)
         {
            if (m_res)
            {
               ExecStatusType got = status();
               if (!in<ExecStatusType, Excepted...>(got))
                  e = error(m_res);
            }
         }
         void clear()
         {
            if (m_res)
            {
               PQclear(m_res);
               m_res = nullptr;
            }
         }
      private:
         PGresult *m_res;
      };
      class base_statement
      {
         friend class error;
      public:
         explicit base_statement(base_database &db);
         ~base_statement()
         {
         }
         base_statement(const base_statement &) = delete;
         base_statement(base_statement &&src)
            : m_conn(src.m_conn), m_binders(std::move(src.m_binders)), m_res(std::move(src.m_res)), _name(std::move(src._name))
         {
         }
         base_statement &operator=(const base_statement &) = delete;
         base_statement &operator=(base_statement &&src)
         {
            if (this != &src)
            {
               close();
               m_conn = src.m_conn;
               m_binders = std::move(src.m_binders);
               m_res = std::move(src.m_res);
            }
            return *this;
         }
         result &get_result() { return m_res; }
         void close()
         {
            m_res = nullptr;
         }
         uint64_t affetced_rows() const
         {
            return m_res.affected_rows();
         }
         void bind_param(size_t index, const char *param, size_t length)
         {
            m_binders[index].bind(param, length);
         }
         template <class Param>
         void bind_param(size_t index, const Param &param)
         {
            m_binders[index].bind(param);
         }
         template <class Type>
         void bind_field(size_t index, Type &&value)
         {
            if (m_res.is_null(0, static_cast<int>(index)))
               value = Type();
            else
               value = m_binders[index].get<typename std::remove_const<Type>::type>();
         }
         void bind_field(size_t index, char *value, size_t length)
         {
            memcpy(value, m_binders[index].value(), std::min<size_t>(length, m_binders[index].length()));
         }
         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)
         {
            value.assign(m_binders[index].value(), m_binders[index].length());
         }
         template <typename Type>
         void bind_field(size_t index, indicator<Type> &&value)
         {
            if (m_res)
            {
               qtl::bind_field(*this, index, value.data);
               value.is_null = m_res.is_null(0, static_cast<int>(index));
               value.length = m_res.length(0, static_cast<int>(index));
               value.is_truncated = m_binders[index].length() < value.length;
            }
         }
         void bind_field(size_t index, large_object &&value)
         {
            if (m_res.is_null(0, static_cast<int>(index)))
               value.close();
            else
               value = m_binders[index].get<large_object>(m_conn);
         }
         void bind_field(size_t index, blob_data &&value)
         {
            if (m_res.is_null(0, static_cast<int>(index)))
            {
               value.data = nullptr;
               value.size = 0;
            }
            else
            {
               handler(postgres::error(conn));
               m_binders[index].get(value);
            }
         }
         template <typename... Types>
         void bind_field(size_t index, std::tuple<Types...> &&value)
         {
            if (m_res.is_null(0, static_cast<int>(index)))
               value = std::tuple<Types...>();
            else
               m_binders[index].get(value);
         }
#ifdef _QTL_ENABLE_CPP17
         template <typename T>
         inline void bind_field(size_t index, std::optional<T> &&value)
         {
            if (m_res.is_null(0, static_cast<int>(index)))
            {
               value.reset();
            }
            else
            {
               T v;
               bind_field(index, v);
               value = std::move(v);
            }
         }
         void bind_field(size_t index, std::any &&value)
         {
            if (m_res.is_null(0, static_cast<int>(index)))
            {
               value = nullptr;
            }
            else
            {
               Oid oid = m_res.get_column_type(index);
               switch (oid)
               {
               case object_traits<bool>::type_id:
                  value = field_cast<bool>(index);
                  break;
               case object_traits<char>::type_id:
                  value = field_cast<char>(index);
                  break;
               case object_traits<float>::type_id:
                  value = field_cast<float>(index);
                  break;
               case object_traits<double>::type_id:
                  value = field_cast<double>(index);
                  break;
               case object_traits<int16_t>::type_id:
                  value = field_cast<int16_t>(index);
                  break;
               case object_traits<int32_t>::type_id:
                  value = field_cast<int32_t>(index);
                  break;
               case object_traits<int64_t>::type_id:
                  value = field_cast<int64_t>(index);
                  break;
               case object_traits<Oid>::type_id:
                  value = field_cast<Oid>(index);
                  break;
               case object_traits<std::string>::type_id:
                  value = field_cast<std::string>(index);
                  break;
               case object_traits<timestamp>::type_id:
                  value = field_cast<timestamp>(index);
                  break;
               case object_traits<interval>::type_id:
                  value = field_cast<interval>(index);
                  break;
               case object_traits<date>::type_id:
                  value = field_cast<date>(index);
                  break;
               case object_traits<std::vector<uint8_t>>::type_id:
                  value = field_cast<std::vector<uint8_t>>(index);
                  break;
               case object_traits<bool>::array_type_id:
                  value = field_cast<std::vector<bool>>(index);
                  break;
               case object_traits<char>::array_type_id:
                  value = field_cast<std::vector<char>>(index);
                  break;
               case object_traits<float>::array_type_id:
                  value = field_cast<std::vector<float>>(index);
                  break;
               case object_traits<double>::array_type_id:
                  value = field_cast<std::vector<double>>(index);
                  break;
               case object_traits<int16_t>::array_type_id:
                  value = field_cast<std::vector<int16_t>>(index);
                  break;
               case object_traits<int32_t>::array_type_id:
                  value = field_cast<std::vector<int32_t>>(index);
                  break;
               case object_traits<int64_t>::array_type_id:
                  value = field_cast<std::vector<int64_t>>(index);
                  break;
               case object_traits<Oid>::array_type_id:
                  value = field_cast<std::vector<Oid>>(index);
                  break;
               case object_traits<std::string>::array_type_id:
                  value = field_cast<std::vector<std::string>>(index);
                  break;
               case object_traits<timestamp>::array_type_id:
                  value = field_cast<std::vector<timestamp>>(index);
                  break;
               case object_traits<interval>::array_type_id:
                  value = field_cast<std::vector<interval>>(index);
                  break;
               case object_traits<date>::array_type_id:
                  value = field_cast<std::vector<date>>(index);
                  break;
               default:
                  throw postgres::error("Unsupported field type");
               }
            }
         }
#endif // C++17
      protected:
         PGconn *m_conn;
         result m_res;
         std::string _name;
         std::vector<binder> m_binders;
         template <ExecStatusType... Excepted>
         void verify_error()
         {
            if (m_res)
               m_res.verify_error<Excepted...>();
            else
               throw error(m_conn);
         }
         void finish(result &res)
         {
            while (res)
            {
               res = PQgetResult(m_conn);
            }
         }
         template <typename T>
         T field_cast(size_t index)
         {
            T v;
            m_binders[index].get(v);
            return v;
         }
      };
      class statement : public base_statement
      {
      public:
         explicit statement(base_database &db) : base_statement(db)
         {
         }
         statement(const statement &) = delete;
         statement(statement &&src) : base_statement(std::move(src))
         {
         }
         ~statement()
         {
            finish(m_res);
            if (!_name.empty())
            {
               std::ostringstream oss;
               oss << "DEALLOCATE " << _name << ";";
               result res = PQexec(m_conn, oss.str().data());
               error e(res.handle());
            }
         }
         void open(const char *command, int nParams = 0, const Oid *paramTypes = nullptr)
         {
            _name.resize(sizeof(intptr_t) * 2 + 1);
            int n = sprintf(const_cast<char *>(_name.data()), "q%p", this);
            _name.resize(n);
            std::transform(_name.begin(), _name.end(), _name.begin(), tolower);
            result res = PQprepare(m_conn, _name.data(), command, nParams, paramTypes);
            res.verify_error<PGRES_COMMAND_OK>();
         }
         template <typename... Types>
         void open(const char *command)
         {
            auto binder_list = make_binder_list(Types()...);
            std::array<Oid, sizeof...(Types)> types;
            std::transform(binder_list.begin(), binder_list.end(), types.begin(), [](const binder &b)
                        { return b.type(); });
            open(command, types.size(), types.data());
         }
         void attach(const char *name)
         {
            result res = PQdescribePrepared(m_conn, name);
            res.verify_error<PGRES_COMMAND_OK>();
            _name = name;
         }
         void execute()
         {
            if (!PQsendQueryPrepared(m_conn, _name.data(), 0, nullptr, nullptr, nullptr, 1))
               throw error(m_conn);
            if (!PQsetSingleRowMode(m_conn))
               throw error(m_conn);
            m_res = PQgetResult(m_conn);
            verify_error<PGRES_COMMAND_OK, PGRES_SINGLE_TUPLE>();
         }
         template <typename Types>
         void execute(const Types &params)
         {
            const size_t count = qtl::params_binder<statement, Types>::size;
            if (count > 0)
            {
               m_binders.resize(count);
               qtl::bind_params(*this, params);
               std::array<const char *, count> values;
               std::array<int, count> lengths;
               std::array<int, count> formats;
               for (size_t i = 0; i != m_binders.size(); i++)
               {
                  values[i] = m_binders[i].value();
                  lengths[i] = static_cast<int>(m_binders[i].length());
                  formats[i] = 1;
               }
               if (!PQsendQueryPrepared(m_conn, _name.data(), static_cast<int>(m_binders.size()), values.data(), lengths.data(), formats.data(), 1))
                  throw error(m_conn);
            }
            else
            {
               if (!PQsendQueryPrepared(m_conn, _name.data(), 0, nullptr, nullptr, nullptr, 1))
                  throw error(m_conn);
            }
            if (!PQsetSingleRowMode(m_conn))
               throw error(m_conn);
            m_res = PQgetResult(m_conn);
            verify_error<PGRES_COMMAND_OK, PGRES_SINGLE_TUPLE, PGRES_TUPLES_OK>();
         }
         template <typename Types>
         bool fetch(Types &&values)
         {
            if (m_res)
            {
               ExecStatusType status = m_res.status();
               if (status == PGRES_SINGLE_TUPLE)
               {
                  int count = m_res.get_column_count();
                  if (count > 0)
                  {
                     m_binders.resize(count);
                     for (int i = 0; i != count; i++)
                     {
                        m_binders[i] = binder(m_res.get_value(0, i), m_res.length(0, i),
                                         m_res.get_column_type(i));
                     }
                     qtl::bind_record(*this, std::forward<Types>(values));
                  }
                  m_res = PQgetResult(m_conn);
                  return true;
               }
               else
               {
                  verify_error<PGRES_TUPLES_OK>();
               }
            }
            return false;
         }
         bool next_result()
         {
            m_res = PQgetResult(m_conn);
            return m_res && m_res.status() == PGRES_SINGLE_TUPLE;
         }
         void reset()
         {
            finish(m_res);
            m_res.clear();
         }
      };
      class base_database
      {
      protected:
         base_database()
         {
            m_conn = nullptr;
         }
      public:
         typedef postgres::error exception_type;
         base_database(const base_database &) = delete;
         base_database(base_database &&src)
         {
            m_conn = src.m_conn;
            src.m_conn = nullptr;
         }
         ~base_database()
         {
            if (m_conn)
               PQfinish(m_conn);
         }
         base_database &operator=(const base_database &) = delete;
         base_database &operator=(base_database &&src)
         {
            if (this != &src)
            {
               if (m_conn)
                  PQfinish(m_conn);
               m_conn = src.m_conn;
               src.m_conn = nullptr;
            }
            return *this;
         }
         const char *errmsg() const
         {
            return PQerrorMessage(m_conn);
         }
         PGconn *handle() { return m_conn; }
         const char *encoding() const
         {
            int encoding = PQclientEncoding(m_conn);
            return (encoding >= 0) ? pg_encoding_to_char(encoding) : nullptr;
         }
         void encoding(const char *encoding)
         {
            if (PQsetClientEncoding(m_conn, encoding))
               throw error(m_conn);
         }
         void trace(FILE *stream)
         {
            PQtrace(m_conn, stream);
         }
         void untrace()
         {
            PQuntrace(m_conn);
         }
         const char *current() const
         {
            return PQdb(m_conn);
         }
         const char *user() const
         {
            return PQuser(m_conn);
         }
         const char *host() const
         {
            return PQhost(m_conn);
         }
         const char *password() const
         {
            return PQpass(m_conn);
         }
         const char *port() const
         {
            return PQport(m_conn);
         }
         const char *options() const
         {
            return PQoptions(m_conn);
         }
         ConnStatusType status() const
         {
            return PQstatus(m_conn);
         }
         PGTransactionStatusType transactionStatus() const
         {
            return PQtransactionStatus(m_conn);
         }
         const char *parameterStatus(const char *paramName) const
         {
            return PQparameterStatus(m_conn, paramName);
         }
         void reset()
         {
            if (status() == CONNECTION_BAD)
               PQreset(m_conn);
         }
         void close()
         {
            PQfinish(m_conn);
            m_conn = nullptr;
         }
      protected:
         PGconn *m_conn;
         void throw_exception() { throw postgres::error(m_conn); }
      };
      class simple_statment : public base_statement
      {
      public:
         simple_statment(base_database &db, qtl::postgres::result &&res) : base_statement(db)
         {
            m_res = std::move(res);
         }
         template <typename ValueProc>
         void fetch_all(ValueProc &proc)
         {
            int row_count = PQntuples(m_res.handle());
            if (row_count > 0)
            {
               int col_count = m_res.get_column_count();
               m_binders.resize(col_count);
               auto values = qtl::detail::make_values(proc);
               for (int i = 0; i != row_count; i++)
               {
                  for (int j = 0; j != col_count; j++)
                  {
                     m_binders[j] = binder(m_res.get_value(i, j), m_res.length(i, j),
                                      m_res.get_column_type(j));
                  }
                  qtl::bind_record(*this, std::forward<decltype(values)>(values));
                  qtl::detail::apply(proc, std::forward<decltype(values)>(values));
               }
            }
         }
      };
      class database : public base_database, public qtl::base_database<database, statement>
      {
      public:
         database() = default;
         bool open(const std::map<std::string, std::string> &params, bool expand_dbname = false)
         {
            std::vector<const char *> keywords(params.size() + 1);
            std::vector<const char *> values(params.size() + 1);
            for (auto &param : params)
            {
               keywords.push_back(param.first.data());
               values.push_back(param.second.data());
            }
            keywords.push_back(nullptr);
            values.push_back(nullptr);
            m_conn = PQconnectdbParams(keywords.data(), values.data(), expand_dbname);
            return m_conn != nullptr && status() == CONNECTION_OK;
         }
         bool open(const char *conninfo)
         {
            m_conn = PQconnectdb(conninfo);
            return m_conn != nullptr && status() == CONNECTION_OK;
         }
         bool open(const char *host, const char *user, const char *password,
                 unsigned short port = 5432, const char *db = "postgres", const char *options = nullptr)
         {
            char port_text[16];
            sprintf(port_text, "%u", port);
            m_conn = PQsetdbLogin(host, port_text, options, nullptr, db, user, password);
            return m_conn != nullptr && status() == CONNECTION_OK;
         }
         statement open_command(const char *query_text, size_t /*text_length*/)
         {
            statement stmt(*this);
            stmt.open(query_text);
            return stmt;
         }
         statement open_command(const char *query_text)
         {
            return open_command(query_text, 0);
         }
         statement open_command(const std::string &query_text)
         {
            return open_command(query_text.data());
         }
         void simple_execute(const char *query_text, uint64_t *paffected = nullptr)
         {
            qtl::postgres::result res(PQexec(m_conn, query_text));
            if (!res)
               throw_exception();
            res.verify_error<PGRES_COMMAND_OK, PGRES_TUPLES_OK>();
            if (paffected)
               *paffected = res.affected_rows();
         }
         template <typename ValueProc>
         void simple_query(const char *query_text, ValueProc &&proc)
         {
            qtl::postgres::result res(PQexec(m_conn, query_text));
            if (!res)
               throw_exception();
            res.verify_error<PGRES_COMMAND_OK, PGRES_TUPLES_OK>();
            if (res.status() == PGRES_TUPLES_OK)
            {
               simple_statment stmt(*this, std::move(res));
               stmt.fetch_all(std::forward<ValueProc>(proc));
            }
         }
         void auto_commit(bool on)
         {
            if (on)
               simple_execute("SET AUTOCOMMIT TO ON");
            else
               simple_execute("SET AUTOCOMMIT TO OFF");
         }
         void begin_transaction()
         {
            simple_execute("BEGIN");
         }
         void rollback()
         {
            simple_execute("ROLLBACK");
         }
         void commit()
         {
            simple_execute("COMMIT");
         }
         bool is_alive()
         {
            qtl::postgres::result res(PQexec(m_conn, ""));
            return res && res.status() == PGRES_COMMAND_OK;
         }
      };
      inline int event_flags(PostgresPollingStatusType status)
      {
         int flags = 0;
         if (status == PGRES_POLLING_READING)
            flags |= event::ef_read;
         else if (status == PGRES_POLLING_WRITING)
            flags |= event::ef_write;
         else if (status == PGRES_POLLING_FAILED)
            flags |= event::ef_exception;
         return flags;
      }
      class async_connection;
      template <typename Handler>
      inline void async_wait(qtl::event *event, PGconn *conn, int timeout, Handler &&handler)
      {
         int flushed = PQflush(conn);
         if (flushed < 0)
         {
            handler(error(conn));
            return;
         }
         if (flushed == 1)
         {
            event->set_io_handler(qtl::event::ef_read | qtl::event::ef_write, timeout,
                             [event, conn, timeout, handler](int flags) mutable
                             {
                                if (flags & qtl::event::ef_timeout)
                                {
                                   handler(postgres::timeout());
                                   return;
                                }
                                if (flags & qtl::event::ef_read)
                                {
                                   if (!PQconsumeInput(conn))
                                   {
                                      handler(error(conn));
                                      return;
                                   }
                                }
                                if (flags & (qtl::event::ef_read | qtl::event::ef_write | event::ef_exception))
                                   async_wait(event, conn, timeout, handler);
                             });
         }
         else
         {
            handler(postgres::error(conn));
            event->set_io_handler(qtl::event::ef_read, 10,
                             [event, conn, timeout, handler](int flags) mutable
                             {
                                if (flags & qtl::event::ef_timeout)
                                {
                                   handler(postgres::timeout());
                                }
                                else if (flags & (qtl::event::ef_read | qtl::event::ef_exception))
                                {
                                   if (PQconsumeInput(conn))
                                   {
                                      if (!PQisBusy(conn))
                                         handler(postgres::error());
                                      else
                                         async_wait(event, conn, timeout, handler);
                                   }
                                   else
                                   {
                                      handler(postgres::error(conn));
                                   }
                                }
                                else
                                {
                                   handler(postgres::error(conn));
                                }
                             });
         }
      });
   }
}
class async_statement : public base_statement
{
public:
   async_statement(async_connection& db);
   async_statement(async_statement&& src)
      : base_statement(std::move(src)), m_timeout(2)
   {
      m_event = src.m_event;
      m_timeout = src.m_timeout;
      src.m_event = nullptr;
   }
   async_statement& operator=(async_statement&& src)
   {
      if (this != &src)
      {
         base_statement::operator =(std::move(src));
         m_event = src.m_event;
         m_timeout = src.m_timeout;
         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* command, int nParams = 0, const Oid *paramTypes = nullptr)
   {
      _name.resize(sizeof(intptr_t) * 2 + 1);
      int n = sprintf(const_cast<char*>(_name.data()), "q%p", this);
      _name.resize(n);
      std::transform(_name.begin(), _name.end(), _name.begin(), tolower);
      if (PQsendPrepare(m_conn, _name.data(), command, nParams, paramTypes))
      class async_statement : public base_statement
      {
         async_wait([this, handler](error e) mutable {
      public:
         async_statement(async_connection &db);
         async_statement(async_statement &&src)
            : base_statement(std::move(src)), m_timeout(2)
         {
            m_event = src.m_event;
            m_timeout = src.m_timeout;
            src.m_event = nullptr;
         }
         async_statement &operator=(async_statement &&src)
         {
            if (this != &src)
            {
               base_statement::operator=(std::move(src));
               m_event = src.m_event;
               m_timeout = src.m_timeout;
               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 *command, int nParams = 0, const Oid *paramTypes = nullptr)
         {
            _name.resize(sizeof(intptr_t) * 2 + 1);
            int n = sprintf(const_cast<char *>(_name.data()), "q%p", this);
            _name.resize(n);
            std::transform(_name.begin(), _name.end(), _name.begin(), tolower);
            if (PQsendPrepare(m_conn, _name.data(), command, nParams, paramTypes))
            {
               async_wait([this, handler](error e) mutable
                        {
            if (!e)
            {
               m_res = PQgetResult(m_conn);
@@ -2345,73 +2407,72 @@
                     m_res = PQgetResult(m_conn);
               }
            }
            handler(e);
         });
      }
      else
      {
         _name.clear();
         handler(error(m_conn));
      }
   }
   template<typename Handler, typename... Types>
   void open(Handler&& handler, const char* command)
   {
      auto binder_list = make_binder_list(Types()...);
      std::array<Oid, sizeof...(Types)> types;
      std::transform(binder_list.begin(), binder_list.end(), types.begin(), [](const binder& b) {
         return b.type();
      });
      open(std::forward<Handler>(handler), command, types.size(), types.data());
   }
   void close()
   {
      while (m_res)
      {
         m_res = PQgetResult(m_conn);
      }
      if (!_name.empty())
      {
         std::ostringstream oss;
         oss << "DEALLOCATE " << _name << ";";
         result res = PQexec(m_conn, oss.str().data());
         error e;
         res.verify_error<PGRES_COMMAND_OK, PGRES_TUPLES_OK>(e);
         finish(res);
         if(e) throw e;
      }
      base_statement::close();
   }
   template<typename Handler>
   void close(Handler&& handler)
   {
      while (m_res)
      {
         if(PQisBusy(m_conn))
         {
            async_wait([this, handler](const error& e) mutable {
               close(handler);
            });
            handler(e); });
            }
            else
            {
               _name.clear();
               handler(error(m_conn));
            }
         }
         else
         {
            m_res = PQgetResult(m_conn);
         }
      }
      if (!_name.empty() && PQstatus(m_conn) == CONNECTION_OK)
      {
         std::ostringstream oss;
         oss << "DEALLOCATE " << _name << ";";
         bool ok = PQsendQuery(m_conn, oss.str().data());
         if (ok)
         template <typename Handler, typename... Types>
         void open(Handler &&handler, const char *command)
         {
            async_wait([this, handler](postgres::error e) mutable {
            auto binder_list = make_binder_list(Types()...);
            std::array<Oid, sizeof...(Types)> types;
            std::transform(binder_list.begin(), binder_list.end(), types.begin(), [](const binder &b)
                        { return b.type(); });
            open(std::forward<Handler>(handler), command, types.size(), types.data());
         }
         void close()
         {
            while (m_res)
            {
               m_res = PQgetResult(m_conn);
            }
            if (!_name.empty())
            {
               std::ostringstream oss;
               oss << "DEALLOCATE " << _name << ";";
               result res = PQexec(m_conn, oss.str().data());
               error e;
               res.verify_error<PGRES_COMMAND_OK, PGRES_TUPLES_OK>(e);
               finish(res);
               if (e)
                  throw e;
            }
            base_statement::close();
         }
         template <typename Handler>
         void close(Handler &&handler)
         {
            while (m_res)
            {
               if (PQisBusy(m_conn))
               {
                  async_wait([this, handler](const error &e) mutable
                           { close(handler); });
               }
               else
               {
                  m_res = PQgetResult(m_conn);
               }
            }
            if (!_name.empty() && PQstatus(m_conn) == CONNECTION_OK)
            {
               std::ostringstream oss;
               oss << "DEALLOCATE " << _name << ";";
               bool ok = PQsendQuery(m_conn, oss.str().data());
               if (ok)
               {
                  async_wait([this, handler](postgres::error e) mutable
                           {
               if (PQstatus(m_conn) == CONNECTION_OK)
               {
                  result res(PQgetResult(m_conn));
@@ -2425,86 +2486,86 @@
               {
                  _name.clear();
                  handler(error());
               } });
               }
            });
               else
               {
                  handler(error(m_conn));
               }
            }
            else
            {
               _name.clear();
            }
         }
         else
         {
            handler(error(m_conn));
         }
      }
      else
      {
         _name.clear();
      }
   }
   /*
      ExecuteHandler defiens as:
      void handler(const qtl::mysql::error& e, uint64_t affected);
    */
   template<typename ExecuteHandler>
   void execute(ExecuteHandler&& handler)
   {
      if (PQsendQueryPrepared(m_conn, _name.data(), 0, nullptr, nullptr, nullptr, 1) &&
         PQsetSingleRowMode(m_conn))
      {
         async_wait([this, handler](error e) {
         /*
            ExecuteHandler defiens as:
            void handler(const qtl::mysql::error& e, uint64_t affected);
          */
         template <typename ExecuteHandler>
         void execute(ExecuteHandler &&handler)
         {
            if (PQsendQueryPrepared(m_conn, _name.data(), 0, nullptr, nullptr, nullptr, 1) &&
               PQsetSingleRowMode(m_conn))
            {
               async_wait([this, handler](error e)
                        {
            if (!e)
            {
               m_res = PQgetResult(m_conn);
               m_res.verify_error<PGRES_COMMAND_OK, PGRES_SINGLE_TUPLE>(e);
               finish(m_res);
            }
            handler(e);
         });
      }
      else
      {
         handler(error(m_conn));
      }
   }
            handler(e); });
            }
            else
            {
               handler(error(m_conn));
            }
         }
   template<typename Types, typename Handler>
   void execute(const Types& params, Handler&& handler)
   {
      const size_t count = qtl::params_binder<statement, Types>::size;
      if (count > 0)
      {
         m_binders.resize(count);
         qtl::bind_params(*this, params);
         template <typename Types, typename Handler>
         void execute(const Types &params, Handler &&handler)
         {
            const size_t count = qtl::params_binder<statement, Types>::size;
            if (count > 0)
            {
               m_binders.resize(count);
               qtl::bind_params(*this, params);
         std::array<const char*, count> values;
         std::array<int, count> lengths;
         std::array<int, count> formats;
         for (size_t i = 0; i != m_binders.size(); i++)
         {
            values[i] = m_binders[i].value();
            lengths[i] = static_cast<int>(m_binders[i].length());
            formats[i] = 1;
         }
         if (!PQsendQueryPrepared(m_conn, _name.data(), static_cast<int>(m_binders.size()), values.data(), lengths.data(), formats.data(), 1))
         {
            handler(error(m_conn), 0);
            return;
         }
      }
      else
      {
         if (!PQsendQueryPrepared(m_conn, _name.data(), 0, nullptr, nullptr, nullptr, 1))
         {
            handler(error(m_conn), 0);
            return;
         }
      }
      if (!PQsetSingleRowMode(m_conn))
      {
         handler(error(m_conn), 0);
         return;
      }
      if (PQisBusy(m_conn))
      {
         async_wait([this, handler](error e) mutable {
               std::array<const char *, count> values;
               std::array<int, count> lengths;
               std::array<int, count> formats;
               for (size_t i = 0; i != m_binders.size(); i++)
               {
                  values[i] = m_binders[i].value();
                  lengths[i] = static_cast<int>(m_binders[i].length());
                  formats[i] = 1;
               }
               if (!PQsendQueryPrepared(m_conn, _name.data(), static_cast<int>(m_binders.size()), values.data(), lengths.data(), formats.data(), 1))
               {
                  handler(error(m_conn), 0);
                  return;
               }
            }
            else
            {
               if (!PQsendQueryPrepared(m_conn, _name.data(), 0, nullptr, nullptr, nullptr, 1))
               {
                  handler(error(m_conn), 0);
                  return;
               }
            }
            if (!PQsetSingleRowMode(m_conn))
            {
               handler(error(m_conn), 0);
               return;
            }
            if (PQisBusy(m_conn))
            {
               async_wait([this, handler](error e) mutable
                        {
            if (!e)
            {
               m_res = PQgetResult(m_conn);
@@ -2516,34 +2577,34 @@
            else
            {
               handler(e, 0);
            } });
            }
         });
      }
   }
         }
   template<typename Types, typename RowHandler, typename FinishHandler>
   void fetch(Types&& values, RowHandler&& row_handler, FinishHandler&& finish_handler)
   {
      if (m_res)
      {
         ExecStatusType status = m_res.status();
         if (status == PGRES_SINGLE_TUPLE)
         template <typename Types, typename RowHandler, typename FinishHandler>
         void fetch(Types &&values, RowHandler &&row_handler, FinishHandler &&finish_handler)
         {
            int count = m_res.get_column_count();
            if (count > 0)
            if (m_res)
            {
               m_binders.resize(count);
               for (int i = 0; i != count; i++)
               ExecStatusType status = m_res.status();
               if (status == PGRES_SINGLE_TUPLE)
               {
                  m_binders[i] = binder(m_res.get_value(0, i), m_res.length(0, i),
                     m_res.get_column_type(i));
               }
               qtl::bind_record(*this, std::forward<Types>(values));
            }
            row_handler();
            if (PQisBusy(m_conn))
            {
               async_wait([this, &values, row_handler, finish_handler](const error& e) {
                  int count = m_res.get_column_count();
                  if (count > 0)
                  {
                     m_binders.resize(count);
                     for (int i = 0; i != count; i++)
                     {
                        m_binders[i] = binder(m_res.get_value(0, i), m_res.length(0, i),
                                         m_res.get_column_type(i));
                     }
                     qtl::bind_record(*this, std::forward<Types>(values));
                  }
                  row_handler();
                  if (PQisBusy(m_conn))
                  {
                     async_wait([this, &values, row_handler, finish_handler](const error &e)
                              {
                  if (e)
                  {
                     finish_handler(e);
@@ -2552,32 +2613,32 @@
                  {
                     m_res = PQgetResult(m_conn);
                     fetch(std::forward<Types>(values), row_handler, finish_handler);
                  } });
                  }
               });
                  else
                  {
                     m_res = PQgetResult(m_conn);
                     fetch(std::forward<Types>(values), row_handler, finish_handler);
                  }
               }
               else
               {
                  error e;
                  m_res.verify_error<PGRES_TUPLES_OK>(e);
                  finish_handler(e);
               }
            }
            else
            {
               m_res = PQgetResult(m_conn);
               fetch(std::forward<Types>(values), row_handler, finish_handler);
               finish_handler(error());
            }
         }
         else
         {
            error e;
            m_res.verify_error<PGRES_TUPLES_OK>(e);
            finish_handler(e);
         }
      }
      else
      {
         finish_handler(error());
      }
   }
   template<typename Handler>
   void next_result(Handler&& handler)
   {
      async_wait([this, handler](const error& e) {
         template <typename Handler>
         void next_result(Handler &&handler)
         {
            async_wait([this, handler](const error &e)
                     {
         if (e)
         {
            handler(e);
@@ -2586,161 +2647,161 @@
         {
            m_res = PQgetResult(m_conn);
            handler(error());
         } });
         }
      });
   }
private:
   event* m_event;
   int m_timeout;
   template<typename Handler>
   void async_wait(Handler&& handler)
   {
      qtl::postgres::async_wait(m_event, m_conn, m_timeout, std::forward<Handler>(handler));
   }
};
      private:
         event *m_event;
         int m_timeout;
         template <typename Handler>
         void async_wait(Handler &&handler)
         {
            qtl::postgres::async_wait(m_event, m_conn, m_timeout, std::forward<Handler>(handler));
         }
      };
class async_connection : public base_database, public qtl::async_connection<async_connection, async_statement>
{
public:
   async_connection() : m_connect_timeout(2), m_query_timeout(2)
   {
   }
   async_connection(async_connection&& src)
      : base_database(std::move(src)), m_connect_timeout(src.m_connect_timeout), m_query_timeout(src.m_query_timeout)
   {
   }
   async_connection& operator=(async_connection&& src)
   {
      if (this != &src)
      class async_connection : public base_database, public qtl::async_connection<async_connection, async_statement>
      {
         base_database::operator=(std::move(src));
         m_connect_timeout = src.m_connect_timeout;
         m_query_timeout = src.m_query_timeout;
      }
      return *this;
   }
      public:
         async_connection() : m_connect_timeout(2), m_query_timeout(2)
         {
         }
         async_connection(async_connection &&src)
            : base_database(std::move(src)), m_connect_timeout(src.m_connect_timeout), m_query_timeout(src.m_query_timeout)
         {
         }
         async_connection &operator=(async_connection &&src)
         {
            if (this != &src)
            {
               base_database::operator=(std::move(src));
               m_connect_timeout = src.m_connect_timeout;
               m_query_timeout = src.m_query_timeout;
            }
            return *this;
         }
   /*
      OpenHandler defines as:
         void handler(const qtl::postgres::error& e) NOEXCEPT;
   */
   template<typename EventLoop, typename OpenHandler>
   void open(EventLoop& ev, OpenHandler&& handler, const std::map<std::string, std::string>& params, bool expand_dbname = false)
   {
      std::vector<const char*> keywords;
      std::vector<const char*> values;
      keywords.reserve(params.size());
      values.reserve(params.size());
      for (auto& param : params)
      {
         keywords.push_back(param.first.data());
         values.push_back(param.second.data());
      }
      keywords.push_back(nullptr);
      values.push_back(nullptr);
      m_conn = PQconnectStartParams(keywords.data(), values.data(), expand_dbname);
      if (m_conn == nullptr)
         throw std::bad_alloc();
      if (status() == CONNECTION_BAD)
      {
         handler(error(m_conn));
         return;
      }
         /*
            OpenHandler defines as:
               void handler(const qtl::postgres::error& e) NOEXCEPT;
         */
         template <typename EventLoop, typename OpenHandler>
         void open(EventLoop &ev, OpenHandler &&handler, const std::map<std::string, std::string> &params, bool expand_dbname = false)
         {
            std::vector<const char *> keywords;
            std::vector<const char *> values;
            keywords.reserve(params.size());
            values.reserve(params.size());
            for (auto &param : params)
            {
               keywords.push_back(param.first.data());
               values.push_back(param.second.data());
            }
            keywords.push_back(nullptr);
            values.push_back(nullptr);
            m_conn = PQconnectStartParams(keywords.data(), values.data(), expand_dbname);
            if (m_conn == nullptr)
               throw std::bad_alloc();
            if (status() == CONNECTION_BAD)
            {
               handler(error(m_conn));
               return;
            }
      if (PQsetnonblocking(m_conn, true)!=0)
         handler(error(m_conn));
      get_options();
      bind(ev);
      wait_connect(std::forward<OpenHandler>(handler));
   }
            if (PQsetnonblocking(m_conn, true) != 0)
               handler(error(m_conn));
            get_options();
            bind(ev);
            wait_connect(std::forward<OpenHandler>(handler));
         }
   template<typename EventLoop, typename OpenHandler>
   void open(EventLoop& ev, OpenHandler&& handler, const char * conninfo)
   {
      m_conn = PQconnectStart(conninfo);
      if (m_conn == nullptr)
         throw std::bad_alloc();
      if (status() == CONNECTION_BAD)
      {
         handler(error(m_conn));
         return;
      }
         template <typename EventLoop, typename OpenHandler>
         void open(EventLoop &ev, OpenHandler &&handler, const char *conninfo)
         {
            m_conn = PQconnectStart(conninfo);
            if (m_conn == nullptr)
               throw std::bad_alloc();
            if (status() == CONNECTION_BAD)
            {
               handler(error(m_conn));
               return;
            }
      PQsetnonblocking(m_conn, true);
      get_options();
      bind(ev);
      wait_connect(std::forward<OpenHandler>(handler));
   }
            PQsetnonblocking(m_conn, true);
            get_options();
            bind(ev);
            wait_connect(std::forward<OpenHandler>(handler));
         }
   template<typename OpenHandler>
   void reset(OpenHandler&& handler)
   {
      PQresetStart(m_conn);
      wait_reset(std::forward<OpenHandler>(handler));
   }
         template <typename OpenHandler>
         void reset(OpenHandler &&handler)
         {
            PQresetStart(m_conn);
            wait_reset(std::forward<OpenHandler>(handler));
         }
   /*
      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
   {
      bool ok = PQsendQuery(m_conn, query_text);
      if (ok)
      {
         async_wait([this, handler](postgres::error e) mutable {
         /*
            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
         {
            bool ok = PQsendQuery(m_conn, query_text);
            if (ok)
            {
               async_wait([this, handler](postgres::error e) mutable
                        {
            result res(PQgetResult(m_conn));
            res.verify_error<PGRES_COMMAND_OK, PGRES_TUPLES_OK>(e);
            uint64_t affected = res.affected_rows();
            handler(e, affected);
            while (res)
               res = PQgetResult(m_conn);
         });
      }
      else
      {
         handler(error(m_conn), 0);
      }
   }
               res = PQgetResult(m_conn); });
            }
            else
            {
               handler(error(m_conn), 0);
            }
         }
   template<typename Handler>
   void auto_commit(Handler&& handler, bool on) NOEXCEPT
   {
      simple_execute(std::forward<Handler>(handler),
         on ? "SET AUTOCOMMIT TO ON" : "SET AUTOCOMMIT TO OFF");
   }
         template <typename Handler>
         void auto_commit(Handler &&handler, bool on) NOEXCEPT
         {
            simple_execute(std::forward<Handler>(handler),
                        on ? "SET AUTOCOMMIT TO ON" : "SET AUTOCOMMIT TO OFF");
         }
   template<typename Handler>
   void begin_transaction(Handler&& handler) NOEXCEPT
   {
      simple_execute(std::forward<Handler>(handler), "BEGIN");
   }
         template <typename Handler>
         void begin_transaction(Handler &&handler) NOEXCEPT
         {
            simple_execute(std::forward<Handler>(handler), "BEGIN");
         }
   template<typename Handler>
   void rollback(Handler&& handler) NOEXCEPT
   {
      simple_execute(std::forward<Handler>(handler), "ROLLBACK");
   }
         template <typename Handler>
         void rollback(Handler &&handler) NOEXCEPT
         {
            simple_execute(std::forward<Handler>(handler), "ROLLBACK");
         }
   template<typename Handler>
   void commit(Handler&& handler) NOEXCEPT
   {
      simple_execute(std::forward<Handler>(handler), "COMMIT");
   }
         template <typename Handler>
         void commit(Handler &&handler) NOEXCEPT
         {
            simple_execute(std::forward<Handler>(handler), "COMMIT");
         }
   /*
      ResultHandler defines as:
         void result_handler(const qtl::postgres::error& e) NOEXCEPT;
   */
   template<typename RowHandler, typename ResultHandler>
   void simple_query(const char* query, RowHandler&& row_handler, ResultHandler&& result_handler) NOEXCEPT
   {
      bool ok = PQsendQuery(m_conn, query);
      if (ok)
      {
         async_wait([this, row_handler, result_handler](postgres::error e) mutable {
         /*
            ResultHandler defines as:
               void result_handler(const qtl::postgres::error& e) NOEXCEPT;
         */
         template <typename RowHandler, typename ResultHandler>
         void simple_query(const char *query, RowHandler &&row_handler, ResultHandler &&result_handler) NOEXCEPT
         {
            bool ok = PQsendQuery(m_conn, query);
            if (ok)
            {
               async_wait([this, row_handler, result_handler](postgres::error e) mutable
                        {
            result res(PQgetResult(m_conn));
            res.verify_error<PGRES_COMMAND_OK, PGRES_TUPLES_OK>(e);
            if (e)
@@ -2755,144 +2816,140 @@
               stmt.fetch_all(row_handler);
               res = PQgetResult(m_conn);
            }
            result_handler(e, affected);
         });
      }
      else
      {
         result_handler(error(m_conn), 0);
      }
   }
   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 postgres::error& e) mutable {
         handler(e, stmt);
      }, query_text, 0);
   }
   template<typename Handler>
   void is_alive(Handler&& handler) NOEXCEPT
   {
      simple_execute(std::forward<Handler>(handler), "");
   }
   socket_type socket() const NOEXCEPT { return PQsocket(m_conn); }
   int connect_timeout() const { return m_connect_timeout; }
   void connect_timeout(int timeout) { m_connect_timeout = timeout; }
   int query_timeout() const { return m_query_timeout; }
   void query_timeout(int timeout) { m_query_timeout = timeout; }
private:
   int m_connect_timeout;
   int m_query_timeout;
   void get_options()
   {
      PQconninfoOption* options = PQconninfo(m_conn);
      m_connect_timeout = 2;
      for (PQconninfoOption* option = options; option; option++)
      {
         if (strcmp(option->keyword, "connect_timeout") == 0)
         {
            if (option->val)
               m_connect_timeout = atoi(option->val);
            break;
            result_handler(e, affected); });
            }
            else
            {
               result_handler(error(m_conn), 0);
            }
         }
      }
      PQconninfoFree(options);
   }
   template<typename OpenHandler>
   void wait_connect(OpenHandler&& handler) NOEXCEPT
   {
      PostgresPollingStatusType status = PQconnectPoll(m_conn);
      switch (status)
      {
      case PGRES_POLLING_READING:
      case PGRES_POLLING_WRITING:
         m_event_handler->set_io_handler(event_flags(status), m_connect_timeout,
            [this, handler](int flags) mutable {
            if (flags&event::ef_timeout)
         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 postgres::error &e) mutable
                     { handler(e, stmt); }, query_text, 0);
         }
         template <typename Handler>
         void is_alive(Handler &&handler) NOEXCEPT
         {
            simple_execute(std::forward<Handler>(handler), "");
         }
         socket_type socket() const NOEXCEPT { return PQsocket(m_conn); }
         int connect_timeout() const { return m_connect_timeout; }
         void connect_timeout(int timeout) { m_connect_timeout = timeout; }
         int query_timeout() const { return m_query_timeout; }
         void query_timeout(int timeout) { m_query_timeout = timeout; }
      private:
         int m_connect_timeout;
         int m_query_timeout;
         void get_options()
         {
            PQconninfoOption *options = PQconninfo(m_conn);
            m_connect_timeout = 2;
            for (PQconninfoOption *option = options; option; option++)
            {
               handler(postgres::timeout());
               if (strcmp(option->keyword, "connect_timeout") == 0)
               {
                  if (option->val)
                     m_connect_timeout = atoi(option->val);
                  break;
               }
            }
            else if(flags&(event::ef_read|event::ef_write | event::ef_exception))
               wait_connect(std::forward<OpenHandler>(handler));
         });
         break;
      case PGRES_POLLING_FAILED:
         handler(postgres::error(handle()));
         break;
      case PGRES_POLLING_OK:
         //PQsetnonblocking(m_conn, true);
         handler(postgres::error());
      }
   }
            PQconninfoFree(options);
         }
   template<typename OpenHandler>
   void wait_reset(OpenHandler&& handler) NOEXCEPT
   {
      PostgresPollingStatusType status = PQresetPoll(m_conn);
      switch (status)
      {
      case PGRES_POLLING_READING:
      case PGRES_POLLING_WRITING:
         m_event_handler->set_io_handler(event_flags(status), m_connect_timeout,
            [this, handler](int flags) mutable {
            if (flags&event::ef_timeout)
         template <typename OpenHandler>
         void wait_connect(OpenHandler &&handler) NOEXCEPT
         {
            PostgresPollingStatusType status = PQconnectPoll(m_conn);
            switch (status)
            {
               handler(postgres::timeout());
            case PGRES_POLLING_READING:
            case PGRES_POLLING_WRITING:
               m_event_handler->set_io_handler(event_flags(status), m_connect_timeout,
                                       [this, handler](int flags) mutable
                                       {
                                          if (flags & event::ef_timeout)
                                          {
                                             handler(postgres::timeout());
                                          }
                                          else if (flags & (event::ef_read | event::ef_write | event::ef_exception))
                                             wait_connect(std::forward<OpenHandler>(handler));
                                       });
               break;
            case PGRES_POLLING_FAILED:
               handler(postgres::error(handle()));
               break;
            case PGRES_POLLING_OK:
               // PQsetnonblocking(m_conn, true);
               handler(postgres::error());
            }
            else if (flags&(event::ef_read | event::ef_write | event::ef_exception))
               wait_reset(std::forward<OpenHandler>(handler));
         });
         break;
      case PGRES_POLLING_FAILED:
         handler(postgres::error(m_conn));
         break;
      case PGRES_POLLING_OK:
         handler(postgres::error());
         }
         template <typename OpenHandler>
         void wait_reset(OpenHandler &&handler) NOEXCEPT
         {
            PostgresPollingStatusType status = PQresetPoll(m_conn);
            switch (status)
            {
            case PGRES_POLLING_READING:
            case PGRES_POLLING_WRITING:
               m_event_handler->set_io_handler(event_flags(status), m_connect_timeout,
                                       [this, handler](int flags) mutable
                                       {
                                          if (flags & event::ef_timeout)
                                          {
                                             handler(postgres::timeout());
                                          }
                                          else if (flags & (event::ef_read | event::ef_write | event::ef_exception))
                                             wait_reset(std::forward<OpenHandler>(handler));
                                       });
               break;
            case PGRES_POLLING_FAILED:
               handler(postgres::error(m_conn));
               break;
            case PGRES_POLLING_OK:
               handler(postgres::error());
            }
         }
         template <typename Handler>
         void async_wait(Handler &&handler)
         {
            qtl::postgres::async_wait(event(), m_conn, m_query_timeout, std::forward<Handler>(handler));
         }
      };
      inline async_statement::async_statement(async_connection &db)
         : base_statement(static_cast<base_database &>(db))
      {
         m_event = db.event();
         m_timeout = db.query_timeout();
      }
      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>;
      inline base_statement::base_statement(base_database &db) : m_res(nullptr)
      {
         m_conn = db.handle();
         m_res = nullptr;
      }
   }
   template<typename Handler>
   void async_wait(Handler&& handler)
   {
      qtl::postgres::async_wait(event(), m_conn, m_query_timeout, std::forward<Handler>(handler));
   }
};
inline async_statement::async_statement(async_connection& db)
   : base_statement(static_cast<base_database&>(db))
{
   m_event = db.event();
   m_timeout = db.query_timeout();
}
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>;
inline base_statement::base_statement(base_database& db) : m_res(nullptr)
{
   m_conn = db.handle();
   m_res = nullptr;
}
}
}
#endif //_SQL_POSTGRES_H_