znone
2021-02-02 150e2d1bed201ba1466cde47b21014b0811a22a3
Add support for PostgreSQL
8 files modified
5 files added
1966 ■■■■■ changed files
include/apply_tuple.h 10 ●●●●● patch | view | raw | blame | history
include/qtl_common.hpp 10 ●●●● patch | view | raw | blame | history
include/qtl_mysql.hpp 2 ●●● patch | view | raw | blame | history
include/qtl_postgres.hpp 1420 ●●●●● patch | view | raw | blame | history
include/qtl_sqlite.hpp 4 ●●●● patch | view | raw | blame | history
test/TestPostgres.cpp 230 ●●●●● patch | view | raw | blame | history
test/TestPostgres.h 35 ●●●●● patch | view | raw | blame | history
test/TestSuite.h 1 ●●●● patch | view | raw | blame | history
test/test_mariadb.mak 4 ●●●● patch | view | raw | blame | history
test/test_mysql.mak 4 ●●●● patch | view | raw | blame | history
test/test_postgres.mak 25 ●●●●● patch | view | raw | blame | history
test/vs/test.sln 6 ●●●●● patch | view | raw | blame | history
test/vs/test_postgres/test_postgres.vcproj 215 ●●●●● patch | view | raw | blame | history
include/apply_tuple.h
@@ -6,6 +6,12 @@
#include <type_traits>
#include <utility>
namespace qtl
{
namespace detail
{
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
template <class F, class Tuple>
@@ -58,4 +64,8 @@
#endif // C++17
}
}
#endif //_APPLY_TUPLE_H_
include/qtl_common.hpp
@@ -19,6 +19,7 @@
#include <string>
#include <vector>
#include <functional>
#include <algorithm>
#include "apply_tuple.h"
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
@@ -367,7 +368,7 @@
        typedef bool result_type;
        result_type operator()(F&& f, arg_type&& v)
        {
            apply_tuple(std::forward<F>(f), std::forward<arg_type>(v));
            qtl::detail::apply_tuple(std::forward<F>(f), std::forward<arg_type>(v));
            return true;
        }
    };
@@ -377,7 +378,7 @@
        typedef Ret result_type;
        result_type operator()(F&& f, arg_type&& v)
        {
            return apply_tuple(std::forward<F>(f), std::forward<arg_type>(v));
            return qtl::detail::apply_tuple(std::forward<F>(f), std::forward<arg_type>(v));
        }
    };
