Kea 2.7.7
pgsql_connection.cc
Go to the documentation of this file.
1// Copyright (C) 2016-2025 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
7#include <config.h>
8
14#include <database/db_log.h>
16#include <util/filesystem.h>
17
18#include <exception>
19#include <sstream>
20#include <unordered_map>
21
22using namespace isc::asiolink;
23using namespace isc::data;
24using namespace std;
25
26namespace isc {
27namespace db {
28
29std::string PgSqlConnection::KEA_ADMIN_ = KEA_ADMIN;
30
31// Default connection timeout
32
34const int PGSQL_DEFAULT_CONNECTION_TIMEOUT = 5; // seconds
35
36// Length of error codes
37constexpr size_t PGSQL_STATECODE_LEN = 5;
38
39// Error codes from https://www.postgresql.org/docs/current/errcodes-appendix.html or utils/errcodes.h
40const char PgSqlConnection::DUPLICATE_KEY[] = "23505";
41const char PgSqlConnection::NULL_KEY[] = "23502";
42
44
45PgSqlResult::PgSqlResult(PGresult *result)
46 : result_(result), rows_(0), cols_(0) {
47 if (!result) {
48 // Certain failures, like a loss of connectivity, can return a
49 // null PGresult and we still need to be able to create a PgSqlResult.
50 // We'll set row and col counts to -1 to prevent anyone going off the
51 // rails.
52 rows_ = -1;
53 cols_ = -1;
54 } else {
55 rows_ = PQntuples(result);
56 cols_ = PQnfields(result);
57 }
58}
59
60void
61PgSqlResult::rowCheck(int row) const {
62 if (row < 0 || row >= rows_) {
63 isc_throw (db::DbOperationError, "row: " << row
64 << ", out of range: 0.." << rows_);
65 }
66}
67
69 if (result_) {
70 PQclear(result_);
71 }
72}
73
74void
75PgSqlResult::colCheck(int col) const {
76 if (col < 0 || col >= cols_) {
77 isc_throw (DbOperationError, "col: " << col
78 << ", out of range: 0.." << cols_);
79 }
80}
81
82void
83PgSqlResult::rowColCheck(int row, int col) const {
84 rowCheck(row);
85 colCheck(col);
86}
87
88std::string
89PgSqlResult::getColumnLabel(const int col) const {
90 const char* label = NULL;
91 try {
92 colCheck(col);
93 label = PQfname(result_, col);
94 } catch (...) {
95 std::ostringstream os;
96 os << "Unknown column:" << col;
97 return (os.str());
98 }
99
100 return (label);
101}
102
104 : conn_(conn), committed_(false) {
105 conn_.startTransaction();
106}
107
109 // If commit() wasn't explicitly called, rollback.
110 if (!committed_) {
111 conn_.rollback();
112 }
113}
114
115void
117 conn_.commit();
118 committed_ = true;
119}
120
122 if (conn_ && !isUnusable()) {
123 // Deallocate the prepared queries.
124 if (PQstatus(conn_) == CONNECTION_OK) {
125 PgSqlResult r(PQexec(conn_, "DEALLOCATE all"));
126 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
127 // Highly unlikely but we'll log it and go on.
129 .arg(PQerrorMessage(conn_));
130 }
131 }
132 }
133}
134
135std::pair<uint32_t, uint32_t>
137 const IOServiceAccessorPtr& ac,
138 const DbCallback& cb,
139 const string& timer_name,
140 unsigned int id) {
141 // Get a connection.
142 PgSqlConnection conn(parameters, ac, cb);
143
144 if (!timer_name.empty()) {
145 conn.makeReconnectCtl(timer_name, id);
146 }
147
148 // Open the database.
149 conn.openDatabaseInternal(false);
150
151 const char* version_sql = "SELECT version, minor FROM schema_version;";
152 PgSqlResult r(PQexec(conn.conn_, version_sql));
153 if (PQresultStatus(r) != PGRES_TUPLES_OK) {
154 isc_throw(DbOperationError, "unable to execute PostgreSQL statement <"
155 << version_sql << ", reason: " << PQerrorMessage(conn.conn_));
156 }
157
158 uint32_t version;
160
161 uint32_t minor;
162 PgSqlExchange::getColumnValue(r, 0, 1, minor);
163
164 return (make_pair(version, minor));
165}
166
167void
169 const DbCallback& cb,
170 const string& timer_name) {
171 // retry-on-startup?
172 bool const retry(parameters.count("retry-on-startup") &&
173 parameters.at("retry-on-startup") == "true");
174
176 pair<uint32_t, uint32_t> schema_version;
177 try {
178 schema_version = getVersion(parameters, ac, cb, retry ? timer_name : string());
179 } catch (DbOpenError const& exception) {
180 throw;
181 } catch (DbOpenErrorWithRetry const& exception) {
182 throw;
183 } catch (exception const& exception) {
184 // Disable the recovery mechanism in test mode.
186 throw;
187 }
188 // This failure may occur for a variety of reasons. We are looking at
189 // initializing schema as the only potential mitigation. We could narrow
190 // down on the error that would suggest an uninitialized schema
191 // which would sound something along the lines of
192 // "table schema_version does not exist", but we do not necessarily have
193 // to. If the error had another cause, it will fail again during
194 // initialization or during the subsequent version retrieval and that is
195 // fine, and the error should still be relevant.
196 initializeSchema(parameters);
197
198 // Retrieve again because the initial retrieval failed.
199 schema_version = getVersion(parameters, ac, cb, retry ? timer_name : string());
200 }
201
202 // Check that the versions match.
203 pair<uint32_t, uint32_t> const expected_version(PGSQL_SCHEMA_VERSION_MAJOR,
205 if (schema_version != expected_version) {
206 isc_throw(DbOpenError, "PostgreSQL schema version mismatch: expected version: "
207 << expected_version.first << "." << expected_version.second
208 << ", found version: " << schema_version.first << "."
209 << schema_version.second);
210 }
211}
212
213void
215 if (parameters.count("readonly") && parameters.at("readonly") == "true") {
216 // The readonly flag is historically used for host backends. Still, if
217 // enabled, it is a strong indication that we should not meDDLe with it.
218 return;
219 }
220
222 // It can happen for kea-admin to not exist, especially with
223 // packages that install it in a separate package.
224 return;
225 }
226
227 // Convert parameters.
228 auto const tupl(toKeaAdminParameters(parameters));
229 vector<string> kea_admin_parameters(get<0>(tupl));
230 ProcessEnvVars const vars(get<1>(tupl));
231 kea_admin_parameters.insert(kea_admin_parameters.begin(), "db-init");
232
233 // Run.
234 ProcessSpawn kea_admin(ProcessSpawn::SYNC, KEA_ADMIN_, kea_admin_parameters, vars,
235 /* inherit_env = */ true);
237 pid_t const pid(kea_admin.spawn());
238 if (kea_admin.isRunning(pid)) {
239 isc_throw(SchemaInitializationFailed, "kea-admin still running");
240 }
241 int const exit_code(kea_admin.getExitStatus(pid));
242 if (exit_code != 0) {
243 isc_throw(SchemaInitializationFailed, "Expected exit code 0 for kea-admin. Got " << exit_code);
244 }
245}
246
247tuple<vector<string>, vector<string>>
249 vector<string> result{"pgsql"};
250 ProcessEnvVars vars;
251 for (auto const& p : params) {
252 string const& keyword(p.first);
253 string const& value(p.second);
254
255 // These Kea parameters are the same as the kea-admin parameters.
256 if (keyword == "user" ||
257 keyword == "password" ||
258 keyword == "host" ||
259 keyword == "port" ||
260 keyword == "name") {
261 result.push_back("--" + keyword);
262 result.push_back(value);
263 continue;
264 }
265
266 // These Kea parameters do not have a direct kea-admin equivalent.
267 // But they do have a psql client environment variable equivalent.
268 // We pass them to kea-admin.
269 static unordered_map<string, string> conversions{
270 {"connect-timeout", "PGCONNECT_TIMEOUT"},
271 // {"tcp-user-timeout", "N/A"},
272 };
273 if (conversions.count(keyword)) {
274 vars.push_back(conversions.at(keyword) + "=" + value);
275 }
276 }
277 return make_tuple(result, vars);
278}
279
280void
282 // Prepare all statements queries with all known fields datatype
283 PgSqlResult r(PQprepare(conn_, statement.name, statement.text,
284 statement.nbparams, statement.types));
285 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
286 isc_throw(DbOperationError, "unable to prepare PostgreSQL statement: "
287 << " name: " << statement.name
288 << ", reason: " << PQerrorMessage(conn_)
289 << ", text: " << statement.text);
290 }
291}
292
293void
295 const PgSqlTaggedStatement* end_statement) {
296 // Created the PostgreSQL prepared statements.
297 for (const PgSqlTaggedStatement* tagged_statement = start_statement;
298 tagged_statement != end_statement; ++tagged_statement) {
299 prepareStatement(*tagged_statement);
300 }
301}
302
303std::string
305 return (getConnParametersInternal(false));
306}
307
308std::string
309PgSqlConnection::getConnParametersInternal(bool logging) {
310 string dbconnparameters;
311 string shost = "localhost";
312 try {
313 shost = getParameter("host");
314 } catch(...) {
315 // No host. Fine, we'll use "localhost"
316 }
317
318 dbconnparameters += "host = '" + shost + "'" ;
319
320 unsigned int port = 0;
321 try {
322 setIntParameterValue("port", 0, numeric_limits<uint16_t>::max(), port);
323
324 } catch (const std::exception& ex) {
325 isc_throw(DbInvalidPort, ex.what());
326 }
327
328 // Add port to connection parameters when not default.
329 if (port > 0) {
330 std::ostringstream oss;
331 oss << port;
332 dbconnparameters += " port = " + oss.str();
333 }
334
335 string suser;
336 try {
337 suser = getParameter("user");
338 dbconnparameters += " user = '" + suser + "'";
339 } catch(...) {
340 // No user. Fine, we'll use NULL
341 }
342
343 string spassword;
344 try {
345 spassword = getParameter("password");
346 dbconnparameters += " password = '" + spassword + "'";
347 } catch(...) {
348 // No password. Fine, we'll use NULL
349 }
350 if (!spassword.empty()) {
351 // Refuse default password.
352 DefaultCredentials::check(spassword);
353 }
354
355 string sname;
356 try {
357 sname = getParameter("name");
358 dbconnparameters += " dbname = '" + sname + "'";
359 } catch(...) {
360 // No database name. Throw a "NoDatabaseName" exception
361 isc_throw(NoDatabaseName, "must specify a name for the database");
362 }
363
364 unsigned int connect_timeout = PGSQL_DEFAULT_CONNECTION_TIMEOUT;
365 unsigned int tcp_user_timeout = 0;
366 try {
367 // The timeout is only valid if greater than zero, as depending on the
368 // database, a zero timeout might signify something like "wait
369 // indefinitely".
370 setIntParameterValue("connect-timeout", 1, numeric_limits<int>::max(), connect_timeout);
371 // This timeout value can be 0, meaning that the database client will
372 // follow a default behavior. Earlier Postgres versions didn't have
373 // this parameter, so we allow 0 to skip setting them for these
374 // earlier versions.
375 setIntParameterValue("tcp-user-timeout", 0, numeric_limits<int>::max(), tcp_user_timeout);
376
377 } catch (const std::exception& ex) {
378 isc_throw(DbInvalidTimeout, ex.what());
379 }
380
381 // Append connection timeout.
382 std::ostringstream oss;
383 oss << " connect_timeout = " << connect_timeout;
384
385 if (tcp_user_timeout > 0) {
386// tcp_user_timeout parameter is a PostgreSQL 12+ feature.
387#ifdef HAVE_PGSQL_TCP_USER_TIMEOUT
388 oss << " tcp_user_timeout = " << tcp_user_timeout * 1000;
389 static_cast<void>(logging);
390#else
391 if (logging) {
392 DB_LOG_WARN(PGSQL_TCP_USER_TIMEOUT_UNSUPPORTED).arg();
393 }
394#endif
395 }
396 dbconnparameters += oss.str();
397
398 return (dbconnparameters);
399}
400
401void
403 openDatabaseInternal(true);
404}
405
406void
407PgSqlConnection::openDatabaseInternal(bool logging) {
408 std::string dbconnparameters = getConnParametersInternal(logging);
409 // Connect to Postgres, saving the low level connection pointer
410 // in the holder object
411 PGconn* new_conn = PQconnectdb(dbconnparameters.c_str());
412 if (!new_conn) {
413 isc_throw(DbOpenError, "could not allocate connection object");
414 }
415
416 if (PQstatus(new_conn) != CONNECTION_OK) {
417 // Mark this connection as no longer usable.
418 markUnusable();
419
420 // If we have a connection object, we have to call finish
421 // to release it, but grab the error message first.
422 std::string error_message = PQerrorMessage(new_conn);
423 PQfinish(new_conn);
424
425 auto const& rec = reconnectCtl();
426 if (rec && DatabaseConnection::retry_) {
427
428 // Start the connection recovery.
430
431 std::ostringstream s;
432
433 s << " (scheduling retry " << rec->retryIndex() + 1 << " of " << rec->maxRetries() << " in " << rec->retryInterval() << " milliseconds)";
434
435 error_message += s.str();
436
437 isc_throw(DbOpenErrorWithRetry, error_message);
438 }
439
440 isc_throw(DbOpenError, error_message);
441 }
442
443 // We have a valid connection, so let's save it to our holder
444 conn_.setConnection(new_conn);
445}
446
447bool
448PgSqlConnection::compareError(const PgSqlResult& r, const char* error_state) {
449 const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE);
450 // PostgreSQL guarantees it will always be 5 characters long
451 return ((sqlstate != NULL) &&
452 (memcmp(sqlstate, error_state, PGSQL_STATECODE_LEN) == 0));
453}
454
455void
457 PgSqlTaggedStatement& statement) {
458 int s = PQresultStatus(r);
459 if (s != PGRES_COMMAND_OK && s != PGRES_TUPLES_OK) {
460 // We're testing the first two chars of SQLSTATE, as this is the
461 // error class. Note, there is a severity field, but it can be
462 // misleadingly returned as fatal. However, a loss of connectivity
463 // can lead to a NULL sqlstate with a status of PGRES_FATAL_ERROR.
464 const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE);
465 if ((sqlstate == NULL) ||
466 ((memcmp(sqlstate, "08", 2) == 0) || // Connection Exception
467 (memcmp(sqlstate, "53", 2) == 0) || // Insufficient resources
468 (memcmp(sqlstate, "54", 2) == 0) || // Program Limit exceeded
469 (memcmp(sqlstate, "57", 2) == 0) || // Operator intervention
470 (memcmp(sqlstate, "58", 2) == 0))) { // System error
472 .arg(statement.name)
473 .arg(PQerrorMessage(conn_))
474 .arg(sqlstate ? sqlstate : "<sqlstate null>");
475
476 // Mark this connection as no longer usable.
477 markUnusable();
478
479 // Start the connection recovery.
481
482 // We still need to throw so caller can error out of the current
483 // processing.
485 "fatal database error or connectivity lost");
486 }
487
488 // Failure: check for the special case of duplicate entry.
490 isc_throw(DuplicateEntry, "statement: " << statement.name
491 << ", reason: " << PQerrorMessage(conn_));
492 }
493
494 // Failure: check for the special case of null key violation.
496 isc_throw(NullKeyError, "statement: " << statement.name
497 << ", reason: " << PQerrorMessage(conn_));
498 }
499
500 // Apparently it wasn't fatal, so we throw with a helpful message.
501 const char* error_message = PQerrorMessage(conn_);
502 isc_throw(DbOperationError, "Statement exec failed for: "
503 << statement.name << ", status: " << s
504 << "sqlstate:[ " << (sqlstate ? sqlstate : "<null>")
505 << " ], reason: " << error_message);
506 }
507}
508
509void
511 // If it is nested transaction, do nothing.
512 if (++transaction_ref_count_ > 1) {
513 return;
514 }
515
518 PgSqlResult r(PQexec(conn_, "START TRANSACTION"));
519 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
520 const char* error_message = PQerrorMessage(conn_);
521 isc_throw(DbOperationError, "unable to start transaction"
522 << error_message);
523 }
524}
525
526bool
530
531void
533 if (transaction_ref_count_ <= 0) {
534 isc_throw(Unexpected, "commit called for not started transaction - coding error");
535 }
536
537 // When committing nested transaction, do nothing.
538 if (--transaction_ref_count_ > 0) {
539 return;
540 }
541
544 PgSqlResult r(PQexec(conn_, "COMMIT"));
545 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
546 const char* error_message = PQerrorMessage(conn_);
547 isc_throw(DbOperationError, "commit failed: " << error_message);
548 }
549}
550
551void
553 if (transaction_ref_count_ <= 0) {
554 isc_throw(Unexpected, "rollback called for not started transaction - coding error");
555 }
556
557 // When rolling back nested transaction, do nothing.
558 if (--transaction_ref_count_ > 0) {
559 return;
560 }
561
564 PgSqlResult r(PQexec(conn_, "ROLLBACK"));
565 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
566 const char* error_message = PQerrorMessage(conn_);
567 isc_throw(DbOperationError, "rollback failed: " << error_message);
568 }
569}
570
571void
572PgSqlConnection::createSavepoint(const std::string& name) {
573 if (transaction_ref_count_ <= 0) {
574 isc_throw(InvalidOperation, "no transaction, cannot create savepoint: " << name);
575 }
576
578 std::string sql("SAVEPOINT " + name);
579 executeSQL(sql);
580}
581
582void
583PgSqlConnection::rollbackToSavepoint(const std::string& name) {
584 if (transaction_ref_count_ <= 0) {
585 isc_throw(InvalidOperation, "no transaction, cannot rollback to savepoint: " << name);
586 }
587
588 std::string sql("ROLLBACK TO SAVEPOINT " + name);
589 executeSQL(sql);
590}
591
592void
593PgSqlConnection::executeSQL(const std::string& sql) {
594 // Use a TaggedStatement so we can call checkStatementError and ensure
595 // we detect connectivity issues properly.
596 PgSqlTaggedStatement statement({0, {OID_NONE}, "run-statement", sql.c_str()});
598 PgSqlResult r(PQexec(conn_, statement.text));
599 checkStatementError(r, statement);
600}
601
604 const PsqlBindArray& in_bindings) {
606
607 if (statement.nbparams != in_bindings.size()) {
608 isc_throw (InvalidOperation, "executePreparedStatement:"
609 << " expected: " << statement.nbparams
610 << " parameters, given: " << in_bindings.size()
611 << ", statement: " << statement.name
612 << ", SQL: " << statement.text);
613 }
614
615 const char* const* values = 0;
616 const int* lengths = 0;
617 const int* formats = 0;
618 if (statement.nbparams > 0) {
619 values = static_cast<const char* const*>(&in_bindings.values_[0]);
620 lengths = static_cast<const int *>(&in_bindings.lengths_[0]);
621 formats = static_cast<const int *>(&in_bindings.formats_[0]);
622 }
623
624 PgSqlResultPtr result_set;
625 result_set.reset(new PgSqlResult(PQexecPrepared(conn_, statement.name, statement.nbparams,
626 values, lengths, formats, 0)));
627
628 checkStatementError(*result_set, statement);
629 return (result_set);
630}
631
632void
634 const PsqlBindArray& in_bindings,
635 ConsumeResultRowFun process_result_row) {
636 // Execute the prepared statement.
637 PgSqlResultPtr result_set = executePreparedStatement(statement, in_bindings);
638
639 // Iterate over the returned rows and invoke the row consumption
640 // function on each one.
641 int rows = result_set->getRows();
642 for (int row = 0; row < rows; ++row) {
643 try {
644 process_result_row(*result_set, row);
645 } catch (const std::exception& ex) {
646 // Rethrow the exception with a bit more data.
647 isc_throw(BadValue, ex.what() << ". Statement is <" <<
648 statement.text << ">");
649 }
650 }
651}
652
653void
655 const PsqlBindArray& in_bindings) {
656 // Execute the prepared statement.
657 PgSqlResultPtr result_set = executePreparedStatement(statement, in_bindings);
658}
659
660uint64_t
662 const PsqlBindArray& in_bindings) {
663 // Execute the prepared statement.
664 PgSqlResultPtr result_set = executePreparedStatement(statement, in_bindings);
665
666 return (boost::lexical_cast<int>(PQcmdTuples(*result_set)));
667}
668
669template<typename T>
670void
671PgSqlConnection::setIntParameterValue(const std::string& name, int64_t min, int64_t max, T& value) {
672 string svalue;
673 try {
674 svalue = getParameter(name);
675 } catch (...) {
676 // Do nothing if the parameter is not present.
677 }
678 if (svalue.empty()) {
679 return;
680 }
681 try {
682 // Try to convert the value.
683 auto parsed_value = boost::lexical_cast<T>(svalue);
684 // Check if the value is within the specified range.
685 if ((parsed_value < min) || (parsed_value > max)) {
686 isc_throw(BadValue, "bad " << svalue << " value");
687 }
688 // Everything is fine. Return the parsed value.
689 value = parsed_value;
690
691 } catch (...) {
692 // We may end up here when lexical_cast fails or when the
693 // parsed value is not within the desired range. In both
694 // cases let's throw the same general error.
695 isc_throw(BadValue, name << " parameter (" <<
696 svalue << ") must be an integer between "
697 << min << " and " << max);
698 }
699}
700
701
702} // end of isc::db namespace
703} // end of isc namespace
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...
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.
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.
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
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.
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::function< void(PgSqlResult &, int)> ConsumeResultRowFun
Function invoked to process fetched row.
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 &parameters, 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 &parameters, 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 &params)
Convert PostgreSQL library parameters to kea-admin parameters.
static void initializeSchema(const ParameterMap &parameters)
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.
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.
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.
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.
const int DB_DBG_TRACE_DETAIL
Database logging levels.
Definition db_log.cc:21
const int PGSQL_DEFAULT_CONNECTION_TIMEOUT
@ PGSQL_CREATE_SAVEPOINT
Definition db_log.h:60
@ PGSQL_ROLLBACK
Definition db_log.h:59
@ PGSQL_TCP_USER_TIMEOUT_UNSUPPORTED
Definition db_log.h:62
@ PGSQL_COMMIT
Definition db_log.h:58
@ PGSQL_START_TRANSACTION
Definition db_log.h:57
@ PGSQL_FATAL_ERROR
Definition db_log.h:56
@ PGSQL_INITIALIZE_SCHEMA
Definition db_log.h:54
@ PGSQL_DEALLOC_ERROR
Definition db_log.h:55
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.
Definition filesystem.cc:61
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.
Definition db_log.h:144
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.