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