From abf49b44cc47f39d6cceb83866f915bc03b7d900 Mon Sep 17 00:00:00 2001
From: Ferenc Szontágh <szf@fsociety.hu>
Date: Mon, 01 Jul 2024 18:08:34 +0000
Subject: [PATCH] reformat files
---
include/qtl_postgres.hpp | 5347 ++++++++++++++++++++++++++++++-----------------------------
1 files changed, 2,702 insertions(+), 2,645 deletions(-)
diff --git a/include/qtl_postgres.hpp b/include/qtl_postgres.hpp
index 5839a55..44687d7 100644
--- a/include/qtl_postgres.hpp
+++ b/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 ¶m)
+ {
+ 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 ¶ms)
+ {
+ 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> ¶ms, bool expand_dbname = false)
+ {
+ std::vector<const char *> keywords(params.size() + 1);
+ std::vector<const char *> values(params.size() + 1);
+ for (auto ¶m : 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 ¶ms, 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> ¶ms, bool expand_dbname = false)
+ {
+ std::vector<const char *> keywords;
+ std::vector<const char *> values;
+ keywords.reserve(params.size());
+ values.reserve(params.size());
+ for (auto ¶m : 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_
-
--
Gitblit v1.9.3