1 // Copyright (C) 2016-2021 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 9 #include <asiolink/io_service.h> 10 #include <database/database_connection.h> 11 12 #include <libpq-fe.h> 13 #include <boost/scoped_ptr.hpp> 14 15 #include <vector> 16 #include <stdint.h> 17 18 namespace isc { 19 namespace db { 20 21 /// @brief Define PostgreSQL backend version: 6.2 22 const uint32_t PG_SCHEMA_VERSION_MAJOR = 6; 23 const uint32_t PG_SCHEMA_VERSION_MINOR = 2; 24 25 // Maximum number of parameters that can be used a statement 26 // @todo This allows us to use an initializer list (since we can't 27 // require C++11). It's unlikely we'd go past this many a single 28 // statement. 29 const size_t PGSQL_MAX_PARAMETERS_IN_QUERY = 32; 30 31 /// @brief Define a PostgreSQL statement. 32 /// 33 /// Each statement is associated with an index, which is used to reference the 34 /// associated prepared statement. 35 struct PgSqlTaggedStatement { 36 /// Number of parameters for a given query 37 int nbparams; 38 39 /// @brief OID types 40 /// 41 /// Specify parameter types. See /usr/include/postgresql/catalog/pg_type.h. 42 /// For some reason that header does not export those parameters. 43 /// Those OIDs must match both input and output parameters. 44 const Oid types[PGSQL_MAX_PARAMETERS_IN_QUERY]; 45 46 /// Short name of the query. 47 const char* name; 48 49 /// Text representation of the actual query. 50 const char* text; 51 }; 52 53 /// @{ 54 /// @brief Constants for PostgreSQL data types 55 /// These are defined by PostgreSQL in <catalog/pg_type.h>, but including 56 /// this file is extraordinarily convoluted, so we'll use these to fill-in. 57 /// @{ 58 const size_t OID_NONE = 0; // PostgreSQL infers proper type 59 const size_t OID_BOOL = 16; 60 const size_t OID_BYTEA = 17; 61 const size_t OID_INT8 = 20; // 8 byte int 62 const size_t OID_INT2 = 21; // 2 byte int 63 const size_t OID_INT4 = 23; // 4 byte int 64 const size_t OID_TEXT = 25; 65 const size_t OID_VARCHAR = 1043; 66 const size_t OID_TIMESTAMP = 1114; 67 /// @} 68 69 /// @brief RAII wrapper for PostgreSQL Result sets 70 /// 71 /// When a Postgresql statement is executed, the results are returned 72 /// in pointer allocated structure, PGresult*. Data and status information 73 /// are accessed via calls to functions such as PQgetvalue() which require 74 /// the results pointer. In order to ensure this structure is freed, any 75 /// invocation of Psql function which returns a PGresult* (e.g. PQexec and 76 77 /// class. Examples: 78 /// {{{ 79 /// PgSqlResult r(PQexec(conn_, "ROLLBACK")); 80 /// }}} 81 /// 82 /// This eliminates the need for an explicit release via, PQclear() and 83 /// guarantees that the resources are released even if the an exception is 84 /// thrown. 85 86 class PgSqlResult : public boost::noncopyable { 87 public: 88 /// @brief Constructor 89 /// 90 /// Store the pointer to the result set to being fetched. Set row 91 /// and column counts for convenience. 92 /// 93 /// @param result - pointer to the Postgresql client layer result 94 /// If the value of is NULL, row and col values will be set to -1. 95 /// This allows PgSqlResult to be passed into statement error 96 /// checking. 97 PgSqlResult(PGresult *result); 98 99 /// @brief Destructor 100 /// 101 /// Frees the result set 102 ~PgSqlResult(); 103 104 /// @brief Returns the number of rows in the result set. getRows()105 int getRows() const { 106 return (rows_); 107 } 108 109 /// @brief Returns the number of columns in the result set. getCols()110 int getCols() const { 111 return (cols_); 112 } 113 114 /// @brief Determines if a row index is valid 115 /// 116 /// @param row index to range check 117 /// 118 /// @throw DbOperationError if the row index is out of range 119 void rowCheck(int row) const; 120 121 /// @brief Determines if a column index is valid 122 /// 123 /// @param col index to range check 124 /// 125 /// @throw DbOperationError if the column index is out of range 126 void colCheck(int col) const; 127 128 /// @brief Determines if both a row and column index are valid 129 /// 130 /// @param row index to range check 131 /// @param col index to range check 132 /// 133 /// @throw DbOperationError if either the row or column index 134 /// is out of range 135 void rowColCheck(int row, int col) const; 136 137 /// @brief Fetches the name of the column in a result set 138 /// 139 /// Returns the column name of the column from the result set. 140 /// If the column index is out of range it will return the 141 /// string "Unknown column:<index>" 142 /// 143 /// @param col index of the column name to fetch 144 /// @return string containing the name of the column 145 /// This method is exception safe. 146 std::string getColumnLabel(const int col) const; 147 148 /// @brief Conversion Operator 149 /// 150 /// Allows the PgSqlResult object to be passed as the result set argument to 151 /// PQxxxx functions. 152 operator PGresult*() const { 153 return (result_); 154 } 155 156 /// @brief Boolean Operator 157 /// 158 /// Allows testing the PgSqlResult object for emptiness: "if (result)" 159 operator bool() const { 160 return (result_); 161 } 162 163 private: 164 PGresult* result_; ///< Result set to be freed 165 int rows_; ///< Number of rows in the result set 166 int cols_; ///< Number of columns in the result set 167 }; 168 169 170 /// @brief Postgresql connection handle Holder 171 /// 172 /// Small RAII object for safer initialization, will close the database 173 /// connection upon destruction. This means that if an exception is thrown 174 /// during database initialization, resources allocated to the database are 175 /// guaranteed to be freed. 176 /// 177 /// It makes no sense to copy an object of this class. After the copy, both 178 /// objects would contain pointers to the same PgSql context object. The 179 /// destruction of one would invalid the context in the remaining object. 180 /// For this reason, the class is declared noncopyable. 181 class PgSqlHolder : public boost::noncopyable { 182 public: 183 184 /// @brief Constructor 185 /// 186 /// Sets the Postgresql API connector handle to NULL. 187 /// PgSqlHolder()188 PgSqlHolder() : pgconn_(NULL) { 189 } 190 191 /// @brief Destructor 192 /// 193 /// Frees up resources allocated by the connection. ~PgSqlHolder()194 ~PgSqlHolder() { 195 if (pgconn_ != NULL) { 196 PQfinish(pgconn_); 197 } 198 } 199 200 /// @brief Sets the connection to the value given 201 /// 202 /// @param connection - pointer to the Postgresql connection instance setConnection(PGconn * connection)203 void setConnection(PGconn* connection) { 204 if (pgconn_ != NULL) { 205 // Already set? Release the current connection first. 206 // Maybe this should be an error instead? 207 PQfinish(pgconn_); 208 } 209 210 pgconn_ = connection; 211 } 212 213 /// @brief Conversion Operator 214 /// 215 /// Allows the PgSqlHolder object to be passed as the context argument to 216 /// PQxxxx functions. 217 operator PGconn*() const { 218 return (pgconn_); 219 } 220 221 /// @brief Boolean Operator 222 /// 223 /// Allows testing the connection for emptiness: "if (holder)" 224 operator bool() const { 225 return (pgconn_); 226 } 227 228 private: 229 PGconn* pgconn_; ///< Postgresql connection 230 }; 231 232 /// @brief Forward declaration to @ref PgSqlConnection. 233 class PgSqlConnection; 234 235 /// @brief RAII object representing a PostgreSQL transaction. 236 /// 237 /// An instance of this class should be created in a scope where multiple 238 /// INSERT statements should be executed within a single transaction. The 239 /// transaction is started when the constructor of this class is invoked. 240 /// The transaction is ended when the @ref PgSqlTransaction::commit is 241 /// explicitly called or when the instance of this class is destroyed. 242 /// The @ref PgSqlTransaction::commit commits changes to the database. 243 /// If the class instance is destroyed before @ref PgSqlTransaction::commit 244 /// has been called, the transaction is rolled back. The rollback on 245 /// destruction guarantees that partial data is not stored in the database 246 /// when an error occurs during any of the operations within a transaction. 247 /// 248 /// By default PostgreSQL performs a commit following each statement which 249 /// alters the database (i.e. "autocommit"). Starting a transaction 250 /// stops autocommit for the connection until the transaction is ended by 251 /// either commit or rollback. Other connections are unaffected. 252 class PgSqlTransaction : public boost::noncopyable { 253 public: 254 255 /// @brief Constructor. 256 /// 257 /// Starts transaction by executing the SQL statement: "START TRANSACTION" 258 /// 259 /// @param conn PostgreSQL connection to use for the transaction. This 260 /// connection will be later used to commit or rollback changes. 261 /// 262 /// @throw DbOperationError if statement execution fails 263 PgSqlTransaction(PgSqlConnection& conn); 264 265 /// @brief Destructor. 266 /// 267 /// If the transaction has not been committed, it is rolled back 268 /// by executing the SQL statement: "ROLLBACK" 269 /// 270 /// @throw DbOperationError if statement execution fails 271 ~PgSqlTransaction(); 272 273 /// @brief Commits transaction. 274 /// 275 /// Commits all changes made during the transaction by executing the 276 /// SQL statement: "COMMIT" 277 /// 278 /// @throw DbOperationError if statement execution fails 279 void commit(); 280 281 private: 282 283 /// @brief Holds reference to the PostgreSQL database connection. 284 PgSqlConnection& conn_; 285 286 /// @brief Boolean flag indicating if the transaction has been committed. 287 /// 288 /// This flag is used in the class destructor to assess if the 289 /// transaction should be rolled back. 290 bool committed_; 291 }; 292 293 /// @brief Common PgSql Connector Pool 294 /// 295 /// This class provides common operations for PgSql database connection 296 /// used by both PgSqlLeaseMgr and PgSqlHostDataSource. It manages connecting 297 /// to the database and preparing compiled statements. Its fields are 298 /// public, because they are used (both set and retrieved) in classes 299 /// that use instances of PgSqlConnection. 300 class PgSqlConnection : public db::DatabaseConnection { 301 public: 302 /// @brief Define the PgSql error state for a duplicate key error. 303 static const char DUPLICATE_KEY[]; 304 305 /// @brief Constructor 306 /// 307 /// Initialize PgSqlConnection object with parameters needed for connection. 308 /// 309 /// @param parameters Specify the connection details. 310 /// @param io_accessor The IOService accessor function. 311 /// @param callback The connection recovery callback. 312 PgSqlConnection(const ParameterMap& parameters, 313 IOServiceAccessorPtr io_accessor = IOServiceAccessorPtr(), 314 DbCallback callback = DbCallback()) DatabaseConnection(parameters,callback)315 : DatabaseConnection(parameters, callback), 316 io_service_accessor_(io_accessor), io_service_() { 317 } 318 319 /// @brief Destructor 320 virtual ~PgSqlConnection(); 321 322 /// @brief Get the schema version. 323 /// 324 /// @param parameters A data structure relating keywords and values 325 /// concerned with the database. 326 /// 327 /// @return Version number as a pair of unsigned integers. "first" is the 328 /// major version number, "second" the minor number. 329 /// 330 /// @throw isc::db::DbOperationError An operation on the open database has 331 /// failed. 332 static std::pair<uint32_t, uint32_t> 333 getVersion(const ParameterMap& parameters); 334 335 /// @brief Prepare Single Statement 336 /// 337 /// Creates a prepared statement from the text given and adds it to the 338 /// statements_ vector at the given index. 339 /// 340 /// @param statement SQL statement to be prepared. 341 /// 342 /// @throw isc::db::DbOperationError An operation on the open database has 343 /// failed. 344 void prepareStatement(const PgSqlTaggedStatement& statement); 345 346 /// @brief Prepare statements 347 /// 348 /// Creates the prepared statements for all of the SQL statements used 349 /// by the PostgreSQL backend. 350 /// 351 /// @param start_statement Pointer to the first statement in range of the 352 /// statements to be compiled. 353 /// @param end_statement Pointer to the statement marking end of the 354 /// range of statements to be compiled. This last statement is not compiled. 355 /// 356 /// @throw isc::db::DbOperationError An operation on the open database has 357 /// failed. 358 void prepareStatements(const PgSqlTaggedStatement* start_statement, 359 const PgSqlTaggedStatement* end_statement); 360 361 /// @brief Open Database 362 /// 363 /// Opens the database using the information supplied in the parameters 364 /// passed to the constructor. 365 /// 366 /// @throw NoDatabaseName Mandatory database name not given 367 /// @throw DbOpenError Error opening the database 368 void openDatabase(); 369 370 /// @brief Start a transaction 371 /// 372 /// Starts a transaction. 373 /// 374 /// @throw DbOperationError If the transaction start failed. 375 void startTransaction(); 376 377 /// @brief Commit Transactions 378 /// 379 /// Commits all pending database operations. 380 /// 381 /// @throw DbOperationError If the commit failed. 382 void commit(); 383 384 /// @brief Rollback Transactions 385 /// 386 /// Rolls back all pending database operations. 387 /// 388 /// @throw DbOperationError If the rollback failed. 389 void rollback(); 390 391 /// @brief Checks a result set's SQL state against an error state. 392 /// 393 /// @param r result set to check 394 /// @param error_state error state to compare against 395 /// 396 /// @return True if the result set's SQL state equals the error_state, 397 /// false otherwise. 398 bool compareError(const PgSqlResult& r, const char* error_state); 399 400 /// @brief Checks result of the r object 401 /// 402 /// This function is used to determine whether or not the SQL statement 403 /// execution succeeded, and in the event of failures, decide whether or 404 /// not the failures are recoverable. 405 /// 406 /// If the error is recoverable, the function will throw a DbOperationError. 407 /// If the error is deemed unrecoverable, such as a loss of connectivity 408 /// with the server, the function will call startRecoverDbConnection() which 409 /// will start the connection recovery. 410 /// 411 /// If the invocation returns true, this indicates the calling layer will 412 /// attempt recovery, and the function throws a DbOperationError to allow 413 /// the caller to error handle the failed db access attempt. 414 /// 415 /// @param r result of the last PostgreSQL operation 416 /// @param statement - tagged statement that was executed 417 /// 418 /// @throw isc::db::DbOperationError Detailed PostgreSQL failure 419 void checkStatementError(const PgSqlResult& r, 420 PgSqlTaggedStatement& statement); 421 422 /// @brief The recover connection 423 /// 424 /// This function starts the recover process of the connection. 425 /// 426 /// @note The recover function must be run on the IO Service thread. startRecoverDbConnection()427 void startRecoverDbConnection() { 428 if (callback_) { 429 if (!io_service_ && io_service_accessor_) { 430 io_service_ = (*io_service_accessor_)(); 431 io_service_accessor_.reset(); 432 } 433 434 if (io_service_) { 435 io_service_->post(std::bind(callback_, reconnectCtl())); 436 } 437 } 438 } 439 440 /// @brief PgSql connection handle 441 /// 442 /// This field is public, because it is used heavily from PgSqlLeaseMgr 443 /// and from PgSqlHostDataSource. 444 PgSqlHolder conn_; 445 446 /// @brief Conversion Operator 447 /// 448 /// Allows the PgConnection object to be passed as the context argument to 449 /// PQxxxx functions. 450 operator PGconn*() const { 451 return (conn_); 452 } 453 454 /// @brief Boolean Operator 455 /// 456 /// Allows testing the PgConnection for initialized connection 457 operator bool() const { 458 return (conn_); 459 } 460 461 /// @brief Accessor function which returns the IOService that can be used to 462 /// recover the connection. 463 /// 464 /// This accessor is used to lazy retrieve the IOService when the connection 465 /// is lost. It is useful to retrieve it at a later time to support hook 466 /// libraries which create managers on load and set IOService later on by 467 /// using the dhcp4_srv_configured and dhcp6_srv_configured hooks. 468 IOServiceAccessorPtr io_service_accessor_; 469 470 /// @brief IOService object, used for all ASIO operations. 471 isc::asiolink::IOServicePtr io_service_; 472 }; 473 474 } // end of isc::db namespace 475 } // end of isc namespace 476 477 #endif // PGSQL_CONNECTION_H 478