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

0001 //
0002 // Copyright (C) 2011 Gevorg Voskanyan
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/postgresql/soci-postgresql.h"
0010 #include "soci/callbacks.h"
0011 #include "soci/connection-parameters.h"
0012 #include <cstring>
0013 
0014 using namespace soci;
0015 using namespace soci::details;
0016 
0017 postgresql_soci_error::postgresql_soci_error(
0018     std::string const & msg, char const *sqlst)
0019     : soci_error(msg), cat_(unknown)
0020 {
0021     std::memcpy(sqlstate_, sqlst, 5);
0022 
0023     if (std::memcmp(sqlst, "08", 2) == 0)
0024     {
0025         cat_ = connection_error;
0026     }
0027     else if (std::memcmp(sqlst, "42501", 5) == 0)
0028     {
0029         cat_ = no_privilege;
0030     }
0031     else if (std::memcmp(sqlst, "42", 2) == 0)
0032     {
0033         cat_ = invalid_statement;
0034     }
0035     else if (std::memcmp(sqlst, "02", 2) == 0)
0036     {
0037         cat_ = no_data;
0038     }
0039     else if (std::memcmp(sqlst, "23", 2) == 0)
0040     {
0041         cat_ = constraint_violation;
0042     }
0043     else if ((std::memcmp(sqlst, "53", 2) == 0) ||
0044         (std::memcmp(sqlst, "54", 2) == 0) ||
0045         (std::memcmp(sqlst, "58", 2) == 0) ||
0046         (std::memcmp(sqlst, "XX", 2) == 0))
0047     {
0048         cat_ = system_error;
0049     }
0050 }
0051 
0052 std::string postgresql_soci_error::sqlstate() const
0053 {
0054     return std::string(sqlstate_, 5);
0055 }
0056 
0057 void
0058 details::postgresql_result::check_for_errors(char const* errMsg) const
0059 {
0060     static_cast<void>(check_for_data(errMsg));
0061 }
0062 
0063 bool
0064 details::postgresql_result::check_for_data(char const* errMsg) const
0065 {
0066     // This SQL state will be used if we can't get anything more precise.
0067     const char* fallback_sql_state = "     ";
0068 
0069     std::string msg(errMsg);
0070 
0071     ExecStatusType const status = PQresultStatus(result_);
0072     switch (status)
0073     {
0074         case PGRES_EMPTY_QUERY:
0075         case PGRES_COMMAND_OK:
0076             // No data but don't throw neither.
0077             return false;
0078 
0079         case PGRES_TUPLES_OK:
0080             return true;
0081 
0082         case PGRES_FATAL_ERROR:
0083             msg += " Fatal error.";
0084 
0085             if (PQstatus(sessionBackend_.conn_) == CONNECTION_BAD)
0086             {
0087                 msg += " Connection failed.";
0088 
0089                 // It's useful to set it here to something at least slightly
0090                 // more specific, as we're not going to get anything from
0091                 // PG_DIAG_SQLSTATE below if the connection is lost.
0092                 fallback_sql_state = "08000"; // connection_exception
0093 
0094                 // call the failover callback, if registered
0095 
0096                 failover_callback * callback = sessionBackend_.failoverCallback_;
0097                 if (callback != NULL)
0098                 {
0099                     bool reconnected = false;
0100 
0101                     try
0102                     {
0103                         callback->started();
0104                     }
0105                     catch (...)
0106                     {
0107                         // ignore exceptions from user callbacks
0108                     }
0109                     bool retry = false;
0110                     do {
0111                         std::string newTarget;
0112 
0113                         try
0114                         {
0115                             callback->failed(retry, newTarget);
0116                         }
0117                         catch (...)
0118                         {
0119                             // do not continue execution because
0120                             // user callback generated an exception
0121                             retry = false;
0122                         }
0123 
0124                         if (retry)
0125                         {
0126                             connection_parameters parameters =
0127                                 sessionBackend_.connectionParameters_;
0128 
0129                             if (!newTarget.empty())
0130                                 parameters.set_connect_string(newTarget);
0131 
0132                             sessionBackend_.clean_up();
0133 
0134                             sessionBackend_.connect(parameters);
0135 
0136                             reconnected = true;
0137                         }
0138                     } while (retry && !reconnected);
0139 
0140                     if (reconnected == false)
0141                     {
0142                         try
0143                         {
0144                             callback->aborted();
0145                         }
0146                         catch (...)
0147                         {
0148                             // ignore exceptions from user callbacks
0149                         }
0150                     }
0151                     else
0152                     {
0153                         try
0154                         {
0155                             callback->finished(*sessionBackend_.session_);
0156                         }
0157                         catch (...)
0158                         {
0159                             // ignore exceptions from user callbacks
0160                         }
0161                     }
0162                 }
0163             }
0164 
0165             break;
0166 
0167         default:
0168             // Some of the other status codes are not really errors but we're
0169             // not prepared to handle them right now and shouldn't ever receive
0170             // them so throw nevertheless
0171 
0172             break;
0173     }
0174 
0175     const char* const pqError = PQresultErrorMessage(result_);
0176     if (pqError && *pqError)
0177     {
0178         msg += " ";
0179         msg += pqError;
0180     }
0181 
0182     const char* sqlstate = PQresultErrorField(result_, PG_DIAG_SQLSTATE);
0183     if (!sqlstate)
0184     {
0185         sqlstate = fallback_sql_state;
0186     }
0187 
0188     throw postgresql_soci_error(msg, sqlstate);
0189 }