00001 #ifndef PSQL_STATEMENT_H_ 00002 #define PSQL_STATEMENT_H_ 00003 00004 #include "Database.h" 00005 #include "PgSqlException.h" 00006 #include "CopyTypes.h" 00007 #include "BindTypes.h" 00008 #include <libpqtypes.h> 00009 #include <tuple> 00010 #include "../util.h" 00011 00012 namespace psql 00013 { 00014 00015 class IStatement 00016 { 00017 }; 00018 00019 template<typename BindTypes> 00020 PGresult* execBT(PGconn* conn, PGparam* param, std::string const& sql) 00021 { 00022 auto res = PQparamExec(conn, param, sql.c_str(), 1); 00023 if (res == NULL) 00024 throw PgSqlException("Error retrieving statement result: " + std::string(PQgeterror())); 00025 return res; 00026 } 00027 00028 template<> 00029 PGresult* execBT<BindTypes<> >(PGconn* conn, PGparam*, std::string const& sql); 00030 00031 template<typename BindTypes> 00032 PGresult* execPrepBT(PGconn* conn, PGparam* param, std::string const& name) 00033 { 00034 auto res = PQparamExecPrepared(conn, param, name.c_str(), 1); 00035 if (res == NULL) 00036 throw PgSqlException("Error retrieving statement result: " + std::string(PQgeterror())); 00037 return res; 00038 } 00039 00040 template<> 00041 PGresult* execPrepBT<BindTypes<> >(PGconn* conn, PGparam*, std::string const& name); 00042 00043 00050 template < typename BindTypes, typename RetTypes, typename CopyTypes = CopyTypes<> > 00051 class Statement : IStatement 00052 { 00053 private: 00054 void check_param() 00055 { 00056 if (param == NULL) 00057 throw PgSqlException("Error creating PGparam object: " + std::string(PQgeterror())); 00058 } 00059 public: 00063 Statement(): 00064 db(NULL), 00065 param(NULL), 00066 res(NULL), 00067 cp(false) 00068 { 00069 } 00070 00076 Statement(std::string const& sql, Database& db): 00077 db(&db), 00078 sql(sql), 00079 prep(false), 00080 param(PQparamCreate(db.get_db())), 00081 res(NULL), 00082 cp(false) 00083 { 00084 check_param(); 00085 } 00092 Statement(std::string const& name, std::string const& sql, Database& db): 00093 db(&db), 00094 name(name), 00095 sql(sql), 00096 prep(true), 00097 param(PQparamCreate(db.get_db())), 00098 res(NULL), 00099 cp(false) 00100 { 00101 db.regist(name, sql, this); 00102 check_param(); 00103 } 00104 ~Statement() 00105 { 00106 if (db == NULL) 00107 return; 00108 if (prep) 00109 db->unregist(name, this); 00110 if (res != NULL) 00111 PQclear(res); 00112 if (cp) 00113 end_copy(); 00114 PQparamClear(param); 00115 } 00116 00117 Statement& operator=(Statement const&) = delete; 00118 Statement(Statement<BindTypes, RetTypes, CopyTypes> const&) = delete; 00124 Statement& operator=(Statement && other) 00125 { 00126 if (db != NULL) 00127 { 00128 if (prep) 00129 db->unregist(name, this); 00130 if (res != NULL) 00131 PQclear(res); 00132 } 00133 db = other.db; 00134 other.db = NULL; 00135 res = other.res; 00136 prep = other.prep; 00137 sql = other.sql; 00138 name = other.name; 00139 param = other.param; 00140 cp = other.cp; 00141 return *this; 00142 } 00147 Statement(Statement && other): 00148 db(NULL), 00149 param(NULL), 00150 res(NULL) 00151 { 00152 *this = std::move(other); 00153 } 00154 00160 template<typename... Args> 00161 void execute(Args... args) 00162 { 00163 if (res != NULL) 00164 PQclear(res); 00165 res = NULL; 00166 00167 bt.put(param, args...); 00168 if (prep) 00169 res = execPrepBT<BindTypes>(db->get_db(), param, name); 00170 else 00171 res = execBT<BindTypes>(db->get_db(), param, sql); 00172 00173 if (PQresultStatus(res) == PGRES_COPY_IN) 00174 cp = true; 00175 } 00176 00182 typename RetTypes::RowType get_row(int row) 00183 { 00184 if (res == NULL) 00185 throw PgSqlException("get_values called with no result"); 00186 return rt.get_values(res, row); 00187 } 00188 00193 int row_count() 00194 { 00195 if (res == NULL) 00196 return 0; 00197 if (PQresultStatus(res) == PGRES_TUPLES_OK) 00198 return PQntuples(res); 00199 return 0; 00200 } 00201 00206 int64_t affected_rows() 00207 { 00208 if (res == NULL) 00209 return 0; 00210 std::string aff(PQcmdTuples(res)); 00211 if (aff == "") 00212 return 0; 00213 return util::parse<int64_t>(aff); 00214 } 00215 00220 bool copying() 00221 { 00222 return cp; 00223 } 00224 00228 void end_copy() 00229 { 00230 auto conn = db->get_db(); 00231 cp = false; 00232 auto result = PQputCopyEnd(conn, NULL); 00233 if (result == 0) 00234 throw PgSqlException("Sorry copy for asynchronous connections is not implemented"); 00235 if (result == -1) 00236 throw PgSqlException("Error sending end copy request: " + std::string(PQerrorMessage(conn))); 00237 res = PQgetResult(conn); 00238 if (res == NULL 00239 || PQresultStatus(res) == PGRES_BAD_RESPONSE 00240 || PQresultStatus(res) == PGRES_FATAL_ERROR) 00241 throw PgSqlException("Error executing statement: " + std::string(PQerrorMessage(conn))); 00242 00243 } 00244 00249 template<typename... Args> 00250 void copy_data(Args... args) 00251 { 00252 ct.copy(*db, args...); 00253 } 00254 00259 std::string get_sql() const 00260 { 00261 return sql; 00262 } 00263 00264 private: 00265 Database* db; 00266 std::string name; 00267 std::string sql; 00268 bool prep; 00269 BindTypes bt; 00270 RetTypes rt; 00271 CopyTypes ct; 00272 PGparam* param; 00273 PGresult* res; 00274 bool cp; 00275 }; 00276 00277 } 00278 00279 #endif