@@ -593,7 +594,7 @@
    typedef decltype(values) values_type;
    while(command.fetch(std::forward<values_type>(values)))
    {
        if(!detail::apply(std::forward<ValueProc>(proc), std::forward<values_type>(values)))
        if(!apply(std::forward<ValueProc>(proc), std::forward<values_type>(values)))
            break;
    }
}
@@ -613,6 +614,7 @@
template<typename Command, typename T>
struct params_binder
{
    enum { size = 1 };
    inline void operator()(Command& command, const T& param) const
    {
        qtl::bind_param(command, 0, param);
@@ -622,6 +624,7 @@
template<typename Command, typename... Types>
struct params_binder<Command, std::tuple<Types...>>
{
    enum { size = sizeof...(Types) };
    void operator()(Command& command, const std::tuple<Types...>& params) const
    {
        (detail::bind_helper<Command, std::tuple_size<std::tuple<Types...>>::value, Types...>(command))(params);
@@ -631,6 +634,7 @@
template<typename Command, typename Type1, typename Type2>
struct params_binder<Command, std::pair<Type1, Type2>>
{
    enum { size = 2 };
    void operator()(Command& command, std::pair<Type1, Type2>&& values) const
    {
        qtl::bind_param(command, 0, std::forward<Type1>(values.first));
include/qtl_mysql.hpp
@@ -214,7 +214,7 @@
    virtual ~error() throw() { }
    int code() const throw() { return m_error; }
    operator bool() const { return m_error != 0;  }
    virtual const char* what() const throw() override { return m_errmsg.data(); }
    virtual const char* what() const NOEXCEPT override { return m_errmsg.data(); }
private:
    unsigned int m_error;
    std::string m_errmsg;
include/qtl_postgres.hpp
New file
@@ -0,0 +1,1420 @@
#ifndef _SQL_POSTGRES_H_
#define _SQL_POSTGRES_H_
#pragma once
#include <string>
#include <map>
#include <vector>
#include <array>
#include <exception>
#include <sstream>
#include <chrono>
#include <algorithm>
#include "qtl_common.hpp"
#define FRONTEND
#include <libpq-fe.h>
#include <pgtypes_error.h>
#include <pgtypes_interval.h>
#include <pgtypes_timestamp.h>
#include <pgtypes_numeric.h>
#include <pgtypes_date.h>
extern "C"
{
#include <c.h>
#include <postgres.h>
#include <catalog/pg_type.h>
}
#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<uint64_t>(v));
    }
    template<typename T>
    inline T& ntoh_inplace(typename std::enable_if<std::is_integral<T>::value && !std::is_const<T>::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<uint64_t>(v));
    }
    template<typename T>
    inline T& hton_inplace(typename std::enable_if<std::is_integral<T>::value && !std::is_const<T>::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<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;
}
/*
    template<typename T>
    struct oid_traits
    {
        typedef T value_type;
        static Oid type();
        static const value_type& get(const char*);
        static std::pair<const char*, size_t> data(const T& v);
    };
*/
template<typename T, Oid id>
struct base_object_traits
{
    typedef T value_type;
    enum { type = id };
    static bool is_match(Oid v)
    {
        return v == type;
    }
};
template<typename T>
struct object_traits;
#define QTL_POSTGRES_DEFOID(T, oid) \
template<> struct object_traits<T> : public base_object_traits<T, oid> { \
    static value_type get(const char* data, size_t n) { return *reinterpret_cast<const value_type*>(data); } \
    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_DEFOID(bool, BOOLOID)
QTL_POSTGRES_DEFOID(char, CHAROID)
QTL_POSTGRES_DEFOID(float, FLOAT4OID)
QTL_POSTGRES_DEFOID(double, FLOAT8OID)
template<typename T, Oid id>
struct integral_traits : public base_object_traits<T, id>
{
    typedef typename base_object_traits<T, id>::value_type value_type;
    static value_type get(const char* data, size_t n)
    {
        return detail::ntoh(*reinterpret_cast<const value_type*>(data));
    }
    static std::pair<const char*, size_t> data(value_type v, std::vector<char>& data)
    {
        data.resize(sizeof(value_type));
        *reinterpret_cast<value_type*>(data.data()) = detail::hton(v);
        return std::make_pair(data.data(), data.size());
    }
};
template<> struct object_traits<int16_t> : public integral_traits<int16_t, INT2OID>
{
};
template<> struct object_traits<int32_t> : public integral_traits<int32_t, INT4OID>
{
};
template<> struct object_traits<int64_t> : public integral_traits<int64_t, INT8OID>
{
};
template<> struct object_traits<const char*> : public base_object_traits<const char*, TEXTOID>
{
    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<const char*, size_t> data(const char* v, std::vector<char>& /*data*/)
    {
        return std::make_pair(v, strlen(v));
    }
};
template<> struct object_traits<char*> : public object_traits<const char*>
{
};
template<> struct object_traits<std::string> : public base_object_traits<std::string, TEXTOID>
{
    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<const char*, size_t> data(const std::string& v, std::vector<char>& /*data*/)
    {
        return std::make_pair(v.data(), v.size());
    }
};
template<> struct object_traits<timestamp> : public base_object_traits<timestamp, TIMESTAMPOID>
{
    static value_type get(const char* data, size_t n)
    {
        value_type result = *reinterpret_cast<const timestamp*>(data);
        result.value = detail::ntoh(result.value);
        return result;
    }
    static std::pair<const char*, size_t> data(const timestamp& v, std::vector<char>& data)
    {
        data.resize(sizeof(timestamp));
        *reinterpret_cast<int64_t*>(data.data()) = detail::hton(v.value);
        return std::make_pair(data.data(), data.size());
    }
};
template<> struct object_traits<timestamptz> : public base_object_traits<timestamptz, TIMESTAMPTZOID>
{
    static value_type get(const char* data, size_t n)
    {
        value_type result = *reinterpret_cast<const timestamptz*>(data);
        result.value = detail::ntoh(result.value);
        return result;
    }
    static std::pair<const char*, size_t> data(const timestamptz& v, std::vector<char>& data)
    {
        data.resize(sizeof(timestamptz));
        *reinterpret_cast<int64_t*>(data.data()) = detail::hton(v.value);
        return std::make_pair(data.data(), data.size());
    }
};
template<> struct object_traits<interval> : public base_object_traits<interval, INTERVALOID>
{
    static value_type get(const char* data, size_t n)
    {
        interval result;
        const ::interval* value = reinterpret_cast<const ::interval*>(data);
        result.value->time = detail::ntoh(value->time);
        result.value->month = detail::ntoh(value->month);
        return std::move(result);
    }
    static std::pair<const char*, size_t> data(const interval& v, std::vector<char>& 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<date> : public base_object_traits<date, DATEOID>
{
    static value_type get(const char* data, size_t n)
    {
        date result = *reinterpret_cast<const date*>(data);
        result.value = detail::ntoh(result.value);
        return result;
    }
    static std::pair<const char*, size_t> data(const date& v, std::vector<char>& data)
    {
        data.resize(sizeof(date));
        reinterpret_cast<date*>(data.data())->value = detail::hton(v.value);
        return std::make_pair(data.data(), data.size());
    }
};
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();
        return object_traits<T>::get(m_value, m_length);
    }
    void bind(std::nullptr_t)
    {
        m_value = nullptr;
        m_length = 0;
    }
    void bind(qtl::null)
    {
        bind(nullptr);
    }
    template<typename T>
    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)
    {
        m_value = data;
        m_length = length;
    }
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);
        }
    }
    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<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)
    {
        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;
        }
    }
