pgsql_connection.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. // Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #ifndef PGSQL_CONNECTION_H
  7. #define PGSQL_CONNECTION_H
  8. #include <dhcpsrv/database_connection.h>
  9. #include <libpq-fe.h>
  10. #include <boost/scoped_ptr.hpp>
  11. #include <vector>
  12. namespace isc {
  13. namespace dhcp {
  14. // Maximum number of parameters that can be used a statement
  15. // @todo This allows us to use an initializer list (since we can't
  16. // require C++11). It's unlikely we'd go past this many a single
  17. // statement.
  18. const size_t PGSQL_MAX_PARAMETERS_IN_QUERY = 32;
  19. /// @brief Defines a Postgresql SQL statement
  20. ///
  21. /// Each statement is associated with an index, which is used to reference the
  22. /// associated prepared statement.
  23. struct PgSqlTaggedStatement {
  24. /// Number of parameters for a given query
  25. int nbparams;
  26. /// @brief OID types
  27. ///
  28. /// Specify parameter types. See /usr/include/postgresql/catalog/pg_type.h.
  29. /// For some reason that header does not export those parameters.
  30. /// Those OIDs must match both input and output parameters.
  31. const Oid types[PGSQL_MAX_PARAMETERS_IN_QUERY];
  32. /// Short name of the query.
  33. const char* name;
  34. /// Text representation of the actual query.
  35. const char* text;
  36. };
  37. /// @brief Constants for PostgreSQL data types
  38. /// This are defined by PostreSQL in <catalog/pg_type.h>, but including
  39. /// this file is extraordinarily convoluted, so we'll use these to fill-in.
  40. const size_t OID_NONE = 0; // PostgreSQL infers proper type
  41. const size_t OID_BOOL = 16;
  42. const size_t OID_BYTEA = 17;
  43. const size_t OID_INT8 = 20; // 8 byte int
  44. const size_t OID_INT2 = 21; // 2 byte int
  45. const size_t OID_INT4 = 23; // 4 byte int
  46. const size_t OID_TEXT = 25;
  47. const size_t OID_VARCHAR = 1043;
  48. const size_t OID_TIMESTAMP = 1114;
  49. //@}
  50. /// @brief RAII wrapper for Posgtresql Result sets
  51. ///
  52. /// When a Postgresql statement is executed, the results are returned
  53. /// in pointer allocated structure, PGresult*. Data and status information
  54. /// are accessed via calls to functions such as PQgetvalue() which require
  55. /// the results pointer. In order to ensure this structure is freed, any
  56. /// invocation of Psql function which returns a PGresult* (e.g. PQexec and
  57. /// class. Examples:
  58. /// {{{
  59. /// PgSqlResult r(PQexec(conn_, "ROLLBACK"));
  60. /// }}}
  61. ///
  62. /// This eliminates the need for an explicit release via, PQclear() and
  63. /// guarantees that the resources are released even if the an exception is
  64. /// thrown.
  65. class PgSqlResult : public boost::noncopyable {
  66. public:
  67. /// @brief Constructor
  68. ///
  69. /// Store the pointer to the result set to being fetched. Set row
  70. /// and column counts for convenience.
  71. ///
  72. PgSqlResult(PGresult *result);
  73. /// @brief Destructor
  74. ///
  75. /// Frees the result set
  76. ~PgSqlResult();
  77. /// @brief Returns the number of rows in the result set.
  78. int getRows() const {
  79. return (rows_);
  80. }
  81. /// @brief Returns the number of columns in the result set.
  82. int getCols() const {
  83. return (cols_);
  84. }
  85. /// @brief Determines if a row index is valid
  86. ///
  87. /// @param row index to range check
  88. ///
  89. /// @throw DbOperationError if the row index is out of range
  90. void rowCheck(int row) const;
  91. /// @brief Determines if a column index is valid
  92. ///
  93. /// @param col index to range check
  94. ///
  95. /// @throw DbOperationError if the column index is out of range
  96. void colCheck(int col) const;
  97. /// @brief Determines if both a row and column index are valid
  98. ///
  99. /// @param row index to range check
  100. /// @param col index to range check
  101. ///
  102. /// @throw DbOperationError if either the row or column index
  103. /// is out of range
  104. void rowColCheck(int row, int col) const;
  105. /// @brief Fetches the name of the column in a result set
  106. ///
  107. /// Returns the column name of the column from the result set.
  108. /// If the column index is out of range it will return the
  109. /// string "Unknown column:<index>"
  110. ///
  111. /// @param col index of the column name to fetch
  112. /// @return string containing the name of the column
  113. /// This method is exception safe.
  114. std::string getColumnLabel(const int col) const;
  115. /// @brief Conversion Operator
  116. ///
  117. /// Allows the PgSqlResult object to be passed as the result set argument to
  118. /// PQxxxx functions.
  119. operator PGresult*() const {
  120. return (result_);
  121. }
  122. /// @brief Boolean Operator
  123. ///
  124. /// Allows testing the PgSqlResult object for emptiness: "if (result)"
  125. operator bool() const {
  126. return (result_);
  127. }
  128. private:
  129. PGresult* result_; ///< Result set to be freed
  130. int rows_; ///< Number of rows in the result set
  131. int cols_; ///< Number of columns in the result set
  132. };
  133. /// @brief Postgresql connection handle Holder
  134. ///
  135. /// Small RAII object for safer initialization, will close the database
  136. /// connection upon destruction. This means that if an exception is thrown
  137. /// during database initialization, resources allocated to the database are
  138. /// guaranteed to be freed.
  139. ///
  140. /// It makes no sense to copy an object of this class. After the copy, both
  141. /// objects would contain pointers to the same PgSql context object. The
  142. /// destruction of one would invalid the context in the remaining object.
  143. /// For this reason, the class is declared noncopyable.
  144. class PgSqlHolder : public boost::noncopyable {
  145. public:
  146. /// @brief Constructor
  147. ///
  148. /// Sets the Postgresql API connector handle to NULL.
  149. ///
  150. PgSqlHolder() : pgconn_(NULL) {
  151. }
  152. /// @brief Destructor
  153. ///
  154. /// Frees up resources allocated by the connection.
  155. ~PgSqlHolder() {
  156. if (pgconn_ != NULL) {
  157. PQfinish(pgconn_);
  158. }
  159. }
  160. /// @brief Sets the connection to the value given
  161. ///
  162. /// @param connection - pointer to the Postgresql connection instance
  163. void setConnection(PGconn* connection) {
  164. if (pgconn_ != NULL) {
  165. // Already set? Release the current connection first.
  166. // Maybe this should be an error instead?
  167. PQfinish(pgconn_);
  168. }
  169. pgconn_ = connection;
  170. }
  171. /// @brief Conversion Operator
  172. ///
  173. /// Allows the PgSqlHolder object to be passed as the context argument to
  174. /// PQxxxx functions.
  175. operator PGconn*() const {
  176. return (pgconn_);
  177. }
  178. /// @brief Boolean Operator
  179. ///
  180. /// Allows testing the connection for emptiness: "if (holder)"
  181. operator bool() const {
  182. return (pgconn_);
  183. }
  184. private:
  185. PGconn* pgconn_; ///< Postgresql connection
  186. };
  187. /// @brief Forward declaration to @ref PgSqlConnection.
  188. class PgSqlConnection;
  189. /// @brief RAII object representing a PostgreSQL transaction.
  190. ///
  191. /// An instance of this class should be created in a scope where multiple
  192. /// INSERT statements should be executed within a single transaction. The
  193. /// transaction is started when the constructor of this class is invoked.
  194. /// The transaction is ended when the @ref PgSqlTransaction::commit is
  195. /// explicitly called or when the instance of this class is destroyed.
  196. /// The @ref PgSqlTransaction::commit commits changes to the database.
  197. /// If the class instance is destroyed before @ref PgSqlTransaction::commit
  198. /// has been called, the transaction is rolled back. The rollback on
  199. /// destruction guarantees that partial data is not stored in the database
  200. /// when an error occurs during any of the operations within a transaction.
  201. ///
  202. /// By default PostgreSQL performs a commit following each statement which
  203. /// alters the database (i.e. "autocommit"). Starting a transaction
  204. /// stops autocommit for the connection until the transaction is ended by
  205. /// either commit or rollback. Other connections are unaffected.
  206. class PgSqlTransaction : public boost::noncopyable {
  207. public:
  208. /// @brief Constructor.
  209. ///
  210. /// Starts transaction by executing the SQL statement: "START TRANSACTION"
  211. ///
  212. /// @param conn PostgreSQL connection to use for the transaction. This
  213. /// connection will be later used to commit or rollback changes.
  214. ///
  215. /// @throw DbOperationError if statement execution fails
  216. PgSqlTransaction(PgSqlConnection& conn);
  217. /// @brief Destructor.
  218. ///
  219. /// If the transaction has not been committed, it is rolled back
  220. /// by executing the SQL statement: "ROLLBACK"
  221. ///
  222. /// @throw DbOperationError if statement execution fails
  223. ~PgSqlTransaction();
  224. /// @brief Commits transaction.
  225. ///
  226. /// Commits all changes made during the transaction by executing the
  227. /// SQL statement: "COMMIT"
  228. ///
  229. /// @throw DbOperationError if statement execution fails
  230. void commit();
  231. private:
  232. /// @brief Holds reference to the PostgreSQL database connection.
  233. PgSqlConnection& conn_;
  234. /// @brief Boolean flag indicating if the transaction has been committed.
  235. ///
  236. /// This flag is used in the class destructor to assess if the
  237. /// transaction should be rolled back.
  238. bool committed_;
  239. };
  240. /// @brief Common PgSql Connector Pool
  241. ///
  242. /// This class provides common operations for PgSql database connection
  243. /// used by both PgSqlLeaseMgr and PgSqlHostDataSource. It manages connecting
  244. /// to the database and preparing compiled statements. Its fields are
  245. /// public, because they are used (both set and retrieved) in classes
  246. /// that use instances of PgSqlConnection.
  247. class PgSqlConnection : public DatabaseConnection {
  248. public:
  249. /// @brief Defines the PgSql error state for a duplicate key error
  250. static const char DUPLICATE_KEY[];
  251. /// @brief Constructor
  252. ///
  253. /// Initialize PgSqlConnection object with parameters needed for connection.
  254. PgSqlConnection(const ParameterMap& parameters)
  255. : DatabaseConnection(parameters) {
  256. }
  257. /// @brief Destructor
  258. virtual ~PgSqlConnection();
  259. /// @brief Prepare Single Statement
  260. ///
  261. /// Creates a prepared statement from the text given and adds it to the
  262. /// statements_ vector at the given index.
  263. ///
  264. /// @param statement SQL statement to be prepared.
  265. ///
  266. /// @throw isc::dhcp::DbOperationError An operation on the open database has
  267. /// failed.
  268. void prepareStatement(const PgSqlTaggedStatement& statement);
  269. /// @brief Prepare statements
  270. ///
  271. /// Creates the prepared statements for all of the SQL statements used
  272. /// by the PostgreSQL backend.
  273. ///
  274. /// @param start_statement Pointer to the first statement in range of the
  275. /// statements to be compiled.
  276. /// @param end_statement Pointer to the statement marking end of the
  277. /// range of statements to be compiled. This last statement is not compiled.
  278. ///
  279. /// @throw isc::dhcp::DbOperationError An operation on the open database has
  280. /// failed.
  281. void prepareStatements(const PgSqlTaggedStatement* start_statement,
  282. const PgSqlTaggedStatement* end_statement);
  283. /// @brief Open Database
  284. ///
  285. /// Opens the database using the information supplied in the parameters
  286. /// passed to the constructor.
  287. ///
  288. /// @throw NoDatabaseName Mandatory database name not given
  289. /// @throw DbOpenError Error opening the database
  290. void openDatabase();
  291. /// @brief Start a transaction
  292. ///
  293. /// Starts a transaction.
  294. ///
  295. /// @throw DbOperationError If the transaction start failed.
  296. void startTransaction();
  297. /// @brief Commit Transactions
  298. ///
  299. /// Commits all pending database operations.
  300. ///
  301. /// @throw DbOperationError If the commit failed.
  302. void commit();
  303. /// @brief Rollback Transactions
  304. ///
  305. /// Rolls back all pending database operations.
  306. ///
  307. /// @throw DbOperationError If the rollback failed.
  308. void rollback();
  309. /// @brief Checks a result set's SQL state against an error state.
  310. ///
  311. /// @param r result set to check
  312. /// @param error_state error state to compare against
  313. ///
  314. /// @return True if the result set's SQL state equals the error_state,
  315. /// false otherwise.
  316. bool compareError(const PgSqlResult& r, const char* error_state);
  317. /// @brief Checks result of the r object
  318. ///
  319. /// This function is used to determine whether or not the SQL statement
  320. /// execution succeeded, and in the event of failures, decide whether or
  321. /// not the failures are recoverable.
  322. ///
  323. /// If the error is recoverable, the method will throw a DbOperationError.
  324. /// In the error is deemed unrecoverable, such as a loss of connectivity
  325. /// with the server, this method will log the error and call exit(-1);
  326. ///
  327. /// @todo Calling exit() is viewed as a short term solution for Kea 1.0.
  328. /// Two tickets are likely to alter this behavior, first is #3639, which
  329. /// calls for the ability to attempt to reconnect to the database. The
  330. /// second ticket, #4087 which calls for the implementation of a generic,
  331. /// FatalException class which will propagate outward.
  332. ///
  333. /// @param r result of the last PostgreSQL operation
  334. /// @param statement - tagged statement that was executed
  335. ///
  336. /// @throw isc::dhcp::DbOperationError Detailed PostgreSQL failure
  337. void checkStatementError(const PgSqlResult& r,
  338. PgSqlTaggedStatement& statement) const;
  339. /// @brief PgSql connection handle
  340. ///
  341. /// This field is public, because it is used heavily from PgSqlLeaseMgr
  342. /// and from PgSqlHostDataSource.
  343. PgSqlHolder conn_;
  344. /// @brief Conversion Operator
  345. ///
  346. /// Allows the PgConnection object to be passed as the context argument to
  347. /// PQxxxx functions.
  348. operator PGconn*() const {
  349. return (conn_);
  350. }
  351. /// @brief Boolean Operator
  352. ///
  353. /// Allows testing the PgConnection for initialized connection
  354. operator bool() const {
  355. return (conn_);
  356. }
  357. };
  358. }; // end of isc::dhcp namespace
  359. }; // end of isc namespace
  360. #endif // PGSQL_CONNECTION_H