Kea 3.1.3
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 .arg(kea_admin.getCommandLine(std::unordered_set<std::string>{"--password"}));
238 pid_t const pid(kea_admin.spawn());
239 if (kea_admin.isRunning(pid)) {
240 isc_throw(SchemaInitializationFailed, "kea-admin still running");
241 }
242 int const exit_code(kea_admin.getExitStatus(pid));
243 if (exit_code != 0) {
244 isc_throw(SchemaInitializationFailed, "Expected exit code 0 for kea-admin. Got " << exit_code);
245 }
246}
247
248tuple<vector<string>, vector<string>>
250 vector<string> result{"pgsql"};
251 ProcessEnvVars vars;
252 for (auto const& p : params) {
253 string const& keyword(p.first);
254 string const& value(p.second);
255
256 // These Kea parameters are the same as the kea-admin parameters.
257 if (keyword == "user" ||
258 keyword == "password" ||
259 keyword == "host" ||
260 keyword == "port" ||
261 keyword == "name") {
262 result.push_back("--" + keyword);
263 result.push_back(value);
264 continue;
265 }
266
267 // These Kea parameters do not have a direct kea-admin equivalent.
268 // But they do have a psql client flag equivalent.
269 // We pass them to kea-admin using the --extra flag.
270 static unordered_map<string, string> conversions{
271 {"cert-file", "sslcert"},
272 {"key-file", "sslkey"},
273 {"trust-anchor", "sslrootcert"},
274 {"ssl-mode", "sslmode"},
275 };
276 if (conversions.count(keyword)) {
277 result.push_back("--extra");
278 result.push_back(conversions.at(keyword) + "=" + value);
279 }
280
281 // These Kea parameters do not have a direct kea-admin equivalent.
282 // But they do have a psql client environment variable equivalent.
283 // We pass them to kea-admin.
284 static unordered_map<string, string> env_conversions{
285 {"connect-timeout", "PGCONNECT_TIMEOUT"},
286 // {"tcp-user-timeout", "N/A"},
287 };
288 if (env_conversions.count(keyword)) {
289 vars.push_back(env_conversions.at(keyword) + "=" + value);
290 }
291 }
292 return make_tuple(result, vars);
293}
294
295void
297 // Prepare all statements queries with all known fields datatype
298 PgSqlResult r(PQprepare(conn_, statement.name, statement.text,
299 statement.nbparams, statement.types));
300 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
301 isc_throw(DbOperationError, "unable to prepare PostgreSQL statement: "
302 << " name: " << statement.name
303 << ", reason: " << PQerrorMessage(conn_)
304 << ", text: " << statement.text);
305 }
306}
307
308void
310 const PgSqlTaggedStatement* end_statement) {
311 // Created the PostgreSQL prepared statements.
312 for (const PgSqlTaggedStatement* tagged_statement = start_statement;
313 tagged_statement != end_statement; ++tagged_statement) {
314 prepareStatement(*tagged_statement);
315 }
316}
317
318std::string
320 return (getConnParametersInternal(false));
321}
322
323std::string
324PgSqlConnection::getConnParametersInternal(bool logging) {
325 string dbconnparameters;
326 string shost = "localhost";
327 try {
328 shost = getParameter("host");
329 } catch(...) {
330 // No host. Fine, we'll use "localhost"
331 }
332
333 dbconnparameters += "host = '" + shost + "'" ;
334
335 unsigned int port = 0;
336 try {
337 setIntParameterValue("port", 0, numeric_limits<uint16_t>::max(), port);
338
339 } catch (const std::exception& ex) {
340 isc_throw(DbInvalidPort, ex.what());
341 }
342
343 // Add port to connection parameters when not default.
344 if (port > 0) {
345 std::ostringstream oss;
346 oss << port;
347 dbconnparameters += " port = " + oss.str();
348 }
349
350 string suser;
351 try {
352 suser = getParameter("user");
353 dbconnparameters += " user = '" + suser + "'";
354 } catch(...) {
355 // No user. Fine, we'll use NULL
356 }
357
358 string spassword;
359 try {
360 spassword = getParameter("password");
361 dbconnparameters += " password = '" + spassword + "'";
362 } catch(...) {
363 // No password. Fine, we'll use NULL
364 }
365 if (!spassword.empty()) {
366 // Refuse default password.
367 DefaultCredentials::check(spassword);
368 }
369
370 string sname;
371 try {
372 sname = getParameter("name");
373 dbconnparameters += " dbname = '" + sname + "'";
374 } catch(...) {
375 // No database name. Throw a "NoDatabaseName" exception
376 isc_throw(NoDatabaseName, "must specify a name for the database");
377 }
378
379 unsigned int connect_timeout = PGSQL_DEFAULT_CONNECTION_TIMEOUT;
380 unsigned int tcp_user_timeout = 0;
381 try {
382 // The timeout is only valid if greater than zero, as depending on the
383 // database, a zero timeout might signify something like "wait
384 // indefinitely".
385 setIntParameterValue("connect-timeout", 1, numeric_limits<int>::max(), connect_timeout);
386 // This timeout value can be 0, meaning that the database client will
387 // follow a default behavior. Earlier Postgres versions didn't have
388 // this parameter, so we allow 0 to skip setting them for these
389 // earlier versions.
390 setIntParameterValue("tcp-user-timeout", 0, numeric_limits<int>::max(), tcp_user_timeout);
391
392 } catch (const std::exception& ex) {
393 isc_throw(DbInvalidTimeout, ex.what());
394 }
395
396 // Append connection timeout.
397 std::ostringstream oss;
398 oss << " connect_timeout = " << connect_timeout;
399
400 if (tcp_user_timeout > 0) {
401// tcp_user_timeout parameter is a PostgreSQL 12+ feature.
402#ifdef HAVE_PGSQL_TCP_USER_TIMEOUT
403 oss << " tcp_user_timeout = " << tcp_user_timeout * 1000;
404 static_cast<void>(logging);
405#else
406 if (logging) {
407 DB_LOG_WARN(PGSQL_TCP_USER_TIMEOUT_UNSUPPORTED).arg();
408 }
409#endif
410 }
411 dbconnparameters += oss.str();
412
413 bool tls = false;
414
415 string ssslmode;
416 try {
417 ssslmode = getParameter("ssl-mode");
418 tls = true;
419 } catch (...) {
420 // No strict ssl mode
421 }
422
423 string sca;
424 try {
425 sca = getParameter("trust-anchor");
426 tls = true;
427 if (ssslmode.empty()) {
428 ssslmode = "verify-ca";
429 }
430 dbconnparameters += " sslrootcert = " + sca;
431 } catch (...) {
432 // No trust anchor
433 }
434
435 string scert;
436 try {
437 scert = getParameter("cert-file");
438 tls = true;
439 dbconnparameters += " sslcert = " + scert;
440 } catch (...) {
441 // No client certificate file
442 }
443
444 string skey;
445 try {
446 skey = getParameter("key-file");
447 tls = true;
448 dbconnparameters += " sslkey = " + skey;
449 } catch (...) {
450 // No private key file
451 }
452
453 if (tls) {
454 if (ssslmode.empty()) {
455 ssslmode = "require";
456 }
457 dbconnparameters += " gssencmode = disable";
458 }
459
460 if (!ssslmode.empty()) {
461 dbconnparameters += " sslmode = " + ssslmode;
462 }
463
464 return (dbconnparameters);
465}
466
467void
469 openDatabaseInternal(true);
470}
471
472void
473PgSqlConnection::openDatabaseInternal(bool logging) {
474 std::string dbconnparameters = getConnParametersInternal(logging);
475 // Connect to PostgreSQL, saving the low level connection pointer
476 // in the holder object
477 PGconn* new_conn = PQconnectdb(dbconnparameters.c_str());
478 if (!new_conn) {
479 isc_throw(DbOpenError, "could not allocate connection object");
480 }
481
482 if (PQstatus(new_conn) != CONNECTION_OK) {
483 // Mark this connection as no longer usable.
484 markUnusable();
485
486 // If we have a connection object, we have to call finish
487 // to release it, but grab the error message first.
488 std::string error_message = PQerrorMessage(new_conn);
489 PQfinish(new_conn);
490
491 auto const& rec = reconnectCtl();
492 if (rec && DatabaseConnection::retry_) {
493
494 // Start the connection recovery.
496
497 std::ostringstream s;
498
499 s << " (scheduling retry " << rec->retryIndex() + 1 << " of " << rec->maxRetries() << " in " << rec->retryInterval() << " milliseconds)";
500
501 error_message += s.str();
502
503 isc_throw(DbOpenErrorWithRetry, error_message);
504 }
505
506 isc_throw(DbOpenError, error_message);
507 }
508
509 // We have a valid connection, so let's save it to our holder
510 conn_.setConnection(new_conn);
511}
512
513bool
514PgSqlConnection::compareError(const PgSqlResult& r, const char* error_state) {
515 const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE);
516 // PostgreSQL guarantees it will always be 5 characters long
517 return ((sqlstate != NULL) &&
518 (memcmp(sqlstate, error_state, PGSQL_STATECODE_LEN) == 0));
519}
520
521void
523 PgSqlTaggedStatement& statement) {
524 int s = PQresultStatus(r);
525 if (s != PGRES_COMMAND_OK && s != PGRES_TUPLES_OK) {
526 // We're testing the first two chars of SQLSTATE, as this is the
527 // error class. Note, there is a severity field, but it can be
528 // misleadingly returned as fatal. However, a loss of connectivity
529 // can lead to a NULL sqlstate with a status of PGRES_FATAL_ERROR.
530 const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE);
531 if ((sqlstate == NULL) ||
532 ((memcmp(sqlstate, "08", 2) == 0) || // Connection Exception
533 (memcmp(sqlstate, "53", 2) == 0) || // Insufficient resources
534 (memcmp(sqlstate, "54", 2) == 0) || // Program Limit exceeded
535 (memcmp(sqlstate, "57", 2) == 0) || // Operator intervention
536 (memcmp(sqlstate, "58", 2) == 0))) { // System error
538 .arg(statement.name)
539 .arg(PQerrorMessage(conn_))
540 .arg(sqlstate ? sqlstate : "<sqlstate null>");
541
542 // Mark this connection as no longer usable.
543 markUnusable();
544
545 // Start the connection recovery.
547
548 // We still need to throw so caller can error out of the current
549 // processing.
551 "fatal database error or connectivity lost");
552 }
553
554 // Failure: check for the special case of duplicate entry.
556 isc_throw(DuplicateEntry, "statement: " << statement.name
557 << ", reason: " << PQerrorMessage(conn_));
558 }
559
560 // Failure: check for the special case of null key violation.
562 isc_throw(NullKeyError, "statement: " << statement.name
563 << ", reason: " << PQerrorMessage(conn_));
564 }
565
566 // Apparently it wasn't fatal, so we throw with a helpful message.
567 const char* error_message = PQerrorMessage(conn_);
568 isc_throw(DbOperationError, "Statement exec failed for: "
569 << statement.name << ", status: " << s
570 << "sqlstate:[ " << (sqlstate ? sqlstate : "<null>")
571 << " ], reason: " << error_message);
572 }
573}
574
575void
577 // If it is nested transaction, do nothing.
578 if (++transaction_ref_count_ > 1) {
579 return;
580 }
581
584 PgSqlResult r(PQexec(conn_, "START TRANSACTION"));
585 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
586 const char* error_message = PQerrorMessage(conn_);
587 isc_throw(DbOperationError, "unable to start transaction"
588 << error_message);
589 }
590}
591
592bool
596
597void
599 if (transaction_ref_count_ <= 0) {
600 isc_throw(Unexpected, "commit called for not started transaction - coding error");
601 }
602
603 // When committing nested transaction, do nothing.
604 if (--transaction_ref_count_ > 0) {
605 return;
606 }
607
610 PgSqlResult r(PQexec(conn_, "COMMIT"));
611 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
612 const char* error_message = PQerrorMessage(conn_);
613 isc_throw(DbOperationError, "commit failed: " << error_message);
614 }
615}
616
617void
619 if (transaction_ref_count_ <= 0) {
620 isc_throw(Unexpected, "rollback called for not started transaction - coding error");
621 }
622
623 // When rolling back nested transaction, do nothing.
624 if (--transaction_ref_count_ > 0) {
625 return;
626 }
627
630 PgSqlResult r(PQexec(conn_, "ROLLBACK"));
631 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
632 const char* error_message = PQerrorMessage(conn_);
633 isc_throw(DbOperationError, "rollback failed: " << error_message);
634 }
635}
636
637void
638PgSqlConnection::createSavepoint(const std::string& name) {
639 if (transaction_ref_count_ <= 0) {
640 isc_throw(InvalidOperation, "no transaction, cannot create savepoint: " << name);
641 }
642
644 std::string sql("SAVEPOINT " + name);
645 executeSQL(sql);
646}
647
648void
649PgSqlConnection::rollbackToSavepoint(const std::string& name) {
650 if (transaction_ref_count_ <= 0) {
651 isc_throw(InvalidOperation, "no transaction, cannot rollback to savepoint: " << name);
652 }
653
654 std::string sql("ROLLBACK TO SAVEPOINT " + name);
655 executeSQL(sql);
656}
657
658void
659PgSqlConnection::executeSQL(const std::string& sql) {
660 // Use a TaggedStatement so we can call checkStatementError and ensure
661 // we detect connectivity issues properly.
662 PgSqlTaggedStatement statement({0, {OID_NONE}, "run-statement", sql.c_str()});
664 PgSqlResult r(PQexec(conn_, statement.text));
665 checkStatementError(r, statement);
666}
667
670 const PsqlBindArray& in_bindings) {
672
673 if (static_cast<size_t>(statement.nbparams) != in_bindings.size()) {
674 isc_throw (InvalidOperation, "executePreparedStatement:"
675 << " expected: " << statement.nbparams
676 << " parameters, given: " << in_bindings.size()
677 << ", statement: " << statement.name
678 << ", SQL: " << statement.text);
679 }
680
681 const char* const* values = 0;
682 const int* lengths = 0;
683 const int* formats = 0;
684 if (statement.nbparams > 0) {
685 values = static_cast<const char* const*>(&in_bindings.values_[0]);
686 lengths = static_cast<const int *>(&in_bindings.lengths_[0]);
687 formats = static_cast<const int *>(&in_bindings.formats_[0]);
688 }
689
690 PgSqlResultPtr result_set;
691 result_set.reset(new PgSqlResult(PQexecPrepared(conn_, statement.name, statement.nbparams,
692 values, lengths, formats, 0)));
693
694 checkStatementError(*result_set, statement);
695 return (result_set);
696}
697
698void
700 const PsqlBindArray& in_bindings,
701 ConsumeResultRowFun process_result_row) {
702 // Execute the prepared statement.
703 PgSqlResultPtr result_set = executePreparedStatement(statement, in_bindings);
704
705 // Iterate over the returned rows and invoke the row consumption
706 // function on each one.
707 int rows = result_set->getRows();
708 for (int row = 0; row < rows; ++row) {
709 try {
710 process_result_row(*result_set, row);
711 } catch (const std::exception& ex) {
712 // Rethrow the exception with a bit more data.
713 isc_throw(BadValue, ex.what() << ". Statement is <" <<
714 statement.text << ">");
715 }
716 }
717}
718
719void
721 const PsqlBindArray& in_bindings) {
722 // Execute the prepared statement.
723 PgSqlResultPtr result_set = executePreparedStatement(statement, in_bindings);
724}
725
726uint64_t
728 const PsqlBindArray& in_bindings) {
729 // Execute the prepared statement.
730 PgSqlResultPtr result_set = executePreparedStatement(statement, in_bindings);
731
732 return (boost::lexical_cast<int>(PQcmdTuples(*result_set)));
733}
734
735template<typename T>
736void
737PgSqlConnection::setIntParameterValue(const std::string& name, int64_t min, int64_t max, T& value) {
738 string svalue;
739 try {
740 svalue = getParameter(name);
741 } catch (...) {
742 // Do nothing if the parameter is not present.
743 }
744 if (svalue.empty()) {
745 return;
746 }
747 try {
748 // Try to convert the value.
749 auto parsed_value = boost::lexical_cast<T>(svalue);
750 // Check if the value is within the specified range.
751 if ((parsed_value < min) || (parsed_value > max)) {
752 isc_throw(BadValue, "bad " << svalue << " value");
753 }
754 // Everything is fine. Return the parsed value.
755 value = parsed_value;
756
757 } catch (...) {
758 // We may end up here when lexical_cast fails or when the
759 // parsed value is not within the desired range. In both
760 // cases let's throw the same general error.
761 isc_throw(BadValue, name << " parameter (" <<
762 svalue << ") must be an integer between "
763 << min << " and " << max);
764 }
765}
766
767
768} // end of isc::db namespace
769} // 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.
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: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: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: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.