20#include <unordered_map> 
   46    : result_(result), rows_(0), cols_(0) {
 
   55        rows_ = PQntuples(result);
 
   56        cols_ = PQnfields(result);
 
 
   62    if (row < 0 || row >= rows_) {
 
   64                   << 
", out of range: 0.." << rows_);
 
 
   76    if (col < 0 || col >= cols_) {
 
   78                   << 
", out of range: 0.." << cols_);
 
 
   90    const char* label = NULL;
 
   93        label = PQfname(result_, col);
 
   95        std::ostringstream os;
 
   96        os << 
"Unknown column:" << col;
 
 
  104    : conn_(conn), committed_(false) {
 
  105    conn_.startTransaction();
 
 
  124        if (PQstatus(
conn_) == CONNECTION_OK) {
 
  126            if (PQresultStatus(r) != PGRES_COMMAND_OK) {
 
 
  135std::pair<uint32_t, uint32_t>
 
  139                            const string& timer_name,
 
  144    if (!timer_name.empty()) {
 
  149    conn.openDatabaseInternal(
false);
 
  151    const char* version_sql = 
"SELECT version, minor FROM schema_version;";
 
  153    if (PQresultStatus(r) != PGRES_TUPLES_OK) {
 
  155                  << version_sql << 
", reason: " << PQerrorMessage(conn.
conn_));
 
  164    return (make_pair(
version, minor));
 
 
  170                                     const string& timer_name) {
 
  172    bool const retry(parameters.count(
"retry-on-startup") &&
 
  173                     parameters.at(
"retry-on-startup") == 
"true");
 
  176    pair<uint32_t, uint32_t> schema_version;
 
  178        schema_version = 
getVersion(parameters, ac, 
cb, retry ? timer_name : 
string());
 
  183    } 
catch (exception 
const& exception) {
 
  199        schema_version = 
getVersion(parameters, ac, 
cb, retry ? timer_name : 
string());
 
  205    if (schema_version != expected_version) {
 
  207                                   << expected_version.first << 
"." << expected_version.second
 
  208                                   << 
", found version: " << schema_version.first << 
"." 
  209                                   << schema_version.second);
 
 
  215    if (parameters.count(
"readonly") && parameters.at(
"readonly") == 
"true") {
 
  229    vector<string> kea_admin_parameters(
get<0>(tupl));
 
  231    kea_admin_parameters.insert(kea_admin_parameters.begin(), 
"db-init");
 
  237    pid_t 
const pid(kea_admin.
spawn());
 
  242    if (exit_code != 0) {
 
 
  247tuple<vector<string>, vector<string>>
 
  249    vector<string> result{
"pgsql"};
 
  251    for (
auto const& p : params) {
 
  252        string const& keyword(p.first);
 
  253        string const& value(p.second);
 
  256        if (keyword == 
"user" ||
 
  257            keyword == 
"password" ||
 
  261            result.push_back(
"--" + keyword);
 
  262            result.push_back(value);
 
  269        static unordered_map<string, string> conversions{
 
  270            {
"cert-file", 
"sslcert"},
 
  271            {
"key-file", 
"sslkey"},
 
  272            {
"trust-anchor", 
"sslrootcert"},
 
  273            {
"ssl-mode", 
"sslmode"},
 
  275        if (conversions.count(keyword)) {
 
  276            result.push_back(
"--extra");
 
  277            result.push_back(conversions.at(keyword) + 
"=" + value);
 
  283        static unordered_map<string, string> env_conversions{
 
  284            {
"connect-timeout", 
"PGCONNECT_TIMEOUT"},
 
  287        if (env_conversions.count(keyword)) {
 
  288            vars.push_back(env_conversions.at(keyword) + 
"=" + value);
 
  291    return make_tuple(result, vars);
 
 
  299    if (PQresultStatus(r) != PGRES_COMMAND_OK) {
 
  301                  << 
" name: " << statement.
name 
  302                  << 
", reason: " << PQerrorMessage(
conn_)
 
  303                  << 
", text: " << statement.
text);
 
 
  312         tagged_statement != end_statement; ++tagged_statement) {
 
 
  319    return (getConnParametersInternal(
false));
 
 
  323PgSqlConnection::getConnParametersInternal(
bool logging) {
 
  324    string dbconnparameters;
 
  325    string shost = 
"localhost";
 
  332    dbconnparameters += 
"host = '" + shost + 
"'" ;
 
  334    unsigned int port = 0;
 
  336        setIntParameterValue(
"port", 0, numeric_limits<uint16_t>::max(), port);
 
  338    } 
catch (
const std::exception& ex) {
 
  344        std::ostringstream oss;
 
  346        dbconnparameters += 
" port = " + oss.str();
 
  352        dbconnparameters += 
" user = '" + suser + 
"'";
 
  360        dbconnparameters += 
" password = '" + spassword + 
"'";
 
  364    if (!spassword.empty()) {
 
  372        dbconnparameters += 
" dbname = '" + sname + 
"'";
 
  375        isc_throw(NoDatabaseName, 
"must specify a name for the database");
 
  379    unsigned int tcp_user_timeout = 0;
 
  384        setIntParameterValue(
"connect-timeout", 1, numeric_limits<int>::max(), connect_timeout);
 
  389        setIntParameterValue(
"tcp-user-timeout", 0, numeric_limits<int>::max(), tcp_user_timeout);
 
  391    } 
catch (
const std::exception& ex) {
 
  396    std::ostringstream oss;
 
  397    oss << 
" connect_timeout = " << connect_timeout;
 
  399    if (tcp_user_timeout > 0) {
 
  401#ifdef HAVE_PGSQL_TCP_USER_TIMEOUT 
  402        oss << 
" tcp_user_timeout = " << tcp_user_timeout * 1000;
 
  403        static_cast<void>(logging);
 
  410    dbconnparameters += oss.str();
 
  426        if (ssslmode.empty()) {
 
  427            ssslmode = 
"verify-ca";
 
  429        dbconnparameters += 
" sslrootcert = " + sca;
 
  438        dbconnparameters += 
" sslcert = " + scert;
 
  447        dbconnparameters += 
" sslkey = " + skey;
 
  453        if (ssslmode.empty()) {
 
  454            ssslmode = 
"require";
 
  456        dbconnparameters += 
" gssencmode = disable";
 
  459    if (!ssslmode.empty()) {
 
  460        dbconnparameters += 
" sslmode = " + ssslmode;
 
  463    return (dbconnparameters);
 
  468    openDatabaseInternal(
true);
 
 
  472PgSqlConnection::openDatabaseInternal(
bool logging) {
 
  473    std::string dbconnparameters = getConnParametersInternal(logging);
 
  476    PGconn* new_conn = PQconnectdb(dbconnparameters.c_str());
 
  481    if (PQstatus(new_conn) != CONNECTION_OK) {
 
  487        std::string error_message = PQerrorMessage(new_conn);
 
  496            std::ostringstream s;
 
  498            s << 
" (scheduling retry " << rec->retryIndex() + 1 << 
" of " << rec->maxRetries() << 
" in " << rec->retryInterval() << 
" milliseconds)";
 
  500            error_message += s.str();
 
  502            isc_throw(DbOpenErrorWithRetry, error_message);
 
  509    conn_.setConnection(new_conn);
 
  514    const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE);
 
  516    return ((sqlstate != NULL) &&
 
 
  523    int s = PQresultStatus(r);
 
  524    if (s != PGRES_COMMAND_OK && s != PGRES_TUPLES_OK) {
 
  529        const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE);
 
  530        if ((sqlstate == NULL) ||
 
  531            ((memcmp(sqlstate, 
"08", 2) == 0) ||  
 
  532             (memcmp(sqlstate, 
"53", 2) == 0) ||  
 
  533             (memcmp(sqlstate, 
"54", 2) == 0) ||  
 
  534             (memcmp(sqlstate, 
"57", 2) == 0) ||  
 
  535             (memcmp(sqlstate, 
"58", 2) == 0))) { 
 
  539                .
arg(sqlstate ? sqlstate : 
"<sqlstate null>");
 
  550                      "fatal database error or connectivity lost");
 
  556                      << 
", reason: " << PQerrorMessage(
conn_));
 
  562                      << 
", reason: " << PQerrorMessage(
conn_));
 
  566        const char* error_message = PQerrorMessage(
conn_);
 
  568                  << statement.
name << 
", status: " << s
 
  569                  << 
"sqlstate:[ " << (sqlstate ? sqlstate : 
"<null>")
 
  570                  << 
" ], reason: " << error_message);
 
 
  584    if (PQresultStatus(r) != PGRES_COMMAND_OK) {
 
  585        const char* error_message = PQerrorMessage(
conn_);
 
 
  610    if (PQresultStatus(r) != PGRES_COMMAND_OK) {
 
  611        const char* error_message = PQerrorMessage(
conn_);
 
 
  630    if (PQresultStatus(r) != PGRES_COMMAND_OK) {
 
  631        const char* error_message = PQerrorMessage(
conn_);
 
 
  643    std::string sql(
"SAVEPOINT " + name);
 
 
  653    std::string sql(
"ROLLBACK TO SAVEPOINT " + name);
 
 
  672    if (
static_cast<size_t>(statement.
nbparams) != in_bindings.
size()) {
 
  674                   << 
" expected: " << statement.
nbparams 
  675                   << 
" parameters, given: " << in_bindings.
size()
 
  676                   << 
", statement: " << statement.
name 
  677                   << 
", SQL: " << statement.
text);
 
  680    const char* 
const* values = 0;
 
  681    const int* lengths = 0;
 
  682    const int* formats = 0;
 
  684        values = 
static_cast<const char* const*
>(&in_bindings.
values_[0]);
 
  685        lengths = 
static_cast<const int *
>(&in_bindings.
lengths_[0]);
 
  686        formats = 
static_cast<const int *
>(&in_bindings.
formats_[0]);
 
  691                                                    values, lengths, formats, 0)));
 
 
  706    int rows = result_set->getRows();
 
  707    for (
int row = 0; row < rows; ++row) {
 
  709            process_result_row(*result_set, row);
 
  710        } 
catch (
const std::exception& ex) {
 
  713                      statement.
text << 
">");
 
 
  731    return (boost::lexical_cast<int>(PQcmdTuples(*result_set)));
 
 
  736PgSqlConnection::setIntParameterValue(
const std::string& name, int64_t min, int64_t max, T& value) {
 
  743    if (svalue.empty()) {
 
  748        auto parsed_value = boost::lexical_cast<T>(svalue);
 
  750        if ((parsed_value < min) || (parsed_value > max)) {
 
  751            isc_throw(BadValue, 
"bad " << svalue << 
" value");
 
  754        value = parsed_value;
 
  760        isc_throw(BadValue, name << 
" parameter (" <<
 
  761                  svalue << 
") must be an integer between " 
  762                  << min << 
" and " << max);
 
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
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.
int getExitStatus(const pid_t pid) const
Returns exit status of the process.
bool isRunning(const pid_t pid) const
Checks if the process is still running.
std::string getCommandLine() const
Returns full command line, including arguments, for the process.
pid_t spawn(bool dismiss=false)
Spawn the new process.
std::string getParameter(const std::string &name) const
Returns value of a connection parameter.
util::ReconnectCtlPtr reconnectCtl()
The reconnect settings.
virtual void makeReconnectCtl(const std::string &timer_name, unsigned int id)
Instantiates a ReconnectCtl based on the connection's reconnect parameters.
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.
bool isUnusable()
Flag which indicates if connection is unusable.
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.
std::function< void(PgSqlResult &, int)> ConsumeResultRowFun
Function invoked to process fetched row.
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::pair< uint32_t, uint32_t > getVersion(const ParameterMap ¶meters, const IOServiceAccessorPtr &ac=IOServiceAccessorPtr(), const DbCallback &cb=DbCallback(), const std::string &timer_name=std::string(), unsigned int id=0)
Get the schema version.
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.
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.
PgSqlConnection(const ParameterMap ¶meters, IOServiceAccessorPtr io_accessor=IOServiceAccessorPtr(), DbCallback callback=DbCallback())
Constructor.
static void getColumnValue(const PgSqlResult &r, const int row, const size_t col, std::string &value)
Fetches text column value as a string.
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.
int version()
returns Kea hooks version.
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.
int get(CalloutHandle &handle)
The gss-tsig-get command.
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
constexpr size_t PGSQL_STATECODE_LEN
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.
static void check(const std::string &value)
Check if the value is a default credential.
DB_LOG & arg(T first, Args... args)
Pass parameters to replace logger placeholders.
Define a PostgreSQL statement.
int nbparams
Number of parameters for a given query.
const char * text
Text representation of the actual query.
const char * name
Short name of the query.
const Oid types[PGSQL_MAX_PARAMETERS_IN_QUERY]
OID types.
std::vector< const char * > values_
Vector of pointers to the data values.
std::vector< int > formats_
Vector of "format" for each value.
size_t size() const
Fetches the number of entries in the array.
std::vector< int > lengths_
Vector of data lengths for each value.