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