PageRenderTime 39ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/mordor/pq/connection.cpp

http://github.com/mozy/mordor
C++ | 277 lines | 256 code | 20 blank | 1 comment | 36 complexity | 390ac4c1b1f7f4cc61c7796a361ff1ea MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Copyright (c) 2009 - Mozy, Inc.
  2. #include "connection.h"
  3. #include "mordor/assert.h"
  4. #include "mordor/iomanager.h"
  5. #include "mordor/log.h"
  6. #include "exception.h"
  7. #ifdef MSVC
  8. #pragma comment(lib, "libpq")
  9. #endif
  10. namespace Mordor {
  11. namespace PQ {
  12. static Logger::ptr g_log = Log::lookup("mordor:pq");
  13. Connection::Connection(const std::string &conninfo, IOManager *ioManager,
  14. Scheduler *scheduler, bool connectImmediately)
  15. : m_conninfo(conninfo)
  16. , m_exceptioned(false)
  17. {
  18. #ifdef WINDOWS
  19. m_scheduler = scheduler;
  20. #else
  21. m_scheduler = ioManager;
  22. #endif
  23. if (connectImmediately)
  24. connect();
  25. }
  26. ConnStatusType
  27. Connection::status()
  28. {
  29. if (!m_conn || m_exceptioned)
  30. return CONNECTION_BAD;
  31. return PQstatus(m_conn.get());
  32. }
  33. void
  34. Connection::connect()
  35. {
  36. m_exceptioned = false;
  37. #ifdef WINDOWS
  38. SchedulerSwitcher switcher(m_scheduler);
  39. #else
  40. if (m_scheduler) {
  41. m_conn.reset(PQconnectStart(m_conninfo.c_str()), &PQfinish);
  42. if (!m_conn)
  43. MORDOR_THROW_EXCEPTION(std::bad_alloc());
  44. if (status() == CONNECTION_BAD)
  45. throwException(m_conn.get());
  46. if (PQsetnonblocking(m_conn.get(), 1))
  47. throwException(m_conn.get());
  48. int fd = PQsocket(m_conn.get());
  49. PostgresPollingStatusType whatToPoll = PGRES_POLLING_WRITING;
  50. while (true) {
  51. MORDOR_LOG_DEBUG(g_log) << m_conn.get() << " PQconnectPoll(): "
  52. << whatToPoll;
  53. switch (whatToPoll) {
  54. case PGRES_POLLING_READING:
  55. m_scheduler->registerEvent(fd, SchedulerType::READ);
  56. Scheduler::yieldTo();
  57. break;
  58. case PGRES_POLLING_WRITING:
  59. m_scheduler->registerEvent(fd, SchedulerType::WRITE);
  60. Scheduler::yieldTo();
  61. break;
  62. case PGRES_POLLING_FAILED:
  63. throwException(m_conn.get());
  64. case PGRES_POLLING_OK:
  65. MORDOR_LOG_INFO(g_log) << m_conn.get() << " PQconnectStart(\""
  66. << m_conninfo << "\")";
  67. return;
  68. default:
  69. MORDOR_NOTREACHED();
  70. }
  71. whatToPoll = PQconnectPoll(m_conn.get());
  72. }
  73. } else
  74. #endif
  75. {
  76. m_conn.reset(PQconnectdb(m_conninfo.c_str()), &PQfinish);
  77. if (!m_conn)
  78. MORDOR_THROW_EXCEPTION(std::bad_alloc());
  79. if (status() == CONNECTION_BAD)
  80. throwException(m_conn.get());
  81. }
  82. MORDOR_LOG_INFO(g_log) << m_conn.get() << " PQconnectdb(\"" << m_conninfo << "\")";
  83. }
  84. void
  85. Connection::reset()
  86. {
  87. m_exceptioned = false;
  88. #ifdef WINDOWS
  89. SchedulerSwitcher switcher(m_scheduler);
  90. #else
  91. if (m_scheduler) {
  92. if (!PQresetStart(m_conn.get()))
  93. throwException(m_conn.get());
  94. int fd = PQsocket(m_conn.get());
  95. PostgresPollingStatusType whatToPoll = PGRES_POLLING_WRITING;
  96. while (true) {
  97. MORDOR_LOG_DEBUG(g_log) << m_conn.get() << " PQresetPoll(): "
  98. << whatToPoll;
  99. switch (whatToPoll) {
  100. case PGRES_POLLING_READING:
  101. m_scheduler->registerEvent(fd, SchedulerType::READ);
  102. Scheduler::yieldTo();
  103. break;
  104. case PGRES_POLLING_WRITING:
  105. m_scheduler->registerEvent(fd, SchedulerType::WRITE);
  106. Scheduler::yieldTo();
  107. break;
  108. case PGRES_POLLING_FAILED:
  109. throwException(m_conn.get());
  110. case PGRES_POLLING_OK:
  111. MORDOR_LOG_INFO(g_log) << m_conn.get() << " PQresetStart()";
  112. return;
  113. default:
  114. MORDOR_NOTREACHED();
  115. }
  116. whatToPoll = PQresetPoll(m_conn.get());
  117. }
  118. } else
  119. #endif
  120. {
  121. PQreset(m_conn.get());
  122. if (status() == CONNECTION_BAD)
  123. throwException(m_conn.get());
  124. }
  125. MORDOR_LOG_INFO(g_log) << m_conn.get() << " PQreset()";
  126. }
  127. std::string escape(PGconn *conn, const std::string &string)
  128. {
  129. std::string result;
  130. result.resize(string.size() * 2);
  131. int error = 0;
  132. size_t resultSize = PQescapeStringConn(conn, &result[0],
  133. string.c_str(), string.size(), &error);
  134. if (error)
  135. throwException(conn);
  136. result.resize(resultSize);
  137. return result;
  138. }
  139. std::string
  140. Connection::escape(const std::string &string)
  141. {
  142. return PQ::escape(m_conn.get(), string);
  143. }
  144. static std::string escapeBinary(PGconn *conn, const std::string &blob)
  145. {
  146. size_t length;
  147. std::string resultString;
  148. char *result = (char *)PQescapeByteaConn(conn,
  149. (unsigned char *)blob.c_str(), blob.size(), &length);
  150. if (!result)
  151. throwException(conn);
  152. try {
  153. resultString.append(result, length);
  154. } catch (...) {
  155. PQfreemem(result);
  156. throw;
  157. }
  158. PQfreemem(result);
  159. return resultString;
  160. }
  161. std::string
  162. Connection::escapeBinary(const std::string &blob)
  163. {
  164. return PQ::escapeBinary(m_conn.get(), blob);
  165. }
  166. #ifndef WINDOWS
  167. void flush(PGconn *conn, SchedulerType *scheduler)
  168. {
  169. while (true) {
  170. int result = PQflush(conn);
  171. MORDOR_LOG_DEBUG(g_log) << conn << " PQflush(): " << result;
  172. switch (result) {
  173. case 0:
  174. return;
  175. case -1:
  176. throwException(conn);
  177. case 1:
  178. scheduler->registerEvent(PQsocket(conn), SchedulerType::WRITE);
  179. Scheduler::yieldTo();
  180. continue;
  181. default:
  182. MORDOR_NOTREACHED();
  183. }
  184. }
  185. }
  186. PGresult *nextResult(PGconn *conn, SchedulerType *scheduler)
  187. {
  188. while (true) {
  189. if (!PQconsumeInput(conn))
  190. throwException(conn);
  191. if (PQisBusy(conn)) {
  192. MORDOR_LOG_DEBUG(g_log) << conn << " PQisBusy()";
  193. scheduler->registerEvent(PQsocket(conn), SchedulerType::READ);
  194. Scheduler::yieldTo();
  195. continue;
  196. }
  197. MORDOR_LOG_DEBUG(g_log) << conn << " PQconsumeInput()";
  198. return PQgetResult(conn);
  199. }
  200. }
  201. #endif
  202. PreparedStatement
  203. Connection::prepare(const std::string &command, const std::string &name, PreparedStatement::ResultFormat resultFormat)
  204. {
  205. if (!name.empty()) {
  206. #ifdef WINDOWS
  207. SchedulerSwitcher switcher(m_scheduler);
  208. #else
  209. if (m_scheduler) {
  210. if (!PQsendPrepare(m_conn.get(), name.c_str(), command.c_str(), 0, NULL))
  211. throwException(m_conn.get());
  212. flush(m_conn.get(), m_scheduler);
  213. boost::shared_ptr<PGresult> result(nextResult(m_conn.get(), m_scheduler),
  214. &PQclear);
  215. while (result) {
  216. ExecStatusType status = PQresultStatus(result.get());
  217. MORDOR_LOG_DEBUG(g_log) << m_conn.get() << " PQresultStatus("
  218. << result.get() << "): " << PQresStatus(status);
  219. if (status != PGRES_COMMAND_OK)
  220. throwException(result.get());
  221. result.reset(nextResult(m_conn.get(), m_scheduler),
  222. &PQclear);
  223. }
  224. MORDOR_LOG_VERBOSE(g_log) << m_conn.get() << " PQsendPrepare(\""
  225. << name << "\", \"" << command << "\")";
  226. return PreparedStatement(m_conn, std::string(), name, m_scheduler, resultFormat);
  227. } else
  228. #endif
  229. {
  230. boost::shared_ptr<PGresult> result(PQprepare(m_conn.get(),
  231. name.c_str(), command.c_str(), 0, NULL), &PQclear);
  232. if (!result)
  233. throwException(m_conn.get());
  234. ExecStatusType status = PQresultStatus(result.get());
  235. MORDOR_LOG_DEBUG(g_log) << m_conn.get() << " PQresultStatus("
  236. << result.get() << "): " << PQresStatus(status);
  237. if (status != PGRES_COMMAND_OK)
  238. throwException(result.get());
  239. MORDOR_LOG_VERBOSE(g_log) << m_conn.get() << " PQprepare(\"" << name
  240. << "\", \"" << command << "\")";
  241. return PreparedStatement(m_conn, std::string(), name, m_scheduler, resultFormat);
  242. }
  243. } else {
  244. return PreparedStatement(m_conn, command, name, m_scheduler, resultFormat);
  245. }
  246. }
  247. Connection::CopyInParams
  248. Connection::copyIn(const std::string &table)
  249. {
  250. return CopyInParams(table, m_conn, m_scheduler);
  251. }
  252. Connection::CopyOutParams
  253. Connection::copyOut(const std::string &table)
  254. {
  255. return CopyOutParams(table, m_conn, m_scheduler);
  256. }
  257. }}