File indexing completed on 2025-02-23 05:15:20

0001 //
0002 // Copyright (C) 2004-2008 Maciej Sobczak, Stephen Hutton
0003 // Distributed under the Boost Software License, Version 1.0.
0004 // (See accompanying file LICENSE_1_0.txt or copy at
0005 // http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 
0008 #define SOCI_POSTGRESQL_SOURCE
0009 #include "soci/soci-platform.h"
0010 #include "soci/postgresql/soci-postgresql.h"
0011 #include "soci/session.h"
0012 #include <libpq/libpq-fs.h> // libpq
0013 #include <cctype>
0014 #include <cstdio>
0015 #include <cstring>
0016 #include <ctime>
0017 #include <sstream>
0018 
0019 using namespace soci;
0020 using namespace soci::details;
0021 
0022 namespace // unnamed
0023 {
0024 
0025 // helper function for hardcoded queries
0026 void hard_exec(postgresql_session_backend & session_backend,
0027     PGconn * conn, char const * query, char const * errMsg)
0028 {
0029     postgresql_result(session_backend, PQexec(conn, query)).check_for_errors(errMsg);
0030 }
0031 
0032 } // namespace unnamed
0033 
0034 postgresql_session_backend::postgresql_session_backend(
0035     connection_parameters const& parameters, bool single_row_mode)
0036     : statementCount_(0), conn_(0)
0037 {
0038     single_row_mode_ = single_row_mode;
0039 
0040     connect(parameters);
0041 }
0042 
0043 void postgresql_session_backend::connect(
0044     connection_parameters const& parameters)
0045 {
0046     PGconn* conn = PQconnectdb(parameters.get_connect_string().c_str());
0047     if (0 == conn || CONNECTION_OK != PQstatus(conn))
0048     {
0049         std::string msg = "Cannot establish connection to the database.";
0050         if (0 != conn)
0051         {
0052             msg += '\n';
0053             msg += PQerrorMessage(conn);
0054             PQfinish(conn);
0055         }
0056 
0057         throw soci_error(msg);
0058     }
0059 
0060     // Increase the number of digits used for floating point values to ensure
0061     // that the conversions to/from text round trip correctly, which is not the
0062     // case with the default value of 0. Use the maximal supported value, which
0063     // was 2 until 9.x and is 3 since it.
0064     int const version = PQserverVersion(conn);
0065     hard_exec(*this, conn,
0066         version >= 90000 ? "SET extra_float_digits = 3"
0067                          : "SET extra_float_digits = 2",
0068         "Cannot set extra_float_digits parameter");
0069 
0070     conn_ = conn;
0071     connectionParameters_ = parameters;
0072 }
0073 
0074 postgresql_session_backend::~postgresql_session_backend()
0075 {
0076     clean_up();
0077 }
0078 
0079 bool postgresql_session_backend::is_connected()
0080 {
0081     // For the connection to work, its status must be OK, but this is not
0082     // sufficient, so try to actually do something with it, even if it's
0083     // something as trivial as sending an empty command to the server.
0084     if ( PQstatus(conn_) != CONNECTION_OK )
0085         return false;
0086 
0087     postgresql_result(*this, PQexec(conn_, "/* ping */"));
0088 
0089     // And then check it again.
0090     return PQstatus(conn_) == CONNECTION_OK;
0091 }
0092 
0093 void postgresql_session_backend::begin()
0094 {
0095     hard_exec(*this, conn_, "BEGIN", "Cannot begin transaction.");
0096 }
0097 
0098 void postgresql_session_backend::commit()
0099 {
0100     hard_exec(*this, conn_, "COMMIT", "Cannot commit transaction.");
0101 }
0102 
0103 void postgresql_session_backend::rollback()
0104 {
0105     hard_exec(*this, conn_, "ROLLBACK", "Cannot rollback transaction.");
0106 }
0107 
0108 void postgresql_session_backend::deallocate_prepared_statement(
0109     const std::string & statementName)
0110 {
0111     const std::string & query = "DEALLOCATE " + statementName;
0112 
0113     hard_exec(*this, conn_, query.c_str(),
0114         "Cannot deallocate prepared statement.");
0115 }
0116 
0117 bool postgresql_session_backend::get_next_sequence_value(
0118     session & s, std::string const & sequence, long long & value)
0119 {
0120     s << "select nextval('" + sequence + "')", into(value);
0121 
0122     return true;
0123 }
0124 
0125 void postgresql_session_backend::clean_up()
0126 {
0127     if (0 != conn_)
0128     {
0129         PQfinish(conn_);
0130         conn_ = 0;
0131     }
0132 }
0133 
0134 std::string postgresql_session_backend::get_next_statement_name()
0135 {
0136     char nameBuf[20] = { 0 }; // arbitrary length
0137     sprintf(nameBuf, "st_%d", ++statementCount_);
0138     return nameBuf;
0139 }
0140 
0141 postgresql_statement_backend * postgresql_session_backend::make_statement_backend()
0142 {
0143     return new postgresql_statement_backend(*this, single_row_mode_);
0144 }
0145 
0146 postgresql_rowid_backend * postgresql_session_backend::make_rowid_backend()
0147 {
0148     return new postgresql_rowid_backend(*this);
0149 }
0150 
0151 postgresql_blob_backend * postgresql_session_backend::make_blob_backend()
0152 {
0153     return new postgresql_blob_backend(*this);
0154 }