diff --git a/cppdb/backend.h b/cppdb/backend.h index 6e8d675..83712d1 100644 --- a/cppdb/backend.h +++ b/cppdb/backend.h @@ -186,12 +186,16 @@ namespace cppdb { /// virtual bool fetch(int col,std::tm &v) = 0; /// + virtual bool fetch(int col,bool &v) = 0; + /// /// Check if the column \a col is NULL starting from 0, should throw invalid_column() if the index out of range /// virtual bool is_null(int col) = 0; /// /// Return the number of columns in the result. Should be valid even without calling next() first time. /// + virtual int data_type(int col) = 0; + /// virtual int cols() = 0; /// /// Return the number of columns by its name. Return -1 if the name is invalid @@ -276,12 +280,15 @@ namespace cppdb { /// virtual void bind(int col,std::istream &) = 0; /// + virtual void bind(int col,bool) = 0; + /// /// Bind an integer value to column \a col (starting from 1). /// /// Should throw invalid_placeholder() if the value of col is out of range. May /// ignore if it is impossible to know whether the placeholder exists without special /// support from back-end. /// + /// virtual void bind(int col,int v) = 0; /// /// Bind an integer value to column \a col (starting from 1). diff --git a/cppdb/frontend.h b/cppdb/frontend.h index 5d5de4c..42cb346 100644 --- a/cppdb/frontend.h +++ b/cppdb/frontend.h @@ -263,6 +263,15 @@ namespace cppdb { /// bool is_null(std::string const &n); + /// + /// Return data type (oid) of column number \a col (starting from 0) + /// + int data_type(int col); + /// + /// Return data type (oid) of column named \a n + /// + int data_type(std::string const &n); + /// /// Clears the result, no further use of the result should be done until it is assigned again with a new statement result. /// @@ -349,6 +358,9 @@ namespace cppdb { /// If the data type is not blob, it may throw bad_value_cast() /// bool fetch(int col,std::ostream &v); + /// + /// + bool fetch(int col,bool &v); /// /// Fetch a value from column named \a n into \a v. Returns false @@ -420,7 +432,9 @@ namespace cppdb { /// the \a n value is invalid throws invalid_column exception /// bool fetch(std::string const &n,std::ostream &v); - + /// + /// + bool fetch(std::string const &n,bool &v); /// /// Fetch a value from the next column in the row starting from the first one. Returns false @@ -481,6 +495,9 @@ namespace cppdb { /// automatically. /// bool fetch(std::ostream &v); + /// + /// + bool fetch(bool &v); /// /// Get a value of type \a T from column named \a name. If the column @@ -723,6 +740,9 @@ namespace cppdb { /// statement &bind(std::istream &v); /// + /// + statement &bind(bool v); + /// /// Bind a NULL value to the next placeholder marked with '?' marker in the query. /// /// If number of calls is higher then the number placeholders is the statement it @@ -814,6 +834,9 @@ namespace cppdb { /// void bind(int col,std::istream &v); /// + /// + void bind(int col,bool v); + /// /// Bind a NULL value to the placeholder number \a col (starting from 1) marked with '?' marker in the query. /// /// If \a cols is invalid (less then 1 or higher then the number of the placeholders is the statement) it @@ -895,6 +918,9 @@ namespace cppdb { /// statement &operator<<(std::istream &v); /// + /// + statement &operator<<(bool v); + /// /// Apply manipulator on the statement, same as manipulator(*this). /// statement &operator<<(void (*manipulator)(statement &st)); diff --git a/cppdb/postgres_backend.h b/cppdb/postgres_backend.h new file mode 100644 index 0000000..c59ad1e --- /dev/null +++ b/cppdb/postgres_backend.h @@ -0,0 +1,815 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2010-2011 Artyom Beilis (Tonkikh) +// +// Distributed under: +// +// the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// or (at your opinion) under: +// +// The MIT License +// (See accompanying file MIT.txt or a copy at +// http://www.opensource.org/licenses/mit-license.php) +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef CPPDB_POSTGRES_BACKEND_H +#define CPPDB_POSTGRES_BACKEND_H +#define CPPDB_DRIVER_SOURCE +#ifdef CPPDB_WITH_PQ +# define CPPDB_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace cppdb { + namespace postgresql { + + typedef enum { + lo_type, + bytea_type + } blob_type; + + class pqerror : public cppdb_error { + public: + pqerror(char const *msg) : cppdb_error(message(msg)) {} + pqerror(PGresult *r,char const *msg) : cppdb_error(message(msg,r)) {} + pqerror(PGconn *c,char const *msg) : cppdb_error(message(msg,c)) {} + + static std::string message(char const *msg) + { + return std::string("cppdb::posgresql: ") + msg; + } + static std::string message(char const *msg,PGresult *r) + { + std::string m="cppdb::posgresql: "; + m+=msg; + m+=": "; + m+=PQresultErrorMessage(r); + return m; + } + static std::string message(char const *msg,PGconn *c) + { + std::string m="cppdb::posgresql: "; + m+=msg; + m+=": "; + m+=PQerrorMessage(c); + return m; + } + }; + + class result : public backend::result { + public: + result(PGresult *res,PGconn *conn,blob_type b) : + res_(res), + conn_(conn), + rows_(PQntuples(res)), + cols_(PQnfields(res)), + current_(-1), + blob_(b) + { + ss_.imbue(std::locale::classic()); + } + virtual ~result() + { + PQclear(res_); + } + virtual next_row has_next() + { + if(current_ + 1 < rows_) + return next_row_exists; + else + return last_row_reached; + + } + virtual bool next() + { + current_ ++; + if(current_ < rows_) { + return true; + } + return false; + } + + template + bool do_fetch(int col,T &v) + { + if(do_isnull(col)) + return false; + std::string tmp(PQgetvalue(res_,current_,col),PQgetlength(res_,current_,col)); + v=parse_number(tmp,ss_); + return true; + } + virtual bool fetch(int col,short &v) + { + return do_fetch(col,v); + } + virtual bool fetch(int col,unsigned short &v) + { + return do_fetch(col,v); + } + virtual bool fetch(int col,int &v) + { + return do_fetch(col,v); + } + virtual bool fetch(int col,unsigned &v) + { + return do_fetch(col,v); + } + virtual bool fetch(int col,long &v) + { + return do_fetch(col,v); + } + virtual bool fetch(int col,unsigned long &v) + { + return do_fetch(col,v); + } + virtual bool fetch(int col,long long &v) + { + return do_fetch(col,v); + } + virtual bool fetch(int col,unsigned long long &v) + { + return do_fetch(col,v); + } + virtual bool fetch(int col,float &v) + { + return do_fetch(col,v); + } + virtual bool fetch(int col,double &v) + { + return do_fetch(col,v); + } + virtual bool fetch(int col,long double &v) + { + return do_fetch(col,v); + } + virtual bool fetch(int col,std::string &v) + { + if(do_isnull(col)) + return false; + v.assign(PQgetvalue(res_,current_,col),PQgetlength(res_,current_,col)); + return true; + } + virtual bool fetch(int col,std::ostream &v) + { + if(do_isnull(col)) + return false; + if(blob_ == bytea_type) { + unsigned char *val=(unsigned char*)PQgetvalue(res_,current_,col); + size_t len = 0; + unsigned char *buf=PQunescapeBytea(val,&len); + if(!buf) { + throw bad_value_cast(); + } + try { + v.write((char *)buf,len); + }catch(...) { + PQfreemem(buf); + throw; + } + PQfreemem(buf); + } + else { // oid + Oid id = 0; + fetch(col,id); + if(id==0) { + throw pqerror("fetching large object failed, oid=0"); + } + int fd = -1; + try { + fd = lo_open(conn_,id,INV_READ | INV_WRITE); + if(fd < 0) + throw pqerror(conn_,"Failed opening large object for read"); + char buf[4096]; + for(;;) { + int n=lo_read(conn_,fd,buf,sizeof(buf)); + if(n < 0) + throw pqerror(conn_,"Failed reading large object"); + if(n>=0) + v.write(buf,n); + if(n < int(sizeof(buf))) + break; + } + int r = lo_close(conn_,fd); + fd = -1; + if(r < 0) + throw pqerror(conn_,"error on close of large object"); + } + catch(...) { + if(fd != -1) + lo_close(conn_,fd); + throw; + } + } + return true; + } + virtual bool fetch(int col,std::tm &v) + { + if(do_isnull(col)) + return false; + v=parse_time(PQgetvalue(res_,current_,col)); + return true; + } + virtual bool fetch(int col,bool &v) + { + if(do_isnull(col)) + return false; + v=*PQgetvalue(res_,current_,col) == 't'; + return true; + } + virtual bool is_null(int col) + { + return do_isnull(col); + } + virtual int data_type(int col) + { + return PQftype(res_,col); + } + virtual int cols() + { + return cols_; + } + virtual int name_to_column(std::string const &n) + { + return PQfnumber(res_,n.c_str()); + } + virtual std::string column_to_name(int pos) + { + char const *name = PQfname(res_,pos); + if(!name) + return std::string(); + return name; + } + private: + + void check(int c) + { + if(c < 0 || c>= cols_) + throw invalid_column(); + } + bool do_isnull(int col) + { + check(col); + return PQgetisnull(res_,current_,col); + } + PGresult *res_; + PGconn *conn_; + int rows_; + int cols_; + int current_; + blob_type blob_; + std::istringstream ss_; + }; + + class statement : public backend::statement { + public: + + typedef enum { + null_param, + text_param, + binary_param + } param_type; + + statement(PGconn *conn,std::string const &src_query,blob_type b,unsigned long long prepared_id) : + res_(0), + conn_(conn), + orig_query_(src_query), + params_(0), + blob_(b) + { + fmt_.imbue(std::locale::classic()); + + query_.reserve(src_query.size()); + bool inside_string=false; + for(unsigned i=0;i 0) { + + fmt_.str(std::string()); + fmt_.clear(); + fmt_<<"cppdb_psqlstmt_" << prepared_id; + prepared_id_ = fmt_.str(); + fmt_.str(std::string()); + fmt_.clear(); + + PGresult *r=PQprepare(conn_,prepared_id_.c_str(),query_.c_str(),0,0); + try { + if(!r) { + throw pqerror("Failed to create prepared statement object!"); + } + if(PQresultStatus(r)!=PGRES_COMMAND_OK) + throw pqerror(r,"statement preparation failed"); + } + catch(...) { + if(r) PQclear(r); + throw; + } + PQclear(r); + } + } + virtual ~statement() + { + try { + if(res_) { + PQclear(res_); + res_ = 0; + } + if(!prepared_id_.empty()) { + std::string stmt = "DEALLOCATE " + prepared_id_; + res_ = PQexec(conn_,stmt.c_str()); + if(res_) { + PQclear(res_); + res_ = 0; + } + } + } + catch(...) + { + } + } + virtual void reset() + { + if(res_) { + PQclear(res_); + res_ = 0; + } + std::vector vals(params_); + std::vector lengths(params_,0); + std::vector pvals(params_,0); + std::vector flags(params_,null_param); + params_values_.swap(vals); + params_pvalues_.swap(pvals); + params_plengths_.swap(lengths); + params_set_.swap(flags); + } + virtual void bind(int col,std::string const &v) + { + bind(col,v.c_str(),v.c_str()+v.size()); + } + virtual void bind(int col,char const *s) + { + bind(col,s,s+strlen(s)); + } + virtual void bind(int col,char const *b,char const *e) + { + check(col); + params_pvalues_[col-1] = b; + params_plengths_[col-1] = e-b; + params_set_[col-1]=text_param; + } + virtual void bind(int col,std::tm const &v) + { + check(col); + params_values_[col-1]=cppdb::format_time(v); + params_set_[col-1]=text_param; + } + virtual void bind(int col,std::istream &in) + { + check(col); + if(blob_ == bytea_type) { + std::ostringstream ss; + ss << in.rdbuf(); + params_values_[col-1]=ss.str(); + params_set_[col-1]=binary_param; + } + else { + Oid id = 0; + int fd = -1; + try { + id = lo_creat(conn_, INV_READ|INV_WRITE); + if(id == 0) + throw pqerror(conn_,"failed to create large object"); + fd = lo_open(conn_,id,INV_READ | INV_WRITE); + if(fd < 0) + throw pqerror(conn_,"failed to open large object for writing"); + char buf[4096]; + for(;;) { + in.read(buf,sizeof(buf)); + int bytes_read = in.gcount(); + if(bytes_read > 0) { + int n = lo_write(conn_,fd,buf,bytes_read); + if(n < 0) { + throw pqerror(conn_,"failed writing to large object"); + } + } + if(bytes_read < int(sizeof(buf))) + break; + } + int r = lo_close(conn_,fd); + fd=-1; + if(r < 0) + throw pqerror(conn_,"error closing large object after write"); + bind(col,id); + } + catch(...) { + if(fd<-1) + lo_close(conn_,fd); + if(id!=0) + lo_unlink(conn_,id); + throw; + } + } + } + virtual void bind(int col,bool v) + { + check(col); + params_values_[col-1]=v ? "true" : "false"; + params_set_[col-1]=text_param; + } + + template + void do_bind(int col,T v) + { + check(col); + fmt_.str(std::string()); + fmt_.clear(); + if(!std::numeric_limits::is_integer) + fmt_ << std::setprecision(std::numeric_limits::digits10+1); + fmt_ << v; + params_values_[col-1]=fmt_.str(); + params_set_[col-1]=text_param; + fmt_.str(std::string()); + fmt_.clear(); + } + + virtual void bind(int col,int v) + { + do_bind(col,v); + } + virtual void bind(int col,unsigned v) + { + do_bind(col,v); + } + virtual void bind(int col,long v) + { + do_bind(col,v); + } + virtual void bind(int col,unsigned long v) + { + do_bind(col,v); + } + virtual void bind(int col,long long v) + { + do_bind(col,v); + } + virtual void bind(int col,unsigned long long v) + { + do_bind(col,v); + } + virtual void bind(int col,double v) + { + do_bind(col,v); + } + virtual void bind(int col,long double v) + { + do_bind(col,v); + } + virtual void bind_null(int col) + { + check(col); + params_set_[col-1]=null_param; + std::string tmp; + params_values_[col-1].swap(tmp); + } + + void real_query() + { + char const * const *pvalues = 0; + int *plengths = 0; + int *pformats = 0; + std::vector values; + std::vector lengths; + std::vector formats; + if(params_>0) { + values.resize(params_,0); + lengths.resize(params_,0); + formats.resize(params_,0); + for(unsigned i=0;i> rowid; + fmt_.str(std::string()); + fmt_.clear(); + } + catch(...) { + if(res) PQclear(res); + throw; + } + PQclear(res); + return rowid; + } + virtual unsigned long long affected() + { + if(res_) { + char const *s=PQcmdTuples(res_); + if(!s || !*s) + return 0; + unsigned long long rows = 0; + fmt_.str(s); + fmt_.clear(); + fmt_ >> rows; + fmt_.str(std::string()); + fmt_.clear(); + return rows; + } + return 0; + } + virtual std::string const &sql_query() + { + return orig_query_; + } + private: + void check(int col) + { + if(col < 1 || col > int(params_)) + throw invalid_placeholder(); + } + PGresult *res_; + PGconn *conn_; + + std::string query_; + std::string orig_query_; + unsigned params_; + std::vector params_values_; + std::vector params_pvalues_; + std::vector params_plengths_; + std::vector params_set_; + std::string prepared_id_; + std::stringstream fmt_; + blob_type blob_; + }; + + class connection : public backend::connection { + public: + void do_simple_exec(char const *s) + { + PGresult *r=PQexec(conn_,s); + try { + + } + catch(...) { + PQclear(r); + throw; + } + PQclear(r); + } + virtual void begin() + { + do_simple_exec("begin"); + } + virtual void commit() + { + do_simple_exec("commit"); + } + virtual void rollback() + { + try { + do_simple_exec("rollback"); + } + catch(...) {} + } + virtual statement *prepare_statement(std::string const &q) + { + return new statement(conn_,q,blob_,++prepared_id_); + } + virtual statement *create_statement(std::string const &q) + { + return new statement(conn_,q,blob_,0); + } + std::string do_escape(char const *b,size_t length) + { + std::vector buf(2*length+1); + size_t len = PQescapeStringConn(conn_,&buf.front(),b,length,0); + return std::string(&buf.front(),len); + } + virtual std::string escape(std::string const &s) + { + return do_escape(s.c_str(),s.size()); + } + virtual std::string escape(char const *s) + { + return do_escape(s,strlen(s)); + } + virtual std::string escape(char const *b,char const *e) + { + return do_escape(b,e-b); + } + std::string pq_string(connection_info const &ci) + { + std::map::const_iterator p; + std::string pq_str; + for(p=ci.properties.begin();p!=ci.properties.end();p++) { + if(p->first.empty() || p->first[0]=='@') + continue; + pq_str+=p->first; + pq_str+="='"; + pq_str+=escape_for_conn(p->second); + pq_str+="' "; + } + return pq_str; + } + std::string escape_for_conn(std::string const &v) + { + std::string res; + res.reserve(v.size()); + for(unsigned i=0;i -// -// Distributed under: -// -// the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -// -// or (at your opinion) under: -// -// The MIT License -// (See accompanying file MIT.txt or a copy at -// http://www.opensource.org/licenses/mit-license.php) -// -/////////////////////////////////////////////////////////////////////////////// -#define CPPDB_DRIVER_SOURCE -#ifdef CPPDB_WITH_PQ -# define CPPDB_SOURCE -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace cppdb { - namespace postgresql { - - typedef enum { - lo_type, - bytea_type - } blob_type; - - class pqerror : public cppdb_error { - public: - pqerror(char const *msg) : cppdb_error(message(msg)) {} - pqerror(PGresult *r,char const *msg) : cppdb_error(message(msg,r)) {} - pqerror(PGconn *c,char const *msg) : cppdb_error(message(msg,c)) {} - - static std::string message(char const *msg) - { - return std::string("cppdb::posgresql: ") + msg; - } - static std::string message(char const *msg,PGresult *r) - { - std::string m="cppdb::posgresql: "; - m+=msg; - m+=": "; - m+=PQresultErrorMessage(r); - return m; - } - static std::string message(char const *msg,PGconn *c) - { - std::string m="cppdb::posgresql: "; - m+=msg; - m+=": "; - m+=PQerrorMessage(c); - return m; - } - }; - - class result : public backend::result { - public: - result(PGresult *res,PGconn *conn,blob_type b) : - res_(res), - conn_(conn), - rows_(PQntuples(res)), - cols_(PQnfields(res)), - current_(-1), - blob_(b) - { - ss_.imbue(std::locale::classic()); - } - virtual ~result() - { - PQclear(res_); - } - virtual next_row has_next() - { - if(current_ + 1 < rows_) - return next_row_exists; - else - return last_row_reached; - - } - virtual bool next() - { - current_ ++; - if(current_ < rows_) { - return true; - } - return false; - } - - template - bool do_fetch(int col,T &v) - { - if(do_isnull(col)) - return false; - std::string tmp(PQgetvalue(res_,current_,col),PQgetlength(res_,current_,col)); - v=parse_number(tmp,ss_); - return true; - } - virtual bool fetch(int col,short &v) - { - return do_fetch(col,v); - } - virtual bool fetch(int col,unsigned short &v) - { - return do_fetch(col,v); - } - virtual bool fetch(int col,int &v) - { - return do_fetch(col,v); - } - virtual bool fetch(int col,unsigned &v) - { - return do_fetch(col,v); - } - virtual bool fetch(int col,long &v) - { - return do_fetch(col,v); - } - virtual bool fetch(int col,unsigned long &v) - { - return do_fetch(col,v); - } - virtual bool fetch(int col,long long &v) - { - return do_fetch(col,v); - } - virtual bool fetch(int col,unsigned long long &v) - { - return do_fetch(col,v); - } - virtual bool fetch(int col,float &v) - { - return do_fetch(col,v); - } - virtual bool fetch(int col,double &v) - { - return do_fetch(col,v); - } - virtual bool fetch(int col,long double &v) - { - return do_fetch(col,v); - } - virtual bool fetch(int col,std::string &v) - { - if(do_isnull(col)) - return false; - v.assign(PQgetvalue(res_,current_,col),PQgetlength(res_,current_,col)); - return true; - } - virtual bool fetch(int col,std::ostream &v) - { - if(do_isnull(col)) - return false; - if(blob_ == bytea_type) { - unsigned char *val=(unsigned char*)PQgetvalue(res_,current_,col); - size_t len = 0; - unsigned char *buf=PQunescapeBytea(val,&len); - if(!buf) { - throw bad_value_cast(); - } - try { - v.write((char *)buf,len); - }catch(...) { - PQfreemem(buf); - throw; - } - PQfreemem(buf); - } - else { // oid - Oid id = 0; - fetch(col,id); - if(id==0) { - throw pqerror("fetching large object failed, oid=0"); - } - int fd = -1; - try { - fd = lo_open(conn_,id,INV_READ | INV_WRITE); - if(fd < 0) - throw pqerror(conn_,"Failed opening large object for read"); - char buf[4096]; - for(;;) { - int n=lo_read(conn_,fd,buf,sizeof(buf)); - if(n < 0) - throw pqerror(conn_,"Failed reading large object"); - if(n>=0) - v.write(buf,n); - if(n < int(sizeof(buf))) - break; - } - int r = lo_close(conn_,fd); - fd = -1; - if(r < 0) - throw pqerror(conn_,"error on close of large object"); - } - catch(...) { - if(fd != -1) - lo_close(conn_,fd); - throw; - } - } - return true; - } - virtual bool fetch(int col,std::tm &v) - { - if(do_isnull(col)) - return false; - v=parse_time(PQgetvalue(res_,current_,col)); - return true; - } - virtual bool is_null(int col) - { - return do_isnull(col); - } - virtual int cols() - { - return cols_; - } - virtual int name_to_column(std::string const &n) - { - return PQfnumber(res_,n.c_str()); - } - virtual std::string column_to_name(int pos) - { - char const *name = PQfname(res_,pos); - if(!name) - return std::string(); - return name; - } - private: - - void check(int c) - { - if(c < 0 || c>= cols_) - throw invalid_column(); - } - bool do_isnull(int col) - { - check(col); - return PQgetisnull(res_,current_,col); - } - PGresult *res_; - PGconn *conn_; - int rows_; - int cols_; - int current_; - blob_type blob_; - std::istringstream ss_; - }; - - class statement : public backend::statement { - public: - - typedef enum { - null_param, - text_param, - binary_param - } param_type; - - statement(PGconn *conn,std::string const &src_query,blob_type b,unsigned long long prepared_id) : - res_(0), - conn_(conn), - orig_query_(src_query), - params_(0), - blob_(b) - { - fmt_.imbue(std::locale::classic()); - - query_.reserve(src_query.size()); - bool inside_string=false; - for(unsigned i=0;i 0) { - - fmt_.str(std::string()); - fmt_.clear(); - fmt_<<"cppdb_psqlstmt_" << prepared_id; - prepared_id_ = fmt_.str(); - fmt_.str(std::string()); - fmt_.clear(); - - PGresult *r=PQprepare(conn_,prepared_id_.c_str(),query_.c_str(),0,0); - try { - if(!r) { - throw pqerror("Failed to create prepared statement object!"); - } - if(PQresultStatus(r)!=PGRES_COMMAND_OK) - throw pqerror(r,"statement preparation failed"); - } - catch(...) { - if(r) PQclear(r); - throw; - } - PQclear(r); - } - } - virtual ~statement() - { - try { - if(res_) { - PQclear(res_); - res_ = 0; - } - if(!prepared_id_.empty()) { - std::string stmt = "DEALLOCATE " + prepared_id_; - res_ = PQexec(conn_,stmt.c_str()); - if(res_) { - PQclear(res_); - res_ = 0; - } - } - } - catch(...) - { - } - } - virtual void reset() - { - if(res_) { - PQclear(res_); - res_ = 0; - } - std::vector vals(params_); - std::vector lengths(params_,0); - std::vector pvals(params_,0); - std::vector flags(params_,null_param); - params_values_.swap(vals); - params_pvalues_.swap(pvals); - params_plengths_.swap(lengths); - params_set_.swap(flags); - } - virtual void bind(int col,std::string const &v) - { - bind(col,v.c_str(),v.c_str()+v.size()); - } - virtual void bind(int col,char const *s) - { - bind(col,s,s+strlen(s)); - } - virtual void bind(int col,char const *b,char const *e) - { - check(col); - params_pvalues_[col-1] = b; - params_plengths_[col-1] = e-b; - params_set_[col-1]=text_param; - } - virtual void bind(int col,std::tm const &v) - { - check(col); - params_values_[col-1]=cppdb::format_time(v); - params_set_[col-1]=text_param; - } - virtual void bind(int col,std::istream &in) - { - check(col); - if(blob_ == bytea_type) { - std::ostringstream ss; - ss << in.rdbuf(); - params_values_[col-1]=ss.str(); - params_set_[col-1]=binary_param; - } - else { - Oid id = 0; - int fd = -1; - try { - id = lo_creat(conn_, INV_READ|INV_WRITE); - if(id == 0) - throw pqerror(conn_,"failed to create large object"); - fd = lo_open(conn_,id,INV_READ | INV_WRITE); - if(fd < 0) - throw pqerror(conn_,"failed to open large object for writing"); - char buf[4096]; - for(;;) { - in.read(buf,sizeof(buf)); - int bytes_read = in.gcount(); - if(bytes_read > 0) { - int n = lo_write(conn_,fd,buf,bytes_read); - if(n < 0) { - throw pqerror(conn_,"failed writing to large object"); - } - } - if(bytes_read < int(sizeof(buf))) - break; - } - int r = lo_close(conn_,fd); - fd=-1; - if(r < 0) - throw pqerror(conn_,"error closing large object after write"); - bind(col,id); - } - catch(...) { - if(fd<-1) - lo_close(conn_,fd); - if(id!=0) - lo_unlink(conn_,id); - throw; - } - } - } - - template - void do_bind(int col,T v) - { - check(col); - fmt_.str(std::string()); - fmt_.clear(); - if(!std::numeric_limits::is_integer) - fmt_ << std::setprecision(std::numeric_limits::digits10+1); - fmt_ << v; - params_values_[col-1]=fmt_.str(); - params_set_[col-1]=text_param; - fmt_.str(std::string()); - fmt_.clear(); - } - - virtual void bind(int col,int v) - { - do_bind(col,v); - } - virtual void bind(int col,unsigned v) - { - do_bind(col,v); - } - virtual void bind(int col,long v) - { - do_bind(col,v); - } - virtual void bind(int col,unsigned long v) - { - do_bind(col,v); - } - virtual void bind(int col,long long v) - { - do_bind(col,v); - } - virtual void bind(int col,unsigned long long v) - { - do_bind(col,v); - } - virtual void bind(int col,double v) - { - do_bind(col,v); - } - virtual void bind(int col,long double v) - { - do_bind(col,v); - } - virtual void bind_null(int col) - { - check(col); - params_set_[col-1]=null_param; - std::string tmp; - params_values_[col-1].swap(tmp); - } - - void real_query() - { - char const * const *pvalues = 0; - int *plengths = 0; - int *pformats = 0; - std::vector values; - std::vector lengths; - std::vector formats; - if(params_>0) { - values.resize(params_,0); - lengths.resize(params_,0); - formats.resize(params_,0); - for(unsigned i=0;i> rowid; - fmt_.str(std::string()); - fmt_.clear(); - } - catch(...) { - if(res) PQclear(res); - throw; - } - PQclear(res); - return rowid; - } - virtual unsigned long long affected() - { - if(res_) { - char const *s=PQcmdTuples(res_); - if(!s || !*s) - return 0; - unsigned long long rows = 0; - fmt_.str(s); - fmt_.clear(); - fmt_ >> rows; - fmt_.str(std::string()); - fmt_.clear(); - return rows; - } - return 0; - } - virtual std::string const &sql_query() - { - return orig_query_; - } - private: - void check(int col) - { - if(col < 1 || col > int(params_)) - throw invalid_placeholder(); - } - PGresult *res_; - PGconn *conn_; - - std::string query_; - std::string orig_query_; - unsigned params_; - std::vector params_values_; - std::vector params_pvalues_; - std::vector params_plengths_; - std::vector params_set_; - std::string prepared_id_; - std::stringstream fmt_; - blob_type blob_; - }; - - class connection : public backend::connection { - public: - void do_simple_exec(char const *s) - { - PGresult *r=PQexec(conn_,s); - try { - - } - catch(...) { - PQclear(r); - throw; - } - PQclear(r); - } - virtual void begin() - { - do_simple_exec("begin"); - } - virtual void commit() - { - do_simple_exec("commit"); - } - virtual void rollback() - { - try { - do_simple_exec("rollback"); - } - catch(...) {} - } - virtual statement *prepare_statement(std::string const &q) - { - return new statement(conn_,q,blob_,++prepared_id_); - } - virtual statement *create_statement(std::string const &q) - { - return new statement(conn_,q,blob_,0); - } - std::string do_escape(char const *b,size_t length) - { - std::vector buf(2*length+1); - size_t len = PQescapeStringConn(conn_,&buf.front(),b,length,0); - return std::string(&buf.front(),len); - } - virtual std::string escape(std::string const &s) - { - return do_escape(s.c_str(),s.size()); - } - virtual std::string escape(char const *s) - { - return do_escape(s,strlen(s)); - } - virtual std::string escape(char const *b,char const *e) - { - return do_escape(b,e-b); - } - std::string pq_string(connection_info const &ci) - { - std::map::const_iterator p; - std::string pq_str; - for(p=ci.properties.begin();p!=ci.properties.end();p++) { - if(p->first.empty() || p->first[0]=='@') - continue; - pq_str+=p->first; - pq_str+="='"; - pq_str+=escape_for_conn(p->second); - pq_str+="' "; - } - return pq_str; - } - std::string escape_for_conn(std::string const &v) - { - std::string res; - res.reserve(v.size()); - for(unsigned i=0;idata_type(col); + } + int result::data_type(std::string const &n) + { + return data_type(index(n)); + } bool result::fetch(int col,short &v) { return res_->fetch(col,v); } bool result::fetch(int col,unsigned short &v) { return res_->fetch(col,v); } @@ -177,6 +185,7 @@ namespace cppdb { bool result::fetch(int col,std::string &v) { return res_->fetch(col,v); } bool result::fetch(int col,std::tm &v) { return res_->fetch(col,v); } bool result::fetch(int col,std::ostream &v) { return res_->fetch(col,v); } + bool result::fetch(int col,bool &v) { return res_->fetch(col,v); } bool result::fetch(std::string const &n,short &v) { return res_->fetch(index(n),v); } bool result::fetch(std::string const &n,unsigned short &v) { return res_->fetch(index(n),v); } @@ -192,6 +201,7 @@ namespace cppdb { bool result::fetch(std::string const &n,std::string &v) { return res_->fetch(index(n),v); } bool result::fetch(std::string const &n,std::tm &v) { return res_->fetch(index(n),v); } bool result::fetch(std::string const &n,std::ostream &v) { return res_->fetch(index(n),v); } + bool result::fetch(std::string const &n,bool &v) { return res_->fetch(index(n),v); } bool result::fetch(short &v) { return res_->fetch(current_col_++,v); } bool result::fetch(unsigned short &v) { return res_->fetch(current_col_++,v); } @@ -207,6 +217,7 @@ namespace cppdb { bool result::fetch(std::string &v) { return res_->fetch(current_col_++,v); } bool result::fetch(std::tm &v) { return res_->fetch(current_col_++,v); } bool result::fetch(std::ostream &v) { return res_->fetch(current_col_++,v); } + bool result::fetch(bool &v) { return res_->fetch(current_col_++,v); } @@ -277,6 +288,11 @@ namespace cppdb { return bind(v); } + statement &statement::operator<<(bool v) + { + return bind(v); + } + statement &statement::operator<<(void (*manipulator)(statement &st)) { manipulator(*this); @@ -353,6 +369,11 @@ namespace cppdb { stat_->bind(placeholder_++,v); return *this; } + statement &statement::bind(bool v) + { + stat_->bind(placeholder_++,v); + return *this; + } statement &statement::bind_null() { stat_->bind_null(placeholder_++); @@ -380,6 +401,10 @@ namespace cppdb { { stat_->bind(col,v); } + void statement::bind(int col,bool v) + { + stat_->bind(col,v); + } void statement::bind(int col,int v) { stat_->bind(col,v); diff --git a/test/dummy_driver.h b/test/dummy_driver.h index 60ea744..f6de3ae 100644 --- a/test/dummy_driver.h +++ b/test/dummy_driver.h @@ -54,7 +54,9 @@ namespace dummy { virtual bool fetch(int,std::string &){ return false; } virtual bool fetch(int,std::ostream &){ return false; } virtual bool fetch(int,std::tm &){ return false; } + virtual bool fetch(int,bool &){ return false; } virtual bool is_null(int){ return true; } + virtual int data_type(int){ return 1700; } virtual int cols() { return 10; } virtual int name_to_column(std::string const &) { return -1; @@ -95,6 +97,7 @@ namespace dummy { virtual void bind(int,unsigned long long){} virtual void bind(int,double){} virtual void bind(int,long double){} + virtual void bind(int,bool){} virtual void bind_null(int){} virtual long long sequence_last(std::string const &/*sequence*/) { throw cppdb::not_supported_by_backend("unsupported"); } virtual unsigned long long affected() { return 0; }