Kea 3.2.0-git
pgsql_connection.cc
Go to the documentation of this file.
1// Copyright (C) 2016-2026 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 // Stop here. Initializing the schema won't work if we cannot create a connection to the
181 // database.
182 throw;
183 } catch (DbOpenErrorWithRetry const& exception) {
184 // Stop here. Initializing the schema won't work if we cannot create a connection to the
185 // database even as we are retrying.
186 throw;
187 } catch (DefaultCredential const& exception) {
188 // Stop here. Initializing the schema won't work if we cannot create a connection to the
189 // database due to default credentials being used.
190 throw;
191 } catch (exception const& exception) {
193
194 // Disable the recovery mechanism in test mode.
196 throw;
197 }
198 // This failure may occur for a variety of reasons. We are looking at
199 // initializing schema as the only potential mitigation. We could narrow
200 // down on the error that would suggest an uninitialized schema
201 // which would sound something along the lines of
202 // "table schema_version does not exist", but we do not necessarily have
203 // to. If the error had another cause, it will fail again during
204 // initialization or during the subsequent version retrieval and that is
205 // fine, and the error should still be relevant.
206 initializeSchema(parameters);
207
208 // Retrieve again because the initial retrieval failed.
209 schema_version = getVersion(parameters, ac, cb, retry ? timer_name : string());
210 }
211
212 // Check that the versions match.
213 pair<uint32_t, uint32_t> const expected_version(PGSQL_SCHEMA_VERSION_MAJOR,
215 if (schema_version != expected_version) {
216 isc_throw(DbOpenError, "PostgreSQL schema version mismatch: expected version: "
217 << expected_version.first << "." << expected_version.second
218 << ", found version: " << schema_version.first << "."
219 << schema_version.second);
220 }
221}
222
223void
225 if (parameters.count("readonly") && parameters.at("readonly") == "true") {
226 // The readonly flag is historically used for host backends. Still, if
227 // enabled, it is a strong indication that we should not meDDLe with it.
229 return;
230 }
231
233 // It can happen for kea-admin to not exist, especially with
234 // packages that install it in a separate package.
236 return;
237 }
238
239 // Convert parameters.
240 auto const tupl(toKeaAdminParameters(parameters));
241 vector<string> kea_admin_parameters(get<0>(tupl));
242 ProcessEnvVars const vars(get<1>(tupl));
243 kea_admin_parameters.insert(kea_admin_parameters.begin(), "db-init");
244
245 // Run.
246 ProcessSpawn kea_admin(ProcessSpawn::SYNC, KEA_ADMIN_, kea_admin_parameters, vars,
247 /* inherit_env = */ true);
249 .arg(kea_admin.getCommandLine(std::unordered_set<std::string>{"--password"}));
250 pid_t const pid(kea_admin.spawn());
251 if (kea_admin.isRunning(pid)) {
252 isc_throw(SchemaInitializationFailed, "kea-admin still running");
253 }
254 int const exit_code(kea_admin.getExitStatus(pid));
255 if (exit_code != 0) {
256 isc_throw(SchemaInitializationFailed, "Expected exit code 0 for kea-admin. Got " << exit_code);
257 }
258}
259
260tuple<vector<string>, vector<string>>
262 vector<string> result{"pgsql"};
263 ProcessEnvVars vars;
264 for (auto const& p : params) {
265 string const& keyword(p.first);
266 string const& value(p.second);
267
268 // These Kea parameters are the same as the kea-admin parameters.
269 if (keyword == "user" ||
270 keyword == "password" ||
271 keyword == "host" ||
272 keyword == "port" ||
273 keyword == "name") {
274 result.push_back("--" + keyword);
275 result.push_back(value);
276 continue;
277 }
278
279 // These Kea parameters do not have a direct kea-admin equivalent.
280 // But they do have a psql client flag equivalent.
281 // We pass them to kea-admin using the --extra flag.
282 static unordered_map<string, string> conversions{
283 {"cert-file", "sslcert"},
284 {"key-file", "sslkey"},
285 {"trust-anchor", "sslrootcert"},
286 {"ssl-mode", "sslmode"},
287 };
288 if (conversions.count(keyword)) {
289 result.push_back("--extra");
290 result.push_back(conversions.at(keyword) + "=" + value);
291 }
292
293 // These Kea parameters do not have a direct kea-admin equivalent.
294 // But they do have a psql client environment variable equivalent.
295 // We pass them to kea-admin.
296 static unordered_map<string, string> env_conversions{
297 {"connect-timeout", "PGCONNECT_TIMEOUT"},
298 // {"tcp-user-timeout", "N/A"},
299 };
300 if (env_conversions.count(keyword)) {
301 vars.push_back(env_conversions.at(keyword) + "=" + value);
302 }
303 }
304 return make_tuple(result, vars);
305}
306
307void
309 // Prepare all statements queries with all known fields datatype
310 PgSqlResult r(PQprepare(conn_, statement.name, statement.text,
311 statement.nbparams, statement.types));
312 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
313 isc_throw(DbOperationError, "unable to prepare PostgreSQL statement: "
314 << " name: " << statement.name
315 << ", reason: " << PQerrorMessage(conn_)
316 << ", text: " << statement.text);
317 }
318}
319
320void
322 const PgSqlTaggedStatement* end_statement) {
323 // Created the PostgreSQL prepared statements.
324 for (const PgSqlTaggedStatement* tagged_statement = start_statement;
325 tagged_statement != end_statement; ++tagged_statement) {
326 prepareStatement(*tagged_statement);
327 }
328}
329
330std::string
332 return (getConnParametersInternal(false));
333}
334
335std::string
336PgSqlConnection::getConnParametersInternal(bool logging) {
337 string dbconnparameters;
338 string shost = "localhost";
339 try {
340 shost = getParameter("host");
341 } catch(...) {
342 // No host. Fine, we'll use "localhost"
343 }
344
345 dbconnparameters += "host = '" + shost + "'" ;
346
347 unsigned int port = 0;
348 try {
349 setIntParameterValue("port", 0, numeric_limits<uint16_t>::max(), port);
350
351 } catch (const std::exception& ex) {
352 isc_throw(DbInvalidPort, ex.what());
353 }
354
355 // Add port to connection parameters when not default.
356 if (port > 0) {
357 std::ostringstream oss;
358 oss << port;
359 dbconnparameters += " port = " + oss.str();
360 }
361
362 string suser;
363 try {
364 suser = getParameter("user");
365 dbconnparameters += " user = '" + suser + "'";
366 } catch(...) {
367 // No user. Fine, we'll use NULL
368 }
369
370 string spassword;
371 try {
372 spassword = getParameter("password");
373 dbconnparameters += " password = '" + spassword + "'";
374 } catch(...) {
375 // No password. Fine, we'll use NULL
376 }
377 if (!spassword.empty()) {
378 // Refuse default password.
379 DefaultCredentials::check(spassword);
380 }
381
382 string sname;
383 try {
384 sname = getParameter("name");
385 dbconnparameters += " dbname = '" + sname + "'";
386 } catch(...) {
387 // No database name. Throw a "NoDatabaseName" exception
388 isc_throw(NoDatabaseName, "must specify a name for the database");
389 }
390
391 unsigned int connect_timeout = PGSQL_DEFAULT_CONNECTION_TIMEOUT;
392 unsigned int tcp_user_timeout = 0;
393 try {
394 // The timeout is only valid if greater than zero, as depending on the
395 // database, a zero timeout might signify something like "wait
396 // indefinitely".
397 setIntParameterValue("connect-timeout", 1, numeric_limits<int>::max(), connect_timeout);
398 // This timeout value can be 0, meaning that the database client will
399 // follow a default behavior. Earlier Postgres versions didn't have
400 // this parameter, so we allow 0 to skip setting them for these
401 // earlier versions.
402 setIntParameterValue("tcp-user-timeout", 0, numeric_limits<int>::max(), tcp_user_timeout);
403
404 } catch (const std::exception& ex) {
405 isc_throw(DbInvalidTimeout, ex.what());
406 }
407
408 // Append connection timeout.
409 std::ostringstream oss;
410 oss << " connect_timeout = " << connect_timeout;
411
412 if (tcp_user_timeout > 0) {
413// tcp_user_timeout parameter is a PostgreSQL 12+ feature.
414#ifdef HAVE_PGSQL_TCP_USER_TIMEOUT
415 oss << " tcp_user_timeout = " << tcp_user_timeout * 1000;
416 static_cast<void>(logging);
417#else
418 if (logging) {
419 DB_LOG_WARN(PGSQL_TCP_USER_TIMEOUT_UNSUPPORTED).arg();
420 }
421#endif
422 }
423 dbconnparameters += oss.str();
424
425 bool tls = false;
426
427 string ssslmode;
428 try {
429 ssslmode = getParameter("ssl-mode");
430 tls = true;
431 } catch (...) {
432 // No strict ssl mode
433 }
434
435 string sca;
436 try {
437 sca = getParameter("trust-anchor");
438 tls = true;
439 if (ssslmode.empty()) {
440 ssslmode = "verify-ca";
441 }
442 dbconnparameters += " sslrootcert = " + sca;
443 } catch (...) {
444 // No trust anchor
445 }
446
447 string scert;
448 try {
449 scert = getParameter("cert-file");
450 tls = true;
451 dbconnparameters += " sslcert = " + scert;
452 } catch (...) {
453 // No client certificate file
454 }
455
456 string skey;
457 try {
458 skey = getParameter("key-file");
459 tls = true;
460 dbconnparameters += " sslkey = " + skey;
461 } catch (...) {
462 // No private key file
463 }
464
465 if (tls) {
466 if (ssslmode.empty()) {
467 ssslmode = "require";
468 }
469 dbconnparameters += " gssencmode = disable";
470 }
471
472 if (!ssslmode.empty()) {
473 dbconnparameters += " sslmode = " + ssslmode;
474 }
475
476 return (dbconnparameters);
477}
478
479void
481 openDatabaseInternal(true);
482}
483
484void
485PgSqlConnection::openDatabaseInternal(bool logging) {
486 std::string dbconnparameters = getConnParametersInternal(logging);
487 // Connect to PostgreSQL, saving the low level connection pointer
488 // in the holder object
489 PGconn* new_conn = PQconnectdb(dbconnparameters.c_str());
490 if (!new_conn) {
491 isc_throw(DbOpenError, "could not allocate connection object");
492 }
493
494 if (PQstatus(new_conn) != CONNECTION_OK) {
495 // Mark this connection as no longer usable.
496 markUnusable();
497
498 // If we have a connection object, we have to call finish
499 // to release it, but grab the error message first.
500 std::string error_message = PQerrorMessage(new_conn);
501 PQfinish(new_conn);
502
503 auto const& rec = reconnectCtl();
504 if (rec && DatabaseConnection::retry_) {
505
506 // Start the connection recovery.
508
509 std::ostringstream s;
510
511 s << " (scheduling retry " << rec->retryIndex() + 1 << " of " << rec->maxRetries() << " in " << rec->retryInterval() << " milliseconds)";
512
513 error_message += s.str();
514
515 isc_throw(DbOpenErrorWithRetry, error_message);
516 }
517
518 isc_throw(DbOpenError, error_message);
519 }
520
521 // We have a valid connection, so let's save it to our holder
522 conn_.setConnection(new_conn);
523}
524
525bool
526PgSqlConnection::compareError(const PgSqlResult& r, const char* error_state) {
527 const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE);
528 // PostgreSQL guarantees it will always be 5 characters long
529 return ((sqlstate != NULL) &&
530 (memcmp(sqlstate, error_state, PGSQL_STATECODE_LEN) == 0));
531}
532
533void
535 PgSqlTaggedStatement& statement) {
536 int s = PQresultStatus(r);
537 if (s != PGRES_COMMAND_OK && s != PGRES_TUPLES_OK) {
538 // We're testing the first two chars of SQLSTATE, as this is the
539 // error class. Note, there is a severity field, but it can be
540 // misleadingly returned as fatal. However, a loss of connectivity
541 // can lead to a NULL sqlstate with a status of PGRES_FATAL_ERROR.
542 const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE);
543 if ((sqlstate == NULL) ||
544 ((memcmp(sqlstate, "08", 2) == 0) || // Connection Exception
545 (memcmp(sqlstate, "53", 2) == 0) || // Insufficient resources
546 (memcmp(sqlstate, "54", 2) == 0) || // Program Limit exceeded
547 (memcmp(sqlstate, "57", 2) == 0) || // Operator intervention
548 (memcmp(sqlstate, "58", 2) == 0))) { // System error
550 .arg(statement.name)
551 .arg(PQerrorMessage(conn_))
552 .arg(sqlstate ? sqlstate : "<sqlstate null>");
553
554 // Mark this connection as no longer usable.
555 markUnusable();
556
557 // Start the connection recovery.
559
560 // We still need to throw so caller can error out of the current
561 // processing.
563 "fatal database error or connectivity lost");
564 }
565
566 // Failure: check for the special case of duplicate entry.
568 isc_throw(DuplicateEntry, "statement: " << statement.name
569 << ", reason: " << PQerrorMessage(conn_));
570 }
571
572 // Failure: check for the special case of null key violation.
574 isc_throw(NullKeyError, "statement: " << statement.name
575 << ", reason: " << PQerrorMessage(conn_));
576 }
577
578 // Apparently it wasn't fatal, so we throw with a helpful message.
579 const char* error_message = PQerrorMessage(conn_);
580 isc_throw(DbOperationError, "Statement exec failed for: "
581 << statement.name << ", status: " << s
582 << "sqlstate:[ " << (sqlstate ? sqlstate : "<null>")
583 << " ], reason: " << error_message);
584 }
585}
586
587void
589 // If it is nested transaction, do nothing.
590 if (++transaction_ref_count_ > 1) {
591 return;
592 }
593
596 PgSqlResult r(PQexec(conn_, "START TRANSACTION"));
597 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
598 const char* error_message = PQerrorMessage(conn_);
599 isc_throw(DbOperationError, "unable to start transaction"
600 << error_message);
601 }
602}
603
604bool
608
609void
611 if (transaction_ref_count_ <= 0) {
612 isc_throw(Unexpected, "commit called for not started transaction - coding error");
613 }
614
615 // When committing nested transaction, do nothing.
616 if (--transaction_ref_count_ > 0) {
617 return;
618 }
619
622 PgSqlResult r(PQexec(conn_, "COMMIT"));
623 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
624 const char* error_message = PQerrorMessage(conn_);
625 isc_throw(DbOperationError, "commit failed: " << error_message);
626 }
627}
628
629void
631 if (transaction_ref_count_ <= 0) {
632 isc_throw(Unexpected, "rollback called for not started transaction - coding error");
633 }
634
635 // When rolling back nested transaction, do nothing.
636 if (--transaction_ref_count_ > 0) {
637 return;
638 }
639
642 PgSqlResult r(PQexec(conn_, "ROLLBACK"));
643 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
644 const char* error_message = PQerrorMessage(conn_);
645 isc_throw(DbOperationError, "rollback failed: " << error_message);
646 }
647}
648
649void
650PgSqlConnection::createSavepoint(const std::string& name) {
651 if (transaction_ref_count_ <= 0) {
652 isc_throw(InvalidOperation, "no transaction, cannot create savepoint: " << name);
653 }
654
656 std::string sql("SAVEPOINT " + name);
657 executeSQL(sql);
658}
659
660void
661PgSqlConnection::rollbackToSavepoint(const std::string& name) {
662 if (transaction_ref_count_ <= 0) {
663 isc_throw(InvalidOperation, "no transaction, cannot rollback to savepoint: " << name);
664 }
665
666 std::string sql("ROLLBACK TO SAVEPOINT " + name);
667 executeSQL(sql);
668}
669
670void
671PgSqlConnection::executeSQL(const std::string& sql) {
672 // Use a TaggedStatement so we can call checkStatementError and ensure
673 // we detect connectivity issues properly.
674 PgSqlTaggedStatement statement({0, {OID_NONE}, "run-statement", sql.c_str()});
676 PgSqlResult r(PQexec(conn_, statement.text));
677 checkStatementError(r, statement);
678}
679
682 const PsqlBindArray& in_bindings) {
684
685 if (static_cast<size_t>(statement.nbparams) != in_bindings.size()) {
686 isc_throw (InvalidOperation, "executePreparedStatement:"
687 << " expected: " << statement.nbparams
688 << " parameters, given: " << in_bindings.size()
689 << ", statement: " << statement.name
690 << ", SQL: " << statement.text);
691 }
692
693 const char* const* values = 0;
694 const int* lengths = 0;
695 const int* formats = 0;
696 if (statement.nbparams > 0) {
697 values = static_cast<const char* const*>(&in_bindings.values_[0]);
698 lengths = static_cast<const int *>(&in_bindings.lengths_[0]);
699 formats = static_cast<const int *>(&in_bindings.formats_[0]);
700 }
701
702 PgSqlResultPtr result_set;
703 result_set.reset(new PgSqlResult(PQexecPrepared(conn_, statement.name, statement.nbparams,
704 values, lengths, formats, 0)));
705
706 checkStatementError(*result_set, statement);
707 return (result_set);
708}
709
710void
712 const PsqlBindArray& in_bindings,
713 ConsumeResultRowFun process_result_row) {
714 // Execute the prepared statement.
715 PgSqlResultPtr result_set = executePreparedStatement(statement, in_bindings);
716
717 // Iterate over the returned rows and invoke the row consumption
718 // function on each one.
719 int rows = result_set->getRows();
720 for (int row = 0; row < rows; ++row) {
721 try {
722 process_result_row(*result_set, row);
723 } catch (const std::exception& ex) {
724 // Rethrow the exception with a bit more data.
725 isc_throw(BadValue, ex.what() << ". Statement is <" <<
726 statement.text << ">");
727 }
728 }
729}
730
731void
733 const PsqlBindArray& in_bindings) {
734 // Execute the prepared statement.
735 PgSqlResultPtr result_set = executePreparedStatement(statement, in_bindings);
736}
737
738uint64_t
740 const PsqlBindArray& in_bindings) {
741 // Execute the prepared statement.
742 PgSqlResultPtr result_set = executePreparedStatement(statement, in_bindings);
743
744 return (boost::lexical_cast<int>(PQcmdTuples(*result_set)));
745}
746
747template<typename T>
748void
749PgSqlConnection::setIntParameterValue(const std::string& name, int64_t min, int64_t max, T& value) {
750 string svalue;
751 try {
752 svalue = getParameter(name);
753 } catch (...) {
754 // Do nothing if the parameter is not present.
755 }
756 if (svalue.empty()) {
757 return;
758 }
759 try {
760 // Try to convert the value.
761 auto parsed_value = boost::lexical_cast<T>(svalue);
762 // Check if the value is within the specified range.
763 if ((parsed_value < min) || (parsed_value > max)) {
764 isc_throw(BadValue, "bad " << svalue << " value");
765 }
766 // Everything is fine. Return the parsed value.
767 value = parsed_value;
768
769 } catch (...) {
770 // We may end up here when lexical_cast fails or when the
771 // parsed value is not within the desired range. In both
772 // cases let's throw the same general error.
773 isc_throw(BadValue, name << " parameter (" <<
774 svalue << ") must be an integer between "
775 << min << " and " << max);
776 }
777}
778
779
780} // end of isc::db namespace
781} // end of isc namespace
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.
Exception thrown on attempt to use a default credential.
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 &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.
PgSqlConnection(const ParameterMap &parameters, 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.
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.
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.
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:63
@ PGSQL_ROLLBACK
Definition db_log.h:62
@ PGSQL_TCP_USER_TIMEOUT_UNSUPPORTED
Definition db_log.h:65
@ PGSQL_COMMIT
Definition db_log.h:61
@ PGSQL_NO_INIT_NO_ADMIN
Definition db_log.h:56
@ PGSQL_START_TRANSACTION
Definition db_log.h:60
@ PGSQL_FATAL_ERROR
Definition db_log.h:59
@ PGSQL_NO_INIT_READONLY
Definition db_log.h:57
@ PGSQL_INITIALIZE_SCHEMA
Definition db_log.h:55
@ PGSQL_DEALLOC_ERROR
Definition db_log.h:58
@ PGSQL_INITIAL_CONNECTION_FAIL
Definition db_log.h:54
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:80
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:150
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.