24 #define MAKE_SQLSTATE(ch1,ch2,ch3,ch4,ch5) {ch1,ch2,ch3,ch4,ch5} 26 #define PGSQL_STATECODE_LEN 5 27 #include <utils/errcodes.h> 41 const char PgSqlConnection::DUPLICATE_KEY[] = ERRCODE_UNIQUE_VIOLATION;
42 const char PgSqlConnection::NULL_KEY[] = ERRCODE_NOT_NULL_VIOLATION;
44 bool PgSqlConnection::warned_about_tls =
false;
46 PgSqlResult::PgSqlResult(PGresult *result)
47 : result_(result), rows_(0), cols_(0) {
56 rows_ = PQntuples(result);
57 cols_ = PQnfields(result);
63 if (row < 0 || row >= rows_) {
65 <<
", out of range: 0.." << rows_);
77 if (col < 0 || col >= cols_) {
79 <<
", out of range: 0.." << cols_);
91 const char* label = NULL;
94 label = PQfname(result_, col);
96 std::ostringstream os;
97 os <<
"Unknown column:" << col;
105 : conn_(conn), committed_(false) {
125 if (PQstatus(conn_) == CONNECTION_OK) {
127 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
130 .
arg(PQerrorMessage(conn_));
136 std::pair<uint32_t, uint32_t>
144 const char* version_sql =
"SELECT version, minor FROM schema_version;";
146 if (PQresultStatus(r) != PGRES_TUPLES_OK) {
148 << version_sql <<
", reason: " << PQerrorMessage(conn.
conn_));
157 return (make_pair(version, minor));
165 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
167 <<
" name: " << statement.
name 168 <<
", reason: " << PQerrorMessage(conn_)
169 <<
", text: " << statement.
text);
178 tagged_statement != end_statement; ++tagged_statement) {
179 prepareStatement(*tagged_statement);
185 string dbconnparameters;
186 string shost =
"localhost";
188 shost = getParameter(
"host");
193 dbconnparameters +=
"host = '" + shost +
"'" ;
195 unsigned int port = 0;
197 setIntParameterValue(
"port", 0, numeric_limits<uint16_t>::max(), port);
199 }
catch (
const std::exception& ex) {
205 std::ostringstream oss;
207 dbconnparameters +=
" port = " + oss.str();
212 suser = getParameter(
"user");
213 dbconnparameters +=
" user = '" + suser +
"'";
220 spassword = getParameter(
"password");
221 dbconnparameters +=
" password = '" + spassword +
"'";
228 sname = getParameter(
"name");
229 dbconnparameters +=
" dbname = '" + sname +
"'";
236 unsigned int tcp_user_timeout = 0;
241 setIntParameterValue(
"connect-timeout", 1, numeric_limits<int>::max(), connect_timeout);
246 setIntParameterValue(
"tcp-user-timeout", 0, numeric_limits<int>::max(), tcp_user_timeout);
248 }
catch (
const std::exception& ex) {
253 std::ostringstream oss;
254 oss <<
" connect_timeout = " << connect_timeout;
255 if (tcp_user_timeout > 0) {
256 oss <<
" tcp_user_timeout = " << tcp_user_timeout * 1000;
258 dbconnparameters += oss.str();
260 return (dbconnparameters);
265 std::string dbconnparameters = getConnParameters();
268 PGconn* new_conn = PQconnectdb(dbconnparameters.c_str());
273 if (PQstatus(new_conn) != CONNECTION_OK) {
276 std::string error_message = PQerrorMessage(new_conn);
282 conn_.setConnection(new_conn);
287 const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE);
289 return ((sqlstate != NULL) &&
296 int s = PQresultStatus(r);
297 if (s != PGRES_COMMAND_OK && s != PGRES_TUPLES_OK) {
302 const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE);
303 if ((sqlstate == NULL) ||
304 ((memcmp(sqlstate,
"08", 2) == 0) ||
305 (memcmp(sqlstate,
"53", 2) == 0) ||
306 (memcmp(sqlstate,
"54", 2) == 0) ||
307 (memcmp(sqlstate,
"57", 2) == 0) ||
308 (memcmp(sqlstate,
"58", 2) == 0))) {
311 .
arg(PQerrorMessage(conn_))
312 .
arg(sqlstate ? sqlstate :
"<sqlstate null>");
318 startRecoverDbConnection();
323 "fatal database error or connectivity lost");
327 if (compareError(r, PgSqlConnection::DUPLICATE_KEY)) {
329 <<
", reason: " << PQerrorMessage(conn_));
333 if (compareError(r, PgSqlConnection::NULL_KEY)) {
335 <<
", reason: " << PQerrorMessage(conn_));
339 const char* error_message = PQerrorMessage(conn_);
341 << statement.
name <<
", status: " << s
342 <<
"sqlstate:[ " << (sqlstate ? sqlstate :
"<null>")
343 <<
" ], reason: " << error_message);
350 if (++transaction_ref_count_ > 1) {
357 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
358 const char* error_message = PQerrorMessage(conn_);
366 return (transaction_ref_count_ > 0);
371 if (transaction_ref_count_ <= 0) {
376 if (--transaction_ref_count_ > 0) {
383 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
384 const char* error_message = PQerrorMessage(conn_);
391 if (transaction_ref_count_ <= 0) {
396 if (--transaction_ref_count_ > 0) {
403 if (PQresultStatus(r) != PGRES_COMMAND_OK) {
404 const char* error_message = PQerrorMessage(conn_);
411 if (transaction_ref_count_ <= 0) {
416 std::string sql(
"SAVEPOINT " + name);
422 if (transaction_ref_count_ <= 0) {
426 std::string sql(
"ROLLBACK TO SAVEPOINT " + name);
437 checkStatementError(r, statement);
447 <<
" expected: " << statement.
nbparams 448 <<
" parameters, given: " << in_bindings.
size()
449 <<
", statement: " << statement.
name 450 <<
", SQL: " << statement.
text);
453 const char*
const* values = 0;
454 const int* lengths = 0;
455 const int* formats = 0;
457 values =
static_cast<const char* const*
>(&in_bindings.
values_[0]);
458 lengths =
static_cast<const int *
>(&in_bindings.
lengths_[0]);
459 formats =
static_cast<const int *
>(&in_bindings.
formats_[0]);
464 values, lengths, formats, 0)));
466 checkStatementError(*result_set, statement);
475 PgSqlResultPtr result_set = executePreparedStatement(statement, in_bindings);
479 int rows = result_set->getRows();
480 for (
int row = 0; row < rows; ++row) {
482 process_result_row(*result_set, row);
483 }
catch (
const std::exception& ex) {
486 statement.
text <<
">");
495 PgSqlResultPtr result_set = executePreparedStatement(statement, in_bindings);
502 PgSqlResultPtr result_set = executePreparedStatement(statement, in_bindings);
504 return (boost::lexical_cast<int>(PQcmdTuples(*result_set)));
509 PgSqlConnection::setIntParameterValue(
const std::string& name, int64_t min, int64_t max, T& value) {
512 svalue = getParameter(name);
516 if (svalue.empty()) {
521 auto parsed_value = boost::lexical_cast<T>(svalue);
523 if ((parsed_value < min) || (parsed_value > max)) {
527 value = parsed_value;
534 svalue <<
") must be an integer between " 535 << min <<
" and " << max);
We want to reuse the database backend connection and exchange code for other uses, in particular for hook libraries.
RAII wrapper for PostgreSQL Result sets.
~PgSqlTransaction()
Destructor.
const Oid types[PGSQL_MAX_PARAMETERS_IN_QUERY]
OID types.
void startTransaction()
Starts new transaction.
void rollbackToSavepoint(const std::string &name)
Rollbacks to the given savepoint.
void commit()
Commits transaction.
const int PGSQL_DEFAULT_CONNECTION_TIMEOUT
std::vector< int > formats_
Vector of "format" for each value.
boost::shared_ptr< PgSqlResult > PgSqlResultPtr
std::vector< int > lengths_
Vector of data lengths for each value.
bool compareError(const PgSqlResult &r, const char *error_state)
Checks a result set's SQL state against an error state.
void insertQuery(PgSqlTaggedStatement &statement, const PsqlBindArray &in_bindings)
Executes INSERT prepared statement.
void selectQuery(PgSqlTaggedStatement &statement, const PsqlBindArray &in_bindings, ConsumeResultRowFun process_result_row)
Executes SELECT query using prepared statement.
void rowCheck(int row) const
Determines if a row index is valid.
std::string getColumnLabel(const int col) const
Fetches the name of the column in a result set.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Exception thrown on failure to open database.
void executeSQL(const std::string &sql)
Executes the an SQL statement.
void commit()
Commits current transaction.
std::vector< const char * > values_
Vector of pointers to the data values.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
void prepareStatements(const PgSqlTaggedStatement *start_statement, const PgSqlTaggedStatement *end_statement)
Prepare statements.
Exception thrown if name of database is not specified.
void createSavepoint(const std::string &name)
Creates a savepoint within the current transaction.
void checkStatementError(const PgSqlResult &r, PgSqlTaggedStatement &statement)
Checks result of the r object.
std::string getConnParameters()
Creates connection string from specified parameters.
A generic exception that is thrown when an unexpected error condition occurs.
bool isTransactionStarted() const
Checks if there is a transaction in progress.
void rollback()
Rollbacks current transaction.
PgSqlTransaction(PgSqlConnection &conn)
Constructor.
int version()
returns Kea hooks version.
static void getColumnValue(const PgSqlResult &r, const int row, const size_t col, std::string &value)
Fetches text column value as a string.
Common PgSql Connector Pool.
virtual ~PgSqlConnection()
Destructor.
void prepareStatement(const PgSqlTaggedStatement &statement)
Prepare Single Statement.
const int DB_DBG_TRACE_DETAIL
Database logging levels.
Defines the logger used by the top-level component of kea-lfc.
Define a PostgreSQL statement.
size_t size() const
Fetches the number of entries in the array.
uint64_t updateDeleteQuery(PgSqlTaggedStatement &statement, const PsqlBindArray &in_bindings)
Executes UPDATE or DELETE prepared statement and returns the number of affected rows.
DB_LOG & arg(T first, Args... args)
Pass parameters to replace logger placeholders.
#define PGSQL_STATECODE_LEN
void rowColCheck(int row, int col) const
Determines if both a row and column index are valid.
A generic exception that is thrown if a function is called in a prohibited way.
const char * text
Text representation of the actual query.
void colCheck(int col) const
Determines if a column index is valid.
~PgSqlResult()
Destructor.
PgSqlResultPtr executePreparedStatement(PgSqlTaggedStatement &statement, const PsqlBindArray &in_bindings=PsqlBindArray())
Executes a prepared SQL statement.
Exception thrown when a specific connection has been rendered unusable either through loss of connect...
void openDatabase()
Open Database.
const char * name
Short name of the query.
static std::pair< uint32_t, uint32_t > getVersion(const ParameterMap ¶meters)
Get the schema version.
const size_t OID_NONE
Constants for PostgreSQL data types These are defined by PostgreSQL in <catalog/pg_type.h>, but including this file is extraordinarily convoluted, so we'll use these to fill-in.
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
PgSqlHolder conn_
PgSql connection handle.
std::function< void(PgSqlResult &, int)> ConsumeResultRowFun
Function invoked to process fetched row.
Exception thrown on failure to execute a database function.
Key is NULL but was specified NOT NULL.
int nbparams
Number of parameters for a given query.
Database duplicate entry error.