From: Marcin Siodelski Date: Thu, 28 Jul 2016 14:21:42 +0000 (+0200) Subject: [4489] Added support for read only mode in PostgreSQL HR backend. X-Git-Tag: trac4631_base~9^2~14 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4e0e41f0609dab1bf85ee50dab43b4d1a66d9195;p=thirdparty%2Fkea.git [4489] Added support for read only mode in PostgreSQL HR backend. --- diff --git a/src/lib/dhcpsrv/pgsql_connection.cc b/src/lib/dhcpsrv/pgsql_connection.cc index 9d722a458a..e79712c2fa 100644 --- a/src/lib/dhcpsrv/pgsql_connection.cc +++ b/src/lib/dhcpsrv/pgsql_connection.cc @@ -129,6 +129,16 @@ PgSqlConnection::prepareStatement(const PgSqlTaggedStatement& statement) { } } +void +PgSqlConnection::prepareStatements(const PgSqlTaggedStatement* start_statement, + const PgSqlTaggedStatement* end_statement) { + // Created the PostgreSQL prepared statements. + for (const PgSqlTaggedStatement* tagged_statement = start_statement; + tagged_statement != end_statement; ++tagged_statement) { + prepareStatement(*tagged_statement); + } +} + void PgSqlConnection::openDatabase() { string dbconnparameters; diff --git a/src/lib/dhcpsrv/pgsql_connection.h b/src/lib/dhcpsrv/pgsql_connection.h index 92bcd4b5ed..2ff1831203 100644 --- a/src/lib/dhcpsrv/pgsql_connection.h +++ b/src/lib/dhcpsrv/pgsql_connection.h @@ -313,6 +313,17 @@ public: /// failed. void prepareStatement(const PgSqlTaggedStatement& statement); + /// @brief Prepare statements + /// + /// Creates the prepared statements for all of the SQL statements used + /// by the PostgreSQL backend. + /// @param tagged_statements an array of statements to be compiled + /// + /// @throw isc::dhcp::DbOperationError An operation on the open database has + /// failed. + void prepareStatements(const PgSqlTaggedStatement* start_statement, + const PgSqlTaggedStatement* end_statement); + /// @brief Open Database /// /// Opens the database using the information supplied in the parameters diff --git a/src/lib/dhcpsrv/pgsql_host_data_source.cc b/src/lib/dhcpsrv/pgsql_host_data_source.cc index 33a75ed262..2d4e57ba03 100644 --- a/src/lib/dhcpsrv/pgsql_host_data_source.cc +++ b/src/lib/dhcpsrv/pgsql_host_data_source.cc @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -35,7 +36,7 @@ namespace { /// @brief Maximum length of option value. /// The maximum size of the raw option data that may be read from the -/// database. +/// database. const size_t OPTION_VALUE_MAX_LEN = 4096; /// @brief Numeric value representing last supported identifier. @@ -1105,10 +1106,6 @@ public: /// /// The contents of the enum are indexes into the list of SQL statements enum StatementIndex { - INSERT_HOST, // Insert new host to collection - INSERT_V6_RESRV, // Insert v6 reservation - INSERT_V4_HOST_OPTION, // Insert DHCPv4 option - INSERT_V6_HOST_OPTION, // Insert DHCPv6 option GET_HOST_DHCPID, // Gets hosts by host identifier GET_HOST_ADDR, // Gets hosts by IPv4 address GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID @@ -1116,9 +1113,20 @@ public: GET_HOST_SUBID_ADDR, // Gets host by IPv4 SubnetID and IPv4 address GET_HOST_PREFIX, // Gets host by IPv6 prefix GET_VERSION, // Obtain version number + INSERT_HOST, // Insert new host to collection + INSERT_V6_RESRV, // Insert v6 reservation + INSERT_V4_HOST_OPTION, // Insert DHCPv4 option + INSERT_V6_HOST_OPTION, // Insert DHCPv6 option NUM_STATEMENTS // Number of statements }; + /// @brief Index of first statement performing write to the database. + /// + /// This value is used to mark border line between queries and other + /// statements and statements performing write operation on the database, + /// such as INSERT, DELETE, UPDATE. + static const StatementIndex WRITE_STMTS_BEGIN = INSERT_HOST; + /// @brief Constructor. /// /// This constructor opens database connection and initializes prepared @@ -1258,66 +1266,25 @@ public: /// @brief MySQL connection PgSqlConnection conn_; + /// @brief Indicates if the database is opened in read only mode. + bool is_readonly_; }; namespace { +/// @brief Array of tagged statements. +typedef boost::array +TaggedStatementArray; + /// @brief Prepared PosgreSQL statements used by the backend to insert and /// retrieve reservation data from the database. -PgSqlTaggedStatement tagged_statements[] = { - // PgSqlHostDataSourceImpl::INSERT_HOST - // Inserts a host into the 'hosts' table. Returns the inserted host id. - {8, - { OID_BYTEA, OID_INT2, - OID_INT4, OID_INT4, OID_INT8, OID_VARCHAR, - OID_VARCHAR, OID_VARCHAR }, - "insert_host", - "INSERT INTO hosts(dhcp_identifier, dhcp_identifier_type, " - " dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, " - " dhcp4_client_classes, dhcp6_client_classes) " - "VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING host_id" - }, - - //PgSqlHostDataSourceImpl::INSERT_V6_RESRV - // Inserts a single IPv6 reservation into 'reservations' table. - {5, - { OID_VARCHAR, OID_INT2, OID_INT4, OID_INT4, OID_INT4 }, - "insert_v6_resrv", - "INSERT INTO ipv6_reservations(address, prefix_len, type, " - " dhcp6_iaid, host_id) " - "VALUES ($1, $2, $3, $4, $5)" - }, - - // PgSqlHostDataSourceImpl::INSERT_V4_HOST_OPTION - // Inserts a single DHCPv4 option into 'dhcp4_options' table. - // Using fixed scope_id = 3, which associates an option with host. - {6, - { OID_INT2, OID_BYTEA, OID_TEXT, - OID_VARCHAR, OID_BOOL, OID_INT8}, - "insert_v4_host_option", - "INSERT INTO dhcp4_options(code, value, formatted_value, space, " - " persistent, host_id, scope_id) " - "VALUES ($1, $2, $3, $4, $5, $6, 3)" - }, - - // PgSqlHostDataSourceImpl::INSERT_V6_HOST_OPTION - // Inserts a single DHCPv6 option into 'dhcp6_options' table. - // Using fixed scope_id = 3, which associates an option with host. - {6, - { OID_INT2, OID_BYTEA, OID_TEXT, - OID_VARCHAR, OID_BOOL, OID_INT8}, - "insert_v6_host_option", - "INSERT INTO dhcp6_options(code, value, formatted_value, space, " - " persistent, host_id, scope_id) " - "VALUES ($1, $2, $3, $4, $5, $6, 3)" - }, - +TaggedStatementArray tagged_statements = { { // PgSqlHostDataSourceImpl::GET_HOST_DHCPID // Retrieves host information, IPv6 reservations and both DHCPv4 and // DHCPv6 options associated with the host. The LEFT JOIN clause is used // to retrieve information from 4 different tables using a single query. // Hence, this query returns multiple rows for a single host. - {2, + {2, { OID_BYTEA, OID_INT2 }, "get_host_dhcpid", "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, " @@ -1417,7 +1384,7 @@ PgSqlTaggedStatement tagged_statements[] = { // are returned due to left joining IPv6 reservations and DHCPv6 options. // The number of rows returned is multiplication of number of existing // IPv6 reservations and DHCPv6 options. - {2, + {2, { OID_VARCHAR, OID_INT2 }, "get_host_prefix", "SELECT h.host_id, h.dhcp_identifier, " @@ -1439,14 +1406,59 @@ PgSqlTaggedStatement tagged_statements[] = { //PgSqlHostDataSourceImpl::GET_VERSION // Retrieves MySQL schema version. - {0, + {0, { OID_NONE }, "get_version", "SELECT version, minor FROM schema_version" }, - // Marks the end of the statements table. - {0, { 0 }, NULL, NULL} + // PgSqlHostDataSourceImpl::INSERT_HOST + // Inserts a host into the 'hosts' table. Returns the inserted host id. + {8, + { OID_BYTEA, OID_INT2, + OID_INT4, OID_INT4, OID_INT8, OID_VARCHAR, + OID_VARCHAR, OID_VARCHAR }, + "insert_host", + "INSERT INTO hosts(dhcp_identifier, dhcp_identifier_type, " + " dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, " + " dhcp4_client_classes, dhcp6_client_classes) " + "VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING host_id" + }, + + //PgSqlHostDataSourceImpl::INSERT_V6_RESRV + // Inserts a single IPv6 reservation into 'reservations' table. + {5, + { OID_VARCHAR, OID_INT2, OID_INT4, OID_INT4, OID_INT4 }, + "insert_v6_resrv", + "INSERT INTO ipv6_reservations(address, prefix_len, type, " + " dhcp6_iaid, host_id) " + "VALUES ($1, $2, $3, $4, $5)" + }, + + // PgSqlHostDataSourceImpl::INSERT_V4_HOST_OPTION + // Inserts a single DHCPv4 option into 'dhcp4_options' table. + // Using fixed scope_id = 3, which associates an option with host. + {6, + { OID_INT2, OID_BYTEA, OID_TEXT, + OID_VARCHAR, OID_BOOL, OID_INT8}, + "insert_v4_host_option", + "INSERT INTO dhcp4_options(code, value, formatted_value, space, " + " persistent, host_id, scope_id) " + "VALUES ($1, $2, $3, $4, $5, $6, 3)" + }, + + // PgSqlHostDataSourceImpl::INSERT_V6_HOST_OPTION + // Inserts a single DHCPv6 option into 'dhcp6_options' table. + // Using fixed scope_id = 3, which associates an option with host. + {6, + { OID_INT2, OID_BYTEA, OID_TEXT, + OID_VARCHAR, OID_BOOL, OID_INT8}, + "insert_v6_host_option", + "INSERT INTO dhcp6_options(code, value, formatted_value, space, " + " persistent, host_id, scope_id) " + "VALUES ($1, $2, $3, $4, $5, $6, 3)" + } +} }; }; // end anonymous namespace @@ -1459,20 +1471,37 @@ PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters) DHCP4_AND_DHCP6)), host_ipv6_reservation_exchange_(new PgSqlIPv6ReservationExchange()), host_option_exchange_(new PgSqlOptionExchange()), - conn_(parameters) { + conn_(parameters), + is_readonly_(false) { // Open the database. conn_.openDatabase(); - int i = 0; - for( ; tagged_statements[i].text != NULL ; ++i) { - conn_.prepareStatement(tagged_statements[i]); + conn_.prepareStatements(tagged_statements.begin(), + tagged_statements.begin() + WRITE_STMTS_BEGIN); + + std::string readonly_value = "false"; + try { + readonly_value = conn_.getParameter("readonly"); + boost::algorithm::to_lower(readonly_value); + } catch (...) { + // Parameter "readonly" hasn't been specified so we simply use + // the default value of "false". + } + + if (readonly_value == "true") { + is_readonly_ = true; + + } else if (readonly_value != "false") { + isc_throw(DbInvalidReadOnly, "invalid value '" << readonly_value + << "' specified for boolean parameter 'readonly'"); } - // Just in case somebody foo-barred things - if (i != NUM_STATEMENTS) { - isc_throw(DbOpenError, "Number of statements prepared: " << i - << " does not match expected count:" << NUM_STATEMENTS); + // If we are using read-write mode for the database we also prepare + // statements for INSERTS etc. + if (!is_readonly_) { + conn_.prepareStatements(tagged_statements.begin() + WRITE_STMTS_BEGIN, + tagged_statements.end()); } } @@ -1637,11 +1666,14 @@ std::pair PgSqlHostDataSourceImpl::getVersion() const { PgSqlHostDataSource:: PgSqlHostDataSource(const PgSqlConnection::ParameterMap& parameters) - : impl_(new PgSqlHostDataSourceImpl(parameters)) { + : impl_(new PgSqlHostDataSourceImpl(parameters), + "PostgreSQL host database backend is configured to" + " operate in read only mode") { + impl_.allowConstOnly(impl_->is_readonly_); } PgSqlHostDataSource::~PgSqlHostDataSource() { - delete impl_; + delete impl_.getPtr(); } void @@ -1894,5 +1926,16 @@ std::pair PgSqlHostDataSource::getVersion() const { return(impl_->getVersion()); } +void +PgSqlHostDataSource::commit() { + impl_->conn_.commit(); +} + + +void +PgSqlHostDataSource::rollback() { + impl_->conn_.rollback(); +} + }; // end of isc::dhcp namespace }; // end of isc namespace diff --git a/src/lib/dhcpsrv/pgsql_host_data_source.h b/src/lib/dhcpsrv/pgsql_host_data_source.h index 2d7ab6c10b..ab062efc3c 100644 --- a/src/lib/dhcpsrv/pgsql_host_data_source.h +++ b/src/lib/dhcpsrv/pgsql_host_data_source.h @@ -8,8 +8,10 @@ #define PGSQL_HOST_DATA_SOURCE_H #include +#include #include #include +#include namespace isc { namespace dhcp { @@ -273,10 +275,20 @@ public: /// has failed. virtual std::pair getVersion() const; + /// @brief Commit Transactions + /// + /// Commits all pending database operations. + virtual void commit(); + + /// @brief Rollback Transactions + /// + /// Rolls back all pending database operations. + virtual void rollback(); + private: /// @brief Pointer to the implementation of the @ref PgSqlHostDataSource. - PgSqlHostDataSourceImpl* impl_; + util::RestrictedConstPtr impl_; }; }