protected:
    PGconn* m_conn;
    result m_res;
    std::vector<binder> m_binders;
    template<ExecStatusType... Excepted>
    void verify_error()
    {
        if (m_res)
            m_res.verify_error<Excepted...>();
        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<char*>(_name.data()), "q%p", this);
        _name.resize(n);
        std::transform(_name.begin(), _name.end(), _name.begin(), tolower);
        result res = PQprepare(m_conn, _name.data(), command, nParams, paramTypes);
        res.verify_error<PGRES_COMMAND_OK>();
    }
    template<typename... Types>
    void open(const char* command)
    {
        auto binder_list = make_binder_list(Types()...);
        std::array<Oid, sizeof...(Types)> types;
        std::transform(binder_list.begin(), binder_list.end(), types.begin(), [](const binder& b) {
            return b.type();
        });
        open(command, types.size(), types.data());
    }
    void attach(const char* name)
    {
        result res = PQdescribePrepared(m_conn, name);
        res.verify_error<PGRES_COMMAND_OK>();
        _name = name;
    }
    void execute()
    {
        if(!PQsendQueryPrepared(m_conn, _name.data(), 0, nullptr, nullptr, nullptr, 1))
            throw error(m_conn);
        if (!PQsetSingleRowMode(m_conn))
            throw error(m_conn);
        m_res = PQgetResult(m_conn);
        verify_error<PGRES_COMMAND_OK, PGRES_SINGLE_TUPLE>();
    }
    template<typename Types>
    void execute(const Types& params)
    {
        const size_t count = qtl::params_binder<statement, Types>::size;
        if (count > 0)
        {
            m_binders.resize(count);
            qtl::bind_params(*this, params);
            std::array<const char*, count> values;
            std::array<int, count> lengths;
            std::array<int, count> formats;
            for (size_t i = 0; i != m_binders.size(); i++)
            {
                values[i] = m_binders[i].value();
                lengths[i] = static_cast<int>(m_binders[i].length());
                formats[i] = 1;
            }
            if (!PQsendQueryPrepared(m_conn, _name.data(), static_cast<int>(m_binders.size()), values.data(), lengths.data(), formats.data(), 1))
                throw error(m_conn);
        }
        else
        {
            if (!PQsendQueryPrepared(m_conn, _name.data(), 0, nullptr, nullptr, nullptr, 1))
                throw error(m_conn);
        }
        if (!PQsetSingleRowMode(m_conn))
            throw error(m_conn);
        m_res = PQgetResult(m_conn);
        verify_error<PGRES_COMMAND_OK, PGRES_SINGLE_TUPLE>();
    }
    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();
    }
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<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));
                proc(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;
    }
    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<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");
    }
};
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_
include/qtl_sqlite.hpp
@@ -411,13 +411,13 @@
        return count>0;;
    }
    int affetced_rows()
    int affetced_rows() const
    {
        sqlite3* db=sqlite3_db_handle(m_stmt);
        return db ? sqlite3_changes(db) : 0;
    }
    int64_t insert_id()
    int64_t insert_id() const
    {
        sqlite3* db=sqlite3_db_handle(m_stmt);
        return db ? sqlite3_last_insert_rowid(db) : 0;
test/TestPostgres.cpp
New file
@@ -0,0 +1,230 @@
#include "stdafx.h"
#include "TestPostgres.h"
#include <iostream>
#include <fstream>
#include <array>
#include <iomanip>
#include "md5.h"
#include "../include/qtl_postgres.hpp"
using namespace std;
struct TestpostgresRecord
{
    int32_t id;
    char name[33];
    qtl::postgres::timestamp create_time;
    TestpostgresRecord()
    {
        memset(this, 0, sizeof(TestpostgresRecord));
    }
    void print() const
    {
        printf("ID=\"%d\", Name=\"%s\"\n",
            id, name);
    }
};
namespace qtl
{
    template<>
    inline void bind_record<qtl::postgres::statement, TestpostgresRecord>(qtl::postgres::statement& command, TestpostgresRecord&& v)
    {
        qtl::bind_fields(command, v.id, v.name, v.create_time);
    }
}
TestPostgres::TestPostgres()
{
    this->id = 0;
    TEST_ADD(TestPostgres::test_dual)
        TEST_ADD(TestPostgres::test_clear)
        TEST_ADD(TestPostgres::test_insert)
        TEST_ADD(TestPostgres::test_select)
        TEST_ADD(TestPostgres::test_update)
        TEST_ADD(TestPostgres::test_insert2)
        TEST_ADD(TestPostgres::test_iterator)
        TEST_ADD(TestPostgres::test_any)
}
inline void TestPostgres::connect(qtl::postgres::database& db)
{
    TEST_ASSERT_MSG(db.open("localhost", "postgres", "111111", 5432U, "test") == true, "Cannot connect to database");
}
void TestPostgres::test_dual()
{
    qtl::postgres::database db;
    connect(db);
    try
    {
        db.query("select 0, 'hello world';",
            [](int32_t i, const std::string& str) {
            printf("0=\"%d\", 'hello world'=\"%s\"\n",
                i, str.data());
        });
    }
    catch (qtl::postgres::error& e)
    {
        ASSERT_EXCEPTION(e);
    }
}
void TestPostgres::test_select()
{
    qtl::postgres::database db;
    connect(db);
    try
    {
        db.query("select * from test where id=$1", 0, id,
            [](const qtl::indicator<int32_t>& id, const std::string& name, const qtl::postgres::timestamp& create_time) {
            printf("ID=\"%d\", Name=\"%s\"\n",
                id.data, name.data());
        });
        db.query("select * from test where id=$1", 0, id,
            [](const TestpostgresRecord& record) {
            printf("ID=\"%d\", Name=\"%s\"\n",
                record.id, record.name);
        });
        db.query("select * from test where id=$1", 0, id,
            &TestpostgresRecord::print);
    }
    catch (qtl::postgres::error& e)
    {
        ASSERT_EXCEPTION(e);
    }
}
void TestPostgres::test_insert()
{
    qtl::postgres::database db;
    connect(db);
    try
    {
        db.query_first("insert into test(Name, CreateTime) values($1, LOCALTIMESTAMP) returning ID",
            "test_user", id);
    }
    catch (qtl::postgres::error& e)
    {
        ASSERT_EXCEPTION(e);
    }
    TEST_ASSERT_MSG(id > 0, "insert failture.");
}
void TestPostgres::test_insert2()
{
    qtl::postgres::database db;
    connect(db);
    try
    {
        uint64_t affected = 0;
        qtl::postgres::statement stmt = db.open_command("insert into test(Name, CreateTime) values($1, LOCALTIMESTAMP)");
        qtl::execute(stmt, &affected, "second_user", "third_user");
        TEST_ASSERT_MSG(affected == 2, "Cannot insert 2 records to table test.");
    }
    catch (qtl::postgres::error& e)
    {
        ASSERT_EXCEPTION(e);
    }
}
void TestPostgres::test_update()
{
    qtl::postgres::database db;
    connect(db);
    try
    {
        db.execute_direct("update test set Name=$1 WHERE ID=$2", NULL,
            "other_user", id);
    }
    catch (qtl::postgres::error& e)
    {
        ASSERT_EXCEPTION(e);
    }
    TEST_ASSERT_MSG(id > 0, "insert failture.");
}
void TestPostgres::test_clear()
{
    qtl::postgres::database db;
    connect(db);
    try
    {
        db.simple_execute("delete from test");
    }
    catch (qtl::postgres::error& e)
    {
        ASSERT_EXCEPTION(e);
    }
}
void TestPostgres::test_iterator()
{
    qtl::postgres::database db;
    connect(db);
    try
    {
        cout << "after insert all:" << endl;
        for (auto& record : db.result<TestpostgresRecord>("select * from test"))
        {
            printf("ID=\"%d\", Name=\"%s\"\n",
                record.id, record.name);
        }
    }
    catch (qtl::postgres::error& e)
    {
        ASSERT_EXCEPTION(e);
    }
}
void TestPostgres::test_any()
{
#ifdef _QTL_ENABLE_CPP17
    qtl::postgres::database db;
    connect(db);
    try
    {
        db.query("select 0, 'hello world', LOCALTIMESTAMP",
            [](const std::any& i, const std::any& str, const std::any& now) {
            const qtl::postgres::time& time = std::any_cast<const qtl::postgres::time&>(now);
            struct tm tm;
            time.as_tm(tm);
            cout << "0=\"" << std::any_cast<int32_t>(i) << "\", 'hello world'=\"" <<
                std::any_cast<const std::string&>(str) << "\", now=\"" <<
                std::put_time(&tm, "%c") << "\" \n";
        });
    }
    catch (qtl::postgres::error& e)
    {
        ASSERT_EXCEPTION(e);
    }
    catch (std::bad_cast& e)
    {
        ASSERT_EXCEPTION(e);
    }
#endif
}
int main(int argc, char* argv[])
{
    Test::TextOutput output(Test::TextOutput::Verbose);
    cout << endl << "Testing postgres:" << endl;
    TestPostgres test_postgres;
    test_postgres.run(output);
    return 0;
}
test/TestPostgres.h
New file
@@ -0,0 +1,35 @@
#ifndef _TEST_MYSQL_H_
#define _TEST_MYSQL_H_
#include "TestSuite.h"
namespace qtl
{
    namespace postgres
    {
        class database;
    }
}
class TestPostgres : public TestSuite
{
public:
    TestPostgres();
private:
    void test_dual();
    void test_clear();
    void test_insert();
    void test_select();
    void test_update();
    void test_insert2();
    void test_iterator();
    void test_any();
private:
    int32_t id;
    void connect(qtl::postgres::database& db);
    void get_md5(std::istream& is, unsigned char* result);
};
#endif //_TEST_MYSQL_H_
test/TestSuite.h
@@ -2,6 +2,7 @@
#define _TEST_SUITE_H_
#include <exception>
#include <cpptest.h>
class TestSuite : public Test::Suite
{
test/test_mariadb.mak
@@ -3,9 +3,9 @@
PCH_HEADER=stdafx.h
PCH=stdafx.h.gch
OBJ=TestMariaDB.o
CFLAGS=-g -D_DEBUG -O2 -I/usr/include -I/usr/include/mariadb -I/usr/local/include -I/usr/local/include/mariadb
CFLAGS=-g -D_DEBUG -O2 -D_QTL_USE_MARIADB -I/usr/include $(shell mariadb_config --cflags)
CXXFLAGS=-I../include -std=c++11
LDFLAGS= -L/usr/local/lib -L/usr/local/mariadb/lib -lmariadb
LDFLAGS= -L/usr/local/lib $(shell -L/usr/local/lib/mariadb/ -lmariadb)
all : $(TARGET)
test/test_mysql.mak
@@ -3,9 +3,9 @@
PCH_HEADER=stdafx.h
PCH=stdafx.h.gch
OBJ=TestMysql.o md5.o
CFLAGS=-g -D_DEBUG -O2 -I../include -I/usr/include/mysql -I/usr/local/include -I/usr/local/mysql/include
CFLAGS=-g -D_DEBUG -O2 -I../include -I/usr/include -I/usr/local/include $(shell mysql_config --cflags)
CXXFLAGS=-std=c++11
LDFLAGS= -L/usr/local/lib -L/usr/local/mysql/lib -lcpptest -lmysqlclient
LDFLAGS= -L/usr/local/lib -lcpptest $(shell mysql_config --libs)
all : $(TARGET)
test/test_postgres.mak
New file
@@ -0,0 +1,25 @@
TARGET=test_postgres
CC=g++
PCH_HEADER=stdafx.h
PCH=stdafx.h.gch
OBJ=TestPostgres.o md5.o
CFLAGS=-g -D_DEBUG -O2-I/usr/include -I/usr/local/include -I$(shell pg_config --includedir) -I$(shell pg_config --includedir-server )
CXXFLAGS= -I../include -std=c++11
LDFLAGS= -L$(shell pg_config --libdir) -lcpptest -lpq -lpgtypes
all : $(TARGET)
$(PCH) : $(PCH_HEADER)
    $(CC) $(CFLAGS) $(CXXFLAGS) -x c++-header -o $@ $<
TestPostgres.o : TestPostgres.cpp $(PCH)
    $(CC) -c $(CFLAGS) $(CXXFLAGS) -o $@ $<
md5.o : md5.c md5.h
    gcc -c $(CFLAGS) -o $@ $<
$(TARGET) : $(OBJ)
    libtool --tag=CXX --mode=link $(CC) $(LDFLAGS) -o $@ $^
clean:
    rm $(TARGET) $(PCH) $(OBJ) -f
test/vs/test.sln
@@ -9,6 +9,8 @@
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_mariadb", "test_mariadb\test_mariadb.vcproj", "{2A539BFA-B934-4192-A029-524593AEC6B8}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_postgres", "test_postgres\test_postgres.vcproj", "{C8634EFC-2489-4F39-94EB-F54DC1ED2211}"
EndProject
Global
    GlobalSection(SolutionConfigurationPlatforms) = preSolution
        Debug|Win32 = Debug|Win32
@@ -31,6 +33,10 @@
        {2A539BFA-B934-4192-A029-524593AEC6B8}.Debug|Win32.Build.0 = Debug|Win32
        {2A539BFA-B934-4192-A029-524593AEC6B8}.Release|Win32.ActiveCfg = Release|Win32
        {2A539BFA-B934-4192-A029-524593AEC6B8}.Release|Win32.Build.0 = Release|Win32
        {C8634EFC-2489-4F39-94EB-F54DC1ED2211}.Debug|Win32.ActiveCfg = Debug|Win32
        {C8634EFC-2489-4F39-94EB-F54DC1ED2211}.Debug|Win32.Build.0 = Debug|Win32
        {C8634EFC-2489-4F39-94EB-F54DC1ED2211}.Release|Win32.ActiveCfg = Release|Win32
        {C8634EFC-2489-4F39-94EB-F54DC1ED2211}.Release|Win32.Build.0 = Release|Win32
    EndGlobalSection
    GlobalSection(SolutionProperties) = preSolution
        HideSolutionNode = FALSE
test/vs/test_postgres/test_postgres.vcproj
New file
@@ -0,0 +1,215 @@
<?xml version="1.0" encoding="gb2312"?>
<VisualStudioProject
    ProjectType="Visual C++"
    Version="8.00"
    Name="test_postgres"
    ProjectGUID="{C8634EFC-2489-4F39-94EB-F54DC1ED2211}"
    RootNamespace="test_postgres"
    Keyword="Win32Proj"
    >
    <Platforms>
        <Platform
            Name="Win32"
        />
    </Platforms>
    <ToolFiles>
    </ToolFiles>
    <Configurations>
        <Configuration
            Name="Debug|Win32"
            OutputDirectory="$(SolutionDir)$(ConfigurationName)"
            IntermediateDirectory="$(ConfigurationName)"
            ConfigurationType="1"
            CharacterSet="1"
            >
            <Tool
                Name="VCPreBuildEventTool"
            />
            <Tool
                Name="VCCustomBuildTool"
            />
            <Tool
                Name="VCXMLDataGeneratorTool"
            />
            <Tool
                Name="VCWebServiceProxyGeneratorTool"
            />
            <Tool
                Name="VCMIDLTool"
            />
            <Tool
                Name="VCCLCompilerTool"
                Optimization="0"
                PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"
                MinimalRebuild="true"
                BasicRuntimeChecks="3"
                RuntimeLibrary="3"
                UsePrecompiledHeader="0"
                WarningLevel="3"
                Detect64BitPortabilityProblems="true"
                DebugInformationFormat="4"
            />
            <Tool
                Name="VCManagedResourceCompilerTool"
            />
            <Tool
                Name="VCResourceCompilerTool"
            />
            <Tool
                Name="VCPreLinkEventTool"
            />
            <Tool
                Name="VCLinkerTool"
                AdditionalDependencies="libpq.lib ws2_32.lib cpptest.lib"
                LinkIncremental="2"
                GenerateDebugInformation="true"
                SubSystem="2"
                TargetMachine="1"
            />
            <Tool
                Name="VCALinkTool"
            />
            <Tool
                Name="VCManifestTool"
            />
            <Tool
                Name="VCXDCMakeTool"
            />
            <Tool
                Name="VCBscMakeTool"
            />
            <Tool
                Name="VCFxCopTool"
            />
            <Tool
                Name="VCAppVerifierTool"
            />
            <Tool
                Name="VCWebDeploymentTool"
            />
            <Tool
                Name="VCPostBuildEventTool"
            />
        </Configuration>
        <Configuration
            Name="Release|Win32"
            OutputDirectory="$(SolutionDir)$(ConfigurationName)"
            IntermediateDirectory="$(ConfigurationName)"
            ConfigurationType="1"
            CharacterSet="1"
            WholeProgramOptimization="1"
            >
            <Tool
                Name="VCPreBuildEventTool"
            />
            <Tool
                Name="VCCustomBuildTool"
            />
            <Tool
                Name="VCXMLDataGeneratorTool"
            />
            <Tool
                Name="VCWebServiceProxyGeneratorTool"
            />
            <Tool
                Name="VCMIDLTool"
            />
            <Tool
                Name="VCCLCompilerTool"
                PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
                RuntimeLibrary="2"
                UsePrecompiledHeader="0"
                WarningLevel="3"
                Detect64BitPortabilityProblems="true"
                DebugInformationFormat="3"
            />
            <Tool
                Name="VCManagedResourceCompilerTool"
            />
            <Tool
                Name="VCResourceCompilerTool"
            />
            <Tool
                Name="VCPreLinkEventTool"
            />
            <Tool
                Name="VCLinkerTool"
                AdditionalDependencies="libpq.lib ws2_32.lib cpptest.lib"
                LinkIncremental="1"
                GenerateDebugInformation="true"
                SubSystem="2"
                OptimizeReferences="2"
                EnableCOMDATFolding="2"
                TargetMachine="1"
            />
            <Tool
                Name="VCALinkTool"
            />
            <Tool
                Name="VCManifestTool"
            />
            <Tool
                Name="VCXDCMakeTool"
            />
            <Tool
                Name="VCBscMakeTool"
            />
            <Tool
                Name="VCFxCopTool"
            />
            <Tool
                Name="VCAppVerifierTool"
            />
            <Tool
                Name="VCWebDeploymentTool"
            />
            <Tool
                Name="VCPostBuildEventTool"
            />
        </Configuration>
    </Configurations>
    <References>
    </References>
    <Files>
        <Filter
            Name="Ô´Îļþ"
            Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
            UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
            >
            <File
                RelativePath="..\..\stdafx.cpp"
                >
            </File>
            <File
                RelativePath="..\..\TestPostgres.cpp"
                >
            </File>
        </Filter>
        <Filter
            Name="Í·Îļþ"
            Filter="h;hpp;hxx;hm;inl;inc;xsd"
            UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
            >
            <File
                RelativePath="..\..\stdafx.h"
                >
            </File>
            <File
                RelativePath="..\..\TestPostgres.h"
                >
            </File>
            <File
                RelativePath="..\..\TestSuite.h"
                >
            </File>
        </Filter>
        <Filter
            Name="×ÊÔ´Îļþ"
            Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
            UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
            >
        </Filter>
    </Files>
    <Globals>
    </Globals>
</VisualStudioProject>