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