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