Kea 2.5.8
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 // This failure may occur for a variety of reasons. We are looking at
195 // initializing schema as the only potential mitigation. We could narrow
196 // down on the error that would suggest an uninitialized schema
197 // which would sound something along the lines of
198 // "table schema_version does not exist", but we do not necessarily have
199 // to. If the error had another cause, it will fail again during
200 // initialization or during the subsequent version retrieval and that is
201 // fine, and the error should still be relevant.
202 initializeSchema(parameters);
203
204 // Retrieve again because the initial retrieval failed.
205 schema_version = getVersion(parameters, ac, cb, retry ? timer_name : string());
206 }
207
208 // Check that the versions match.
209 pair<uint32_t, uint32_t> const expected_version(PGSQL_SCHEMA_VERSION_MAJOR,
211 if (schema_version != expected_version) {
212 isc_throw(DbOpenError, "PostgreSQL schema version mismatch: expected version: "
213 << expected_version.first << "." << expected_version.second
214 << ", found version: " << schema_version.first << "."
215 << schema_version.second);
216 }
217}
218
219void
221 if (parameters.count("readonly") && parameters.at("readonly") == "true") {
222 // The readonly flag is historically used for host backends. Still, if
223 // enabled, it is a strong indication that we should not meDDLe with it.
224 return;
225 }
226
228 // It can happen for kea-admin to not exist, especially with
229 // packages that install it in a separate package.
230 return;
231 }
232
233 // Convert parameters.
234 auto const tupl(toKeaAdminParameters(parameters));
235 vector<string> kea_admin_parameters(get<0>(tupl));
236 ProcessEnvVars const vars(get<1>(tupl));
237 kea_admin_parameters.insert(kea_admin_parameters.begin(), "db-init");
238
239 // Run.
240 ProcessSpawn kea_admin(KEA_ADMIN_, kea_admin_parameters, vars,
241 /* inherit_env = */ true);
243 pid_t const pid(kea_admin.spawn());
244 if (kea_admin.isRunning(pid)) {
245 isc_throw(SchemaInitializationFailed, "kea-admin still running");
246 }
247 int const exit_code(kea_admin.getExitStatus(pid));
248 if (exit_code != 0) {
249 isc_throw(SchemaInitializationFailed, "Expected exit code 0 for kea-admin. Got " << exit_code);
250 }
251}
252
253tuple<vector<string>, vector<string>>
255 vector<string> result{"pgsql"};
256 ProcessEnvVars vars;
257 for (auto const& p : params) {
258 string const& keyword(p.first);
259 string const& value(p.second);
260
261 // These Kea parameters are the same as the kea-admin parameters.
262 if (keyword == "user" ||
263 keyword == "password" ||
264 keyword == "host" ||
265 keyword == "port" ||
266 keyword == "name") {
267 result.push_back("--" + keyword);
268 result.push_back(value);
269 continue;
270 }
271
272 // These Kea parameters do not have a direct kea-admin equivalent.
273 // But they do have a psql client environment variable equivalent.
274 // We pass them to kea-admin.
275 static unordered_map<string, string> conversions{
276 {"connect-timeout", "PGCONNECT_TIMEOUT"},
277 // {"tcp-user-timeout", "N/A"},
278 };
279 if (conversions.count(keyword)) {
280 vars.push_back(conversions.at(keyword) + "=" + value);
281 }
282 }
283 return make_tuple(result, vars);
284}
285
286void
288 // Prepare all statements queries with all known fields datatype
289 PgSqlResult r(PQprepare(conn_, statement.name, statement.text,
290 statement.nbparams, statement.types));
291 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
292 isc_throw(DbOperationError, "unable to prepare PostgreSQL statement: "
293 << " name: " << statement.name
294 << ", reason: " << PQerrorMessage(conn_)
295 << ", text: " << statement.text);
296 }
297}
298
299void
301 const PgSqlTaggedStatement* end_statement) {
302 // Created the PostgreSQL prepared statements.
303 for (const PgSqlTaggedStatement* tagged_statement = start_statement;
304 tagged_statement != end_statement; ++tagged_statement) {
305 prepareStatement(*tagged_statement);
306 }
307}
308
309std::string
311 return (getConnParametersInternal(false));
312}
313
314std::string
315PgSqlConnection::getConnParametersInternal(bool logging) {
316 string dbconnparameters;
317 string shost = "localhost";
318 try {
319 shost = getParameter("host");
320 } catch(...) {
321 // No host. Fine, we'll use "localhost"
322 }
323
324 dbconnparameters += "host = '" + shost + "'" ;
325
326 unsigned int port = 0;
327 try {
328 setIntParameterValue("port", 0, numeric_limits<uint16_t>::max(), port);
329
330 } catch (const std::exception& ex) {
331 isc_throw(DbInvalidPort, ex.what());
332 }
333
334 // Add port to connection parameters when not default.
335 if (port > 0) {
336 std::ostringstream oss;
337 oss << port;
338 dbconnparameters += " port = " + oss.str();
339 }
340
341 string suser;
342 try {
343 suser = getParameter("user");
344 dbconnparameters += " user = '" + suser + "'";
345 } catch(...) {
346 // No user. Fine, we'll use NULL
347 }
348
349 string spassword;
350 try {
351 spassword = getParameter("password");
352 dbconnparameters += " password = '" + spassword + "'";
353 } catch(...) {
354 // No password. Fine, we'll use NULL
355 }
356
357 string sname;
358 try {
359 sname = getParameter("name");
360 dbconnparameters += " dbname = '" + sname + "'";
361 } catch(...) {
362 // No database name. Throw a "NoDatabaseName" exception
363 isc_throw(NoDatabaseName, "must specify a name for the database");
364 }
365
366 unsigned int connect_timeout = PGSQL_DEFAULT_CONNECTION_TIMEOUT;
367 unsigned int tcp_user_timeout = 0;
368 try {
369 // The timeout is only valid if greater than zero, as depending on the
370 // database, a zero timeout might signify something like "wait
371 // indefinitely".
372 setIntParameterValue("connect-timeout", 1, numeric_limits<int>::max(), connect_timeout);
373 // This timeout value can be 0, meaning that the database client will
374 // follow a default behavior. Earlier Postgres versions didn't have
375 // this parameter, so we allow 0 to skip setting them for these
376 // earlier versions.
377 setIntParameterValue("tcp-user-timeout", 0, numeric_limits<int>::max(), tcp_user_timeout);
378
379 } catch (const std::exception& ex) {
380 isc_throw(DbInvalidTimeout, ex.what());
381 }
382
383 // Append connection timeout.
384 std::ostringstream oss;
385 oss << " connect_timeout = " << connect_timeout;
386
387 if (tcp_user_timeout > 0) {
388// tcp_user_timeout parameter is a PostgreSQL 12+ feature.
389#ifdef HAVE_PGSQL_TCP_USER_TIMEOUT
390 oss << " tcp_user_timeout = " << tcp_user_timeout * 1000;
391 static_cast<void>(logging);
392#else
393 if (logging) {
394 DB_LOG_WARN(PGSQL_TCP_USER_TIMEOUT_UNSUPPORTED).arg();
395 }
396#endif
397 }
398 dbconnparameters += oss.str();
399
400 return (dbconnparameters);
401}
402
403void
405 openDatabaseInternal(true);
406}
407
408void
409PgSqlConnection::openDatabaseInternal(bool logging) {
410 std::string dbconnparameters = getConnParametersInternal(logging);
411 // Connect to Postgres, saving the low level connection pointer
412 // in the holder object
413 PGconn* new_conn = PQconnectdb(dbconnparameters.c_str());
414 if (!new_conn) {
415 isc_throw(DbOpenError, "could not allocate connection object");
416 }
417
418 if (PQstatus(new_conn) != CONNECTION_OK) {
419 // If we have a connection object, we have to call finish
420 // to release it, but grab the error message first.
421 std::string error_message = PQerrorMessage(new_conn);
422 PQfinish(new_conn);
423
424 auto const& rec = reconnectCtl();
425 if (rec && DatabaseConnection::retry_) {
426 // Start the connection recovery.
428
429 std::ostringstream s;
430
431 s << " (scheduling retry " << rec->retryIndex() + 1 << " of " << rec->maxRetries() << " in " << rec->retryInterval() << " milliseconds)";
432
433 error_message += s.str();
434
435 isc_throw(DbOpenErrorWithRetry, error_message);
436 }
437
438 isc_throw(DbOpenError, error_message);
439 }
440
441 // We have a valid connection, so let's save it to our holder
442 conn_.setConnection(new_conn);
443}
444
445bool
446PgSqlConnection::compareError(const PgSqlResult& r, const char* error_state) {
447 const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE);
448 // PostgreSQL guarantees it will always be 5 characters long
449 return ((sqlstate != NULL) &&
450 (memcmp(sqlstate, error_state, PGSQL_STATECODE_LEN) == 0));
451}
452
453void
455 PgSqlTaggedStatement& statement) {
456 int s = PQresultStatus(r);
457 if (s != PGRES_COMMAND_OK && s != PGRES_TUPLES_OK) {
458 // We're testing the first two chars of SQLSTATE, as this is the
459 // error class. Note, there is a severity field, but it can be
460 // misleadingly returned as fatal. However, a loss of connectivity
461 // can lead to a NULL sqlstate with a status of PGRES_FATAL_ERROR.
462 const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE);
463 if ((sqlstate == NULL) ||
464 ((memcmp(sqlstate, "08", 2) == 0) || // Connection Exception
465 (memcmp(sqlstate, "53", 2) == 0) || // Insufficient resources
466 (memcmp(sqlstate, "54", 2) == 0) || // Program Limit exceeded
467 (memcmp(sqlstate, "57", 2) == 0) || // Operator intervention
468 (memcmp(sqlstate, "58", 2) == 0))) { // System error
470 .arg(statement.name)
471 .arg(PQerrorMessage(conn_))
472 .arg(sqlstate ? sqlstate : "<sqlstate null>");
473
474 // Mark this connection as no longer usable.
475 markUnusable();
476
477 // Start the connection recovery.
479
480 // We still need to throw so caller can error out of the current
481 // processing.
483 "fatal database error or connectivity lost");
484 }
485
486 // Failure: check for the special case of duplicate entry.
488 isc_throw(DuplicateEntry, "statement: " << statement.name
489 << ", reason: " << PQerrorMessage(conn_));
490 }
491
492 // Failure: check for the special case of null key violation.
494 isc_throw(NullKeyError, "statement: " << statement.name
495 << ", reason: " << PQerrorMessage(conn_));
496 }
497
498 // Apparently it wasn't fatal, so we throw with a helpful message.
499 const char* error_message = PQerrorMessage(conn_);
500 isc_throw(DbOperationError, "Statement exec failed for: "
501 << statement.name << ", status: " << s
502 << "sqlstate:[ " << (sqlstate ? sqlstate : "<null>")
503 << " ], reason: " << error_message);
504 }
505}
506
507void
509 // If it is nested transaction, do nothing.
510 if (++transaction_ref_count_ > 1) {
511 return;
512 }
513
516 PgSqlResult r(PQexec(conn_, "START TRANSACTION"));
517 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
518 const char* error_message = PQerrorMessage(conn_);
519 isc_throw(DbOperationError, "unable to start transaction"
520 << error_message);
521 }
522}
523
524bool
526 return (transaction_ref_count_ > 0);
527}
528
529void
531 if (transaction_ref_count_ <= 0) {
532 isc_throw(Unexpected, "commit called for not started transaction - coding error");
533 }
534
535 // When committing nested transaction, do nothing.
536 if (--transaction_ref_count_ > 0) {
537 return;
538 }
539
542 PgSqlResult r(PQexec(conn_, "COMMIT"));
543 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
544 const char* error_message = PQerrorMessage(conn_);
545 isc_throw(DbOperationError, "commit failed: " << error_message);
546 }
547}
548
549void
551 if (transaction_ref_count_ <= 0) {
552 isc_throw(Unexpected, "rollback called for not started transaction - coding error");
553 }
554
555 // When rolling back nested transaction, do nothing.
556 if (--transaction_ref_count_ > 0) {
557 return;
558 }
559
562 PgSqlResult r(PQexec(conn_, "ROLLBACK"));
563 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
564 const char* error_message = PQerrorMessage(conn_);
565 isc_throw(DbOperationError, "rollback failed: " << error_message);
566 }
567}
568
569void
570PgSqlConnection::createSavepoint(const std::string& name) {
571 if (transaction_ref_count_ <= 0) {
572 isc_throw(InvalidOperation, "no transaction, cannot create savepoint: " << name);
573 }
574
576 std::string sql("SAVEPOINT " + name);
577 executeSQL(sql);
578}
579
580void
581PgSqlConnection::rollbackToSavepoint(const std::string& name) {
582 if (transaction_ref_count_ <= 0) {
583 isc_throw(InvalidOperation, "no transaction, cannot rollback to savepoint: " << name);
584 }
585
586 std::string sql("ROLLBACK TO SAVEPOINT " + name);
587 executeSQL(sql);
588}
589
590void
591PgSqlConnection::executeSQL(const std::string& sql) {
592 // Use a TaggedStatement so we can call checkStatementError and ensure
593 // we detect connectivity issues properly.
594 PgSqlTaggedStatement statement({0, {OID_NONE}, "run-statement", sql.c_str()});
596 PgSqlResult r(PQexec(conn_, statement.text));
597 checkStatementError(r, statement);
598}
599
602 const PsqlBindArray& in_bindings) {
604
605 if (statement.nbparams != in_bindings.size()) {
606 isc_throw (InvalidOperation, "executePreparedStatement:"
607 << " expected: " << statement.nbparams
608 << " parameters, given: " << in_bindings.size()
609 << ", statement: " << statement.name
610 << ", SQL: " << statement.text);
611 }
612
613 const char* const* values = 0;
614 const int* lengths = 0;
615 const int* formats = 0;
616 if (statement.nbparams > 0) {
617 values = static_cast<const char* const*>(&in_bindings.values_[0]);
618 lengths = static_cast<const int *>(&in_bindings.lengths_[0]);
619 formats = static_cast<const int *>(&in_bindings.formats_[0]);
620 }
621
622 PgSqlResultPtr result_set;
623 result_set.reset(new PgSqlResult(PQexecPrepared(conn_, statement.name, statement.nbparams,
624 values, lengths, formats, 0)));
625
626 checkStatementError(*result_set, statement);
627 return (result_set);
628}
629
630void
632 const PsqlBindArray& in_bindings,
633 ConsumeResultRowFun process_result_row) {
634 // Execute the prepared statement.
635 PgSqlResultPtr result_set = executePreparedStatement(statement, in_bindings);
636
637 // Iterate over the returned rows and invoke the row consumption
638 // function on each one.
639 int rows = result_set->getRows();
640 for (int row = 0; row < rows; ++row) {
641 try {
642 process_result_row(*result_set, row);
643 } catch (const std::exception& ex) {
644 // Rethrow the exception with a bit more data.
645 isc_throw(BadValue, ex.what() << ". Statement is <" <<
646 statement.text << ">");
647 }
648 }
649}
650
651void
653 const PsqlBindArray& in_bindings) {
654 // Execute the prepared statement.
655 PgSqlResultPtr result_set = executePreparedStatement(statement, in_bindings);
656}
657
658uint64_t
660 const PsqlBindArray& in_bindings) {
661 // Execute the prepared statement.
662 PgSqlResultPtr result_set = executePreparedStatement(statement, in_bindings);
663
664 return (boost::lexical_cast<int>(PQcmdTuples(*result_set)));
665}
666
667template<typename T>
668void
669PgSqlConnection::setIntParameterValue(const std::string& name, int64_t min, int64_t max, T& value) {
670 string svalue;
671 try {
672 svalue = getParameter(name);
673 } catch (...) {
674 // Do nothing if the parameter is not present.
675 }
676 if (svalue.empty()) {
677 return;
678 }
679 try {
680 // Try to convert the value.
681 auto parsed_value = boost::lexical_cast<T>(svalue);
682 // Check if the value is within the specified range.
683 if ((parsed_value < min) || (parsed_value > max)) {
684 isc_throw(BadValue, "bad " << svalue << " value");
685 }
686 // Everything is fine. Return the parsed value.
687 value = parsed_value;
688
689 } catch (...) {
690 // We may end up here when lexical_cast fails or when the
691 // parsed value is not within the desired range. In both
692 // cases let's throw the same general error.
693 isc_throw(BadValue, name << " parameter (" <<
694 svalue << ") must be an integer between "
695 << min << " and " << max);
696 }
697}
698
699
700} // end of isc::db namespace
701} // 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...
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
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.
virtual void makeReconnectCtl(const std::string &timer_name)
Instantiates a ReconnectCtl based on the connection's reconnect parameters.
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.
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.
Definition: db_exceptions.h:30
Key is NULL but was specified NOT NULL.
Definition: db_exceptions.h:37
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::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:64
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.