#ifndef _SQL_POSTGRES_H_ #define _SQL_POSTGRES_H_ #pragma once #include #include #include #include #include #include #include #include #include "qtl_common.hpp" #define FRONTEND #include #include #include #include #include #include extern "C" { #include #include #include } #ifdef open #undef open #endif //open #ifdef vsnprintf #undef vsnprintf #endif #ifdef snprintf #undef snprintf #endif #ifdef sprintf #undef sprintf #endif #ifdef vfprintf #undef vfprintf #endif #ifdef fprintf #undef fprintf #endif #ifdef printf #undef printf #endif namespace qtl { namespace postgres { namespace detail { inline int16_t ntoh(int16_t v) { return ntohs(v); } inline uint16_t ntoh(uint16_t v) { return ntohs(v); } inline int32_t ntoh(int32_t v) { return ntohl(v); } inline uint32_t ntoh(uint32_t v) { return ntohl(v); } inline uint64_t ntoh(uint64_t v) { #ifdef _WIN32 return ntohll(v); #else return be64toh(v); #endif } inline int64_t ntoh(int64_t v) { return ntoh(static_cast(v)); } template inline T& ntoh_inplace(typename std::enable_if::value && !std::is_const::value, T>::type& v) { v = ntoh(v); return v; } inline int16_t hton(int16_t v) { return htons(v); } inline uint16_t hton(uint16_t v) { return htons(v); } inline int32_t hton(int32_t v) { return htonl(v); } inline uint32_t hton(uint32_t v) { return htonl(v); } inline uint64_t hton(uint64_t v) { #ifdef _WIN32 return htonll(v); #else return htobe64(v); #endif } inline int64_t hton(int64_t v) { return hton(static_cast(v)); } template inline T& hton_inplace(typename std::enable_if::value && !std::is_const::value>::type& v) { v = hton(v); return v; } } class base_database; class result; class error : public std::exception { public: error() : m_errmsg(nullptr) { } 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(); } virtual const char* what() const NOEXCEPT override { return m_errmsg.data(); } private: std::string m_errmsg; }; 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(result.data()))); result.resize(strlen(result.data())); return result; } std::tuple 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(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; } /* template struct oid_traits { typedef T value_type; static Oid type(); static const value_type& get(const char*); static std::pair data(const T& v); }; */ template struct base_object_traits { typedef T value_type; enum { type = id }; static bool is_match(Oid v) { return v == type; } }; template struct object_traits; #define QTL_POSTGRES_DEFOID(T, oid) \ template<> struct object_traits : public base_object_traits { \ static value_type get(const char* data, size_t n) { return *reinterpret_cast(data); } \ static std::pair data(const T& v, std::vector& /*data*/) { \ return std::make_pair(reinterpret_cast(&v), sizeof(T)); \ } \ }; QTL_POSTGRES_DEFOID(bool, BOOLOID) QTL_POSTGRES_DEFOID(char, CHAROID) QTL_POSTGRES_DEFOID(float, FLOAT4OID) QTL_POSTGRES_DEFOID(double, FLOAT8OID) template struct integral_traits : public base_object_traits { typedef typename base_object_traits::value_type value_type; static value_type get(const char* data, size_t n) { return detail::ntoh(*reinterpret_cast(data)); } static std::pair data(value_type v, std::vector& data) { data.resize(sizeof(value_type)); *reinterpret_cast(data.data()) = detail::hton(v); return std::make_pair(data.data(), data.size()); } }; template<> struct object_traits : public integral_traits { }; template<> struct object_traits : public integral_traits { }; template<> struct object_traits : public integral_traits { }; template<> struct object_traits : public base_object_traits { static bool is_match(Oid v) { return v == TEXTOID || v == VARCHAROID || v == BPCHAROID; } static const char* get(const char* data, size_t n) { return data; } static std::pair data(const char* v, std::vector& /*data*/) { return std::make_pair(v, strlen(v)); } }; template<> struct object_traits : public object_traits { }; template<> struct object_traits : public base_object_traits { static bool is_match(Oid v) { return v == TEXTOID || v == VARCHAROID || v == BPCHAROID; } static value_type get(const char* data, size_t n) { return std::string(data, n); } static std::pair data(const std::string& v, std::vector& /*data*/) { return std::make_pair(v.data(), v.size()); } }; template<> struct object_traits : public base_object_traits { static value_type get(const char* data, size_t n) { value_type result = *reinterpret_cast(data); result.value = detail::ntoh(result.value); return result; } static std::pair data(const timestamp& v, std::vector& data) { data.resize(sizeof(timestamp)); *reinterpret_cast(data.data()) = detail::hton(v.value); return std::make_pair(data.data(), data.size()); } }; template<> struct object_traits : public base_object_traits { static value_type get(const char* data, size_t n) { value_type result = *reinterpret_cast(data); result.value = detail::ntoh(result.value); return result; } static std::pair data(const timestamptz& v, std::vector& data) { data.resize(sizeof(timestamptz)); *reinterpret_cast(data.data()) = detail::hton(v.value); return std::make_pair(data.data(), data.size()); } }; template<> struct object_traits : public base_object_traits { static value_type get(const char* data, size_t n) { interval result; const ::interval* value = reinterpret_cast(data); result.value->time = detail::ntoh(value->time); result.value->month = detail::ntoh(value->month); return std::move(result); } static std::pair data(const interval& v, std::vector& data) { data.resize(sizeof(::interval)); ::interval* value = reinterpret_cast<::interval*>(data.data()); value->time = detail::hton(v.value->time); value->month = detail::hton(v.value->month); return std::make_pair(data.data(), data.size()); } }; template<> struct object_traits : public base_object_traits { static value_type get(const char* data, size_t n) { date result = *reinterpret_cast(data); result.value = detail::ntoh(result.value); return result; } static std::pair data(const date& v, std::vector& data) { data.resize(sizeof(date)); reinterpret_cast(data.data())->value = detail::hton(v.value); return std::make_pair(data.data(), data.size()); } }; struct binder { binder() = default; template explicit binder(const T& v) { m_type = object_traits::value(); auto pair = object_traits::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 T get() { if (!object_traits::is_match(m_type)) throw std::bad_cast(); return object_traits::get(m_value, m_length); } void bind(std::nullptr_t) { m_value = nullptr; m_length = 0; } void bind(qtl::null) { bind(nullptr); } template void bind(const T& v) { typedef typename std::decay::type param_type; if (m_type!=0 && !object_traits::is_match(m_type)) throw std::bad_cast(); auto pair = object_traits::data(v, m_data); m_value = pair.first; m_length = pair.second; } void bind(const char* data, size_t length) { m_value = data; m_length = length; } private: Oid m_type; const char* m_value; size_t m_length; std::vector m_data; }; template inline void make_binder_list_helper(std::array& binders, Arg&& arg, Other&&... other) { binders[I]=binder(arg); make_binder_list_helper(binders, std::forward(other)...); } template inline std::array make_binder_list(Args&&... args) { std::array binders; binders.reserve(sizeof...(Args)); make_binder_list_helper(binders, std::forward(args)...); return binders; } template inline bool in_impl(const T& from, const T& to) { return std::equal_to()(from, to); } template inline bool in_impl(const T& from, const T& to, const Ts&... other) { return std::equal_to()(from, to) || in_impl(from, other...); } template 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 void verify_error() { if (m_res) { ExecStatusType got = status(); if (! in(got)) throw 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)) { } 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 void bind_param(size_t index, const Param& param) { m_binders[index].bind(param); } template void bind_field(size_t index, Type&& value) { value = m_binders[index].get::type>(); } void bind_field(size_t index, char* value, size_t length) { memcpy(value, m_binders[index].value(), std::min(length, m_binders[index].length())); } template void bind_field(size_t index, std::array&& value) { bind_field(index, value.data(), value.size()); } template void bind_field(size_t index, bind_string_helper&& value) { value.assign(m_binders[index].value(), m_binders[index].length()); } template void bind_field(size_t index, indicator&& value) { if (m_res) { qtl::bind_field(*this, index, value.data); value.is_null = m_res.is_null(0, static_cast(index)); value.length = m_res.length(0, static_cast(index)); value.is_truncated = m_binders[index].length() < value.length; } } protected: PGconn* m_conn; result m_res; std::vector m_binders; template void verify_error() { if (m_res) m_res.verify_error(); else throw error(m_conn); } }; 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)), _name(std::move(src._name)) { } ~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(_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(); } template void open(const char* command) { auto binder_list = make_binder_list(Types()...); std::array 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(); _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(); } template void execute(const Types& params) { const size_t count = qtl::params_binder::size; if (count > 0) { m_binders.resize(count); qtl::bind_params(*this, params); std::array values; std::array lengths; std::array formats; for (size_t i = 0; i != m_binders.size(); i++) { values[i] = m_binders[i].value(); lengths[i] = static_cast(m_binders[i].length()); formats[i] = 1; } if (!PQsendQueryPrepared(m_conn, _name.data(), static_cast(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(); } template 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(values)); } m_res = PQgetResult(m_conn); return true; } else { verify_error(); } } 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(); } private: std::string _name; void finish(result& res) { while (res) { res = PQgetResult(m_conn); } } }; class base_database { protected: base_database() { m_conn = nullptr; } public: 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 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); } bool is_alive() { } PGPing ping() { } 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 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(values)); proc(values); } } } }; class database : public base_database, public qtl::base_database { public: database() = default; bool open(const std::map& params, bool expand_dbname = false) { std::vector keywords(params.size()+1); std::vector 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; } void close() { PQfinish(m_conn); m_conn = nullptr; } 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(); if (paffected) *paffected = res.affected_rows(); } template 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(); if (res.status() == PGRES_TUPLES_OK) { simple_statment stmt(*this, std::move(res)); stmt.fetch_all(std::forward(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"); } }; typedef qtl::transaction transaction; template using query_iterator = qtl::query_iterator; template using query_result = qtl::query_result; inline base_statement::base_statement(base_database& db) : m_res(nullptr) { m_conn = db.handle(); m_res = nullptr; } } } #endif //_SQL_POSTGRES_H_