18#include <unordered_map>
31#define MAKE_SQLSTATE(ch1,ch2,ch3,ch4,ch5) {ch1,ch2,ch3,ch4,ch5}
33#define PGSQL_STATECODE_LEN 5
34#include <utils/errcodes.h>
57 : result_(result), rows_(0), cols_(0) {
66 rows_ = PQntuples(result);
67 cols_ = PQnfields(result);
73 if (row < 0 || row >= rows_) {
75 <<
", out of range: 0.." << rows_);
87 if (col < 0 || col >= cols_) {
89 <<
", out of range: 0.." << cols_);
101 const char* label = NULL;
104 label = PQfname(result_, col);
106 std::ostringstream os;
107 os <<
"Unknown column:" << col;
115 : conn_(conn), committed_(false) {
135 if (PQstatus(
conn_) == CONNECTION_OK) {
137 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
146std::pair<uint32_t, uint32_t>
150 const string& timer_name) {
154 if (!timer_name.empty()) {
155 conn.makeReconnectCtl(timer_name);
159 conn.openDatabaseInternal(
false);
161 const char* version_sql =
"SELECT version, minor FROM schema_version;";
163 if (PQresultStatus(r) != PGRES_TUPLES_OK) {
165 << version_sql <<
", reason: " << PQerrorMessage(conn.conn_));
174 return (make_pair(
version, minor));
180 const string& timer_name) {
182 bool const retry(parameters.count(
"retry-on-startup") &&
183 parameters.at(
"retry-on-startup") ==
"true");
186 pair<uint32_t, uint32_t> schema_version;
188 schema_version =
getVersion(parameters, ac, cb, retry ? timer_name : string());
193 }
catch (exception
const& exception) {
209 schema_version =
getVersion(parameters, ac, cb, retry ? timer_name : string());
215 if (schema_version != expected_version) {
217 << expected_version.first <<
"." << expected_version.second
218 <<
", found version: " << schema_version.first <<
"."
219 << schema_version.second);
225 if (parameters.count(
"readonly") && parameters.at(
"readonly") ==
"true") {
239 vector<string> kea_admin_parameters(get<0>(tupl));
241 kea_admin_parameters.insert(kea_admin_parameters.begin(),
"db-init");
247 pid_t
const pid(kea_admin.spawn());
248 if (kea_admin.isRunning(pid)) {
251 int const exit_code(kea_admin.getExitStatus(pid));
252 if (exit_code != 0) {
257tuple<vector<string>, vector<string>>
259 vector<string> result{
"pgsql"};
261 for (
auto const& p : params) {
262 string const& keyword(p.first);
263 string const& value(p.second);
266 if (keyword ==
"user" ||
267 keyword ==
"password" ||
271 result.push_back(
"--" + keyword);
272 result.push_back(value);
279 static unordered_map<string, string> conversions{
280 {
"connect-timeout",
"PGCONNECT_TIMEOUT"},
283 if (conversions.count(keyword)) {
284 vars.push_back(conversions.at(keyword) +
"=" + value);
287 return make_tuple(result, vars);
294 statement.nbparams, statement.types));
295 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
297 <<
" name: " << statement.name
298 <<
", reason: " << PQerrorMessage(
conn_)
299 <<
", text: " << statement.text);
308 tagged_statement != end_statement; ++tagged_statement) {
315 return (getConnParametersInternal(
false));
319PgSqlConnection::getConnParametersInternal(
bool logging) {
320 string dbconnparameters;
321 string shost =
"localhost";
328 dbconnparameters +=
"host = '" + shost +
"'" ;
330 unsigned int port = 0;
332 setIntParameterValue(
"port", 0, numeric_limits<uint16_t>::max(), port);
334 }
catch (
const std::exception& ex) {
340 std::ostringstream oss;
342 dbconnparameters +=
" port = " + oss.str();
348 dbconnparameters +=
" user = '" + suser +
"'";
356 dbconnparameters +=
" password = '" + spassword +
"'";
364 dbconnparameters +=
" dbname = '" + sname +
"'";
367 isc_throw(NoDatabaseName,
"must specify a name for the database");
371 unsigned int tcp_user_timeout = 0;
376 setIntParameterValue(
"connect-timeout", 1, numeric_limits<int>::max(), connect_timeout);
381 setIntParameterValue(
"tcp-user-timeout", 0, numeric_limits<int>::max(), tcp_user_timeout);
383 }
catch (
const std::exception& ex) {
388 std::ostringstream oss;
389 oss <<
" connect_timeout = " << connect_timeout;
391 if (tcp_user_timeout > 0) {
393#ifdef HAVE_PGSQL_TCP_USER_TIMEOUT
394 oss <<
" tcp_user_timeout = " << tcp_user_timeout * 1000;
395 static_cast<void>(logging);
402 dbconnparameters += oss.str();
404 return (dbconnparameters);
409 openDatabaseInternal(
true);
413PgSqlConnection::openDatabaseInternal(
bool logging) {
414 std::string dbconnparameters = getConnParametersInternal(logging);
417 PGconn* new_conn = PQconnectdb(dbconnparameters.c_str());
422 if (PQstatus(new_conn) != CONNECTION_OK) {
425 std::string error_message = PQerrorMessage(new_conn);
433 std::ostringstream s;
435 s <<
" (scheduling retry " << rec->retryIndex() + 1 <<
" of " << rec->maxRetries() <<
" in " << rec->retryInterval() <<
" milliseconds)";
437 error_message += s.str();
439 isc_throw(DbOpenErrorWithRetry, error_message);
451 const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE);
453 return ((sqlstate != NULL) &&
460 int s = PQresultStatus(r);
461 if (s != PGRES_COMMAND_OK && s != PGRES_TUPLES_OK) {
466 const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE);
467 if ((sqlstate == NULL) ||
468 ((memcmp(sqlstate,
"08", 2) == 0) ||
469 (memcmp(sqlstate,
"53", 2) == 0) ||
470 (memcmp(sqlstate,
"54", 2) == 0) ||
471 (memcmp(sqlstate,
"57", 2) == 0) ||
472 (memcmp(sqlstate,
"58", 2) == 0))) {
476 .
arg(sqlstate ? sqlstate :
"<sqlstate null>");
487 "fatal database error or connectivity lost");
493 <<
", reason: " << PQerrorMessage(
conn_));
499 <<
", reason: " << PQerrorMessage(
conn_));
503 const char* error_message = PQerrorMessage(
conn_);
505 << statement.name <<
", status: " << s
506 <<
"sqlstate:[ " << (sqlstate ? sqlstate :
"<null>")
507 <<
" ], reason: " << error_message);
521 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
522 const char* error_message = PQerrorMessage(
conn_);
547 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
548 const char* error_message = PQerrorMessage(
conn_);
567 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
568 const char* error_message = PQerrorMessage(
conn_);
580 std::string sql(
"SAVEPOINT " + name);
590 std::string sql(
"ROLLBACK TO SAVEPOINT " + name);
609 if (statement.nbparams != in_bindings.size()) {
611 <<
" expected: " << statement.nbparams
612 <<
" parameters, given: " << in_bindings.size()
613 <<
", statement: " << statement.name
614 <<
", SQL: " << statement.text);
617 const char*
const* values = 0;
618 const int* lengths = 0;
619 const int* formats = 0;
620 if (statement.nbparams > 0) {
621 values =
static_cast<const char* const*
>(&in_bindings.values_[0]);
622 lengths =
static_cast<const int *
>(&in_bindings.lengths_[0]);
623 formats =
static_cast<const int *
>(&in_bindings.formats_[0]);
627 result_set.reset(
new PgSqlResult(PQexecPrepared(
conn_, statement.name, statement.nbparams,
628 values, lengths, formats, 0)));
637 ConsumeResultRowFun process_result_row) {
643 int rows = result_set->getRows();
644 for (
int row = 0; row < rows; ++row) {
646 process_result_row(*result_set, row);
647 }
catch (
const std::exception& ex) {
650 statement.text <<
">");
668 return (boost::lexical_cast<int>(PQcmdTuples(*result_set)));
673PgSqlConnection::setIntParameterValue(
const std::string& name, int64_t min, int64_t max, T& value) {
680 if (svalue.empty()) {
685 auto parsed_value = boost::lexical_cast<T>(svalue);
687 if ((parsed_value < min) || (parsed_value > max)) {
691 value = parsed_value;
698 svalue <<
") must be an integer between "
699 << min <<
" and " << max);
int version()
returns Kea hooks version.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown if a function is called in a prohibited way.
A generic exception that is thrown when an unexpected error condition occurs.
Utility class for spawning new processes.
std::string getParameter(const std::string &name) const
Returns value of a connection parameter.
util::ReconnectCtlPtr reconnectCtl()
The reconnect settings.
void markUnusable()
Sets the unusable flag to true.
static bool test_mode_
Test mode flag (default false).
static bool retry_
Flag which indicates if the database connection should be retried on fail.
void checkUnusable()
Throws an exception if the connection is not usable.
static isc::asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service.
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
Exception thrown when a specific connection has been rendered unusable either through loss of connect...
Exception thrown on failure to open database but permit retries.
Exception thrown on failure to open database.
Exception thrown on failure to execute a database function.
Database duplicate entry error.
Key is NULL but was specified NOT NULL.
Common PgSql Connector Pool.
static bool warned_about_tls
Emit the TLS support warning only once.
void startTransaction()
Starts new transaction.
void rollback()
Rollbacks current transaction.
void createSavepoint(const std::string &name)
Creates a savepoint within the current transaction.
uint64_t updateDeleteQuery(PgSqlTaggedStatement &statement, const PsqlBindArray &in_bindings)
Executes UPDATE or DELETE prepared statement and returns the number of affected rows.
int transaction_ref_count_
Reference counter for transactions.
void selectQuery(PgSqlTaggedStatement &statement, const PsqlBindArray &in_bindings, ConsumeResultRowFun process_result_row)
Executes SELECT query using prepared statement.
bool compareError(const PgSqlResult &r, const char *error_state)
Checks a result set's SQL state against an error state.
std::string getConnParameters()
Creates connection string from specified parameters.
static const char NULL_KEY[]
Define the PgSql error state for a null foreign key error.
void prepareStatement(const PgSqlTaggedStatement &statement)
Prepare Single Statement.
static const char DUPLICATE_KEY[]
Define the PgSql error state for a duplicate key error.
static void ensureSchemaVersion(const ParameterMap ¶meters, const DbCallback &cb=DbCallback(), const std::string &timer_name=std::string())
Retrieve schema version, validate it against the hardcoded version, and attempt to initialize the sch...
PgSqlResultPtr executePreparedStatement(PgSqlTaggedStatement &statement, const PsqlBindArray &in_bindings=PsqlBindArray())
Executes a prepared SQL statement.
bool isTransactionStarted() const
Checks if there is a transaction in progress.
static std::string KEA_ADMIN_
Holds location to kea-admin.
PgSqlHolder conn_
PgSql connection handle.
void rollbackToSavepoint(const std::string &name)
Rollbacks to the given savepoint.
static std::tuple< std::vector< std::string >, std::vector< std::string > > toKeaAdminParameters(ParameterMap const ¶ms)
Convert PostgreSQL library parameters to kea-admin parameters.
static void initializeSchema(const ParameterMap ¶meters)
Initialize schema.
void startRecoverDbConnection()
The recover connection.
void insertQuery(PgSqlTaggedStatement &statement, const PsqlBindArray &in_bindings)
Executes INSERT prepared statement.
void commit()
Commits current transaction.
void executeSQL(const std::string &sql)
Executes the an SQL statement.
static std::pair< uint32_t, uint32_t > getVersion(const ParameterMap ¶meters, const IOServiceAccessorPtr &ac=IOServiceAccessorPtr(), const DbCallback &cb=DbCallback(), const std::string &timer_name=std::string())
Get the schema version.
virtual ~PgSqlConnection()
Destructor.
void checkStatementError(const PgSqlResult &r, PgSqlTaggedStatement &statement)
Checks result of the r object.
void prepareStatements(const PgSqlTaggedStatement *start_statement, const PgSqlTaggedStatement *end_statement)
Prepare statements.
void openDatabase()
Open database with logging.
static void getColumnValue(const PgSqlResult &r, const int row, const size_t col, std::string &value)
Fetches text column value as a string.
void setConnection(PGconn *connection)
Sets the connection to the value given.
RAII wrapper for PostgreSQL Result sets.
void colCheck(int col) const
Determines if a column index is valid.
void rowCheck(int row) const
Determines if a row index is valid.
void rowColCheck(int row, int col) const
Determines if both a row and column index are valid.
~PgSqlResult()
Destructor.
std::string getColumnLabel(const int col) const
Fetches the name of the column in a result set.
PgSqlResult(PGresult *result)
Constructor.
PgSqlTransaction(PgSqlConnection &conn)
Constructor.
void commit()
Commits transaction.
~PgSqlTransaction()
Destructor.
Thrown when an initialization of the schema failed.
We want to reuse the database backend connection and exchange code for other uses,...
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
std::vector< std::string > ProcessEnvVars
Type of the container holding environment variables of the executable being run as a background proce...
const int DB_DBG_TRACE_DETAIL
Database logging levels.
const int PGSQL_DEFAULT_CONNECTION_TIMEOUT
@ PGSQL_TCP_USER_TIMEOUT_UNSUPPORTED
@ PGSQL_START_TRANSACTION
@ PGSQL_INITIALIZE_SCHEMA
boost::shared_ptr< PgSqlResult > PgSqlResultPtr
boost::shared_ptr< IOServiceAccessor > IOServiceAccessorPtr
Pointer to an instance of IOServiceAccessor.
const size_t OID_NONE
Constants for PostgreSQL data types These are defined by PostgreSQL in <catalog/pg_type....
const uint32_t PGSQL_SCHEMA_VERSION_MINOR
std::function< bool(util::ReconnectCtlPtr db_reconnect_ctl)> DbCallback
Defines a callback prototype for propagating events upward.
std::function< isc::asiolink::IOServicePtr()> IOServiceAccessor
Function which returns the IOService that can be used to recover the connection.
const uint32_t PGSQL_SCHEMA_VERSION_MAJOR
Define the PostgreSQL backend version.
bool isFile(string const &path)
Check if there is a file at the given path.
Defines the logger used by the top-level component of kea-lfc.
#define PGSQL_STATECODE_LEN
DB_LOG & arg(T first, Args... args)
Pass parameters to replace logger placeholders.
Define a PostgreSQL statement.