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/postgresql/soci-postgresql.h"
0010 #include "soci/soci-platform.h"
0011 #include <libpq/libpq-fs.h> // libpq
0012 #include <cctype>
0013 #include <cstdio>
0014 #include <cstdlib>
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 // used only with asynchronous operations in single-row mode
0026 #ifndef SOCI_POSTGRESQL_NOSINGLEROWMODE
0027 void wait_until_operation_complete(postgresql_session_backend & session)
0028 {
0029     for (;;)
0030     {
0031         PGresult * result = PQgetResult(session.conn_);
0032         if (result == NULL)
0033         {
0034             break;
0035         }
0036         else
0037         {
0038             postgresql_result r(session, result);
0039             r.check_for_errors("Cannot execute asynchronous query in single-row mode");
0040         }
0041     }
0042 }
0043 
0044 void throw_soci_error(PGconn * conn, const char * msg)
0045 {
0046     std::string description = msg;
0047     description += ": ";
0048     description += PQerrorMessage(conn);
0049 
0050     throw soci_error(description);
0051 }
0052 #endif // !SOCI_POSTGRESQL_NOSINGLEROWMODE
0053 
0054 } // unnamed namespace
0055 
0056 postgresql_statement_backend::postgresql_statement_backend(
0057     postgresql_session_backend &session, bool single_row_mode)
0058     : session_(session), single_row_mode_(single_row_mode),
0059       result_(session, NULL),
0060       rowsAffectedBulk_(-1LL), justDescribed_(false),
0061       hasIntoElements_(false), hasVectorIntoElements_(false),
0062       hasUseElements_(false), hasVectorUseElements_(false)
0063 {
0064 #ifdef SOCI_POSTGRESQL_NOSINGLEROWMODE
0065   if (single_row_mode)
0066   {
0067     throw soci_error("Single row mode not supported in this version of the library");
0068   }
0069 #endif // !SOCI_POSTGRESQL_NOSINGLEROWMODE
0070 }
0071 
0072 postgresql_statement_backend::~postgresql_statement_backend()
0073 {
0074     if (statementName_.empty() == false)
0075     {
0076         try
0077         {
0078             session_.deallocate_prepared_statement(statementName_);
0079         }
0080         catch (...)
0081         {
0082             // Don't allow exceptions to escape from dtor. Suppressing them is
0083             // not ideal, but terminating the program, as would happen if we're
0084             // already unwinding the stack because of a previous exception,
0085             // would be even worse.
0086         }
0087     }
0088 }
0089 
0090 void postgresql_statement_backend::alloc()
0091 {
0092     // nothing to do here
0093 }
0094 
0095 void postgresql_statement_backend::clean_up()
0096 {
0097     // 'reset' the value for a
0098     // potential new execution.
0099     rowsAffectedBulk_ = -1;
0100 
0101     // nothing to do here
0102 }
0103 
0104 void postgresql_statement_backend::prepare(std::string const & query,
0105     statement_type stType)
0106 {
0107     // rewrite the query by transforming all named parameters into
0108     // the postgresql_ numbers ones (:abc -> $1, etc.)
0109 
0110     enum { normal, in_quotes, in_identifier, in_name } state = normal;
0111 
0112     std::string name;
0113     int position = 1;
0114 
0115     for (std::string::const_iterator it = query.begin(), end = query.end();
0116          it != end; ++it)
0117     {
0118         switch (state)
0119         {
0120         case normal:
0121             if (*it == '\'')
0122             {
0123                 query_ += *it;
0124                 state = in_quotes;
0125             }
0126             else if (*it == '\"')
0127             {
0128                 query_ += *it;
0129                 state = in_identifier;
0130             }
0131             else if (*it == ':')
0132             {
0133                 // Check whether this is a cast operator (e.g. 23::float)
0134                 // and treat it as a special case, not as a named binding
0135                 const std::string::const_iterator next_it = it + 1;
0136                 if ((next_it != end) && (*next_it == ':'))
0137                 {
0138                     query_ += "::";
0139                     ++it;
0140                 }
0141                 // Check whether this is an assignment(e.g. x:=y)
0142                 // and treat it as a special case, not as a named binding
0143                 else if ((next_it != end) && (*next_it == '='))
0144                 {
0145                     query_ += ":=";
0146                     ++it;
0147                 }
0148                 else
0149                 {
0150                     state = in_name;
0151                 }
0152             }
0153             else // regular character, stay in the same state
0154             {
0155                 query_ += *it;
0156             }
0157             break;
0158         case in_quotes:
0159            if (*it == '\'' )
0160             {
0161                 query_ += *it;
0162                 state = normal;
0163             }
0164             else // regular quoted character
0165             {
0166                 query_ += *it;
0167             }
0168             break;
0169         case in_identifier:
0170             if ( *it == '\"' )
0171             {
0172                 query_ += *it;
0173                 state = normal;
0174             }
0175             else // regular quoted character
0176             {
0177                 query_ += *it;
0178             }
0179             break;
0180         case in_name:
0181             if (std::isalnum(*it) || *it == '_')
0182             {
0183                 name += *it;
0184             }
0185             else // end of name
0186             {
0187                 names_.push_back(name);
0188                 name.clear();
0189                 std::ostringstream ss;
0190                 ss << '$' << position++;
0191                 query_ += ss.str();
0192                 query_ += *it;
0193                 state = normal;
0194 
0195                 // Check whether the named parameter is immediatelly
0196                 // followed by a cast operator (e.g. :name::float)
0197                 // and handle the additional colon immediately to avoid
0198                 // its misinterpretation later on.
0199                 if (*it == ':')
0200                 {
0201                     const std::string::const_iterator next_it = it + 1;
0202                     if ((next_it != end) && (*next_it == ':'))
0203                     {
0204                         query_ += ':';
0205                         ++it;
0206                     }
0207                 }
0208             }
0209             break;
0210         }
0211     }
0212 
0213     if (state == in_name)
0214     {
0215         names_.push_back(name);
0216         std::ostringstream ss;
0217         ss << '$' << position++;
0218         query_ += ss.str();
0219     }
0220 
0221     if (stType == st_repeatable_query)
0222     {
0223         if (!statementName_.empty())
0224         {
0225             throw soci_error("Shouldn't already have a prepared statement.");
0226         }
0227 
0228         // Holding the name temporarily in this var because
0229         // if it fails to prepare it we can't DEALLOCATE it.
0230         std::string statementName = session_.get_next_statement_name();
0231 
0232 #ifndef SOCI_POSTGRESQL_NOSINGLEROWMODE
0233         if (single_row_mode_)
0234         {
0235             // prepare for single-row retrieval
0236 
0237             int result = PQsendPrepare(session_.conn_, statementName.c_str(),
0238                 query_.c_str(), static_cast<int>(names_.size()), NULL);
0239             if (result != 1)
0240             {
0241                 throw_soci_error(session_.conn_,
0242                     "Cannot prepare statement in singlerow mode");
0243             }
0244 
0245             wait_until_operation_complete(session_);
0246         }
0247         else
0248 #endif // !SOCI_POSTGRESQL_NOSINGLEROWMODE
0249         {
0250             // default multi-row query execution
0251 
0252             postgresql_result result(session_,
0253                 PQprepare(session_.conn_, statementName.c_str(),
0254                     query_.c_str(), static_cast<int>(names_.size()), NULL));
0255             result.check_for_errors("Cannot prepare statement.");
0256         }
0257 
0258         // Now it's safe to save this info.
0259         statementName_ = statementName;
0260     }
0261 
0262     stType_ = stType;
0263 }
0264 
0265 statement_backend::exec_fetch_result
0266 postgresql_statement_backend::execute(int number)
0267 {
0268 #ifndef SOCI_POSTGRESQL_NOSINGLEROWMODE
0269     if (single_row_mode_ && (number > 1))
0270     {
0271         throw soci_error("Bulk operations are not supported with single-row mode.");
0272     }
0273 #endif // !SOCI_POSTGRESQL_NOSINGLEROWMODE
0274 
0275     // If the statement was "just described", then we know that
0276     // it was actually executed with all the use elements
0277     // already bound and pre-used. This means that the result of the
0278     // query is already on the client side, so there is no need
0279     // to re-execute it.
0280     // The optimization based on the existing results
0281     // from the row description can be performed only once.
0282     // If the same statement is re-executed,
0283     // it will be *really* re-executed, without reusing existing data.
0284 
0285     if (justDescribed_ == false)
0286     {
0287         // This object could have been already filled with data before.
0288         clean_up();
0289 
0290         if ((number > 1) && hasIntoElements_)
0291         {
0292              throw soci_error(
0293                   "Bulk use with single into elements is not supported.");
0294         }
0295 
0296         // Since the bulk operations are not natively supported by postgresql_,
0297         // we have to explicitly loop to achieve the bulk operations.
0298         // On the other hand, looping is not needed if there are single
0299         // use elements, even if there is a bulk fetch.
0300         // We know that single use and bulk use elements in the same query are
0301         // not supported anyway, so in the effect the 'number' parameter here
0302         // specifies the size of vectors (into/use), but 'numberOfExecutions'
0303         // specifies the number of loops that need to be performed.
0304 
0305         int numberOfExecutions = 1;
0306         if (number > 0)
0307         {
0308              numberOfExecutions = hasUseElements_ ? 1 : number;
0309         }
0310 
0311         if ((useByPosBuffers_.empty() == false) ||
0312             (useByNameBuffers_.empty() == false))
0313         {
0314             if ((useByPosBuffers_.empty() == false) &&
0315                 (useByNameBuffers_.empty() == false))
0316             {
0317                 throw soci_error(
0318                     "Binding for use elements must be either by position "
0319                     "or by name.");
0320             }
0321             long long rowsAffectedBulkTemp = 0;
0322             for (int i = 0; i != numberOfExecutions; ++i)
0323             {
0324                 std::vector<char *> paramValues;
0325 
0326                 if (useByPosBuffers_.empty() == false)
0327                 {
0328                     // use elements bind by position
0329                     // the map of use buffers can be traversed
0330                     // in its natural order
0331 
0332                     for (UseByPosBuffersMap::iterator
0333                              it = useByPosBuffers_.begin(),
0334                              end = useByPosBuffers_.end();
0335                          it != end; ++it)
0336                     {
0337                         char ** buffers = it->second;
0338                         paramValues.push_back(buffers[i]);
0339                     }
0340                 }
0341                 else
0342                 {
0343                     // use elements bind by name
0344 
0345                     for (std::vector<std::string>::iterator
0346                              it = names_.begin(), end = names_.end();
0347                          it != end; ++it)
0348                     {
0349                         UseByNameBuffersMap::iterator b
0350                             = useByNameBuffers_.find(*it);
0351                         if (b == useByNameBuffers_.end())
0352                         {
0353                             std::string msg(
0354                                 "Missing use element for bind by name (");
0355                             msg += *it;
0356                             msg += ").";
0357                             throw soci_error(msg);
0358                         }
0359                         char ** buffers = b->second;
0360                         paramValues.push_back(buffers[i]);
0361                     }
0362                 }
0363 
0364                 if (stType_ == st_repeatable_query)
0365                 {
0366                     // this query was separately prepared
0367 
0368 #ifndef SOCI_POSTGRESQL_NOSINGLEROWMODE
0369                     if (single_row_mode_)
0370                     {
0371                         int result = PQsendQueryPrepared(session_.conn_,
0372                             statementName_.c_str(),
0373                             static_cast<int>(paramValues.size()),
0374                             &paramValues[0], NULL, NULL, 0);
0375                         if (result != 1)
0376                         {
0377                             throw_soci_error(session_.conn_,
0378                                 "Cannot execute prepared query in single-row mode");
0379                         }
0380 
0381                         result = PQsetSingleRowMode(session_.conn_);
0382                         if (result != 1)
0383                         {
0384                             throw_soci_error(session_.conn_,
0385                                 "Cannot set singlerow mode");
0386                         }
0387                     }
0388                     else
0389 #endif // !SOCI_POSTGRESQL_NOSINGLEROWMODE
0390                     {
0391                         // default multi-row execution
0392 
0393                         result_.reset(PQexecPrepared(session_.conn_,
0394                                 statementName_.c_str(),
0395                                 static_cast<int>(paramValues.size()),
0396                                 &paramValues[0], NULL, NULL, 0));
0397                     }
0398                 }
0399                 else // stType_ == st_one_time_query
0400                 {
0401                     // this query was not separately prepared and should
0402                     // be executed as a one-time query
0403 
0404 #ifndef SOCI_POSTGRESQL_NOSINGLEROWMODE
0405                     if (single_row_mode_)
0406                     {
0407                         int result = PQsendQueryParams(session_.conn_, query_.c_str(),
0408                             static_cast<int>(paramValues.size()),
0409                             NULL, &paramValues[0], NULL, NULL, 0);
0410                         if (result != 1)
0411                         {
0412                             throw_soci_error(session_.conn_,
0413                                 "cannot execute query in single-row mode");
0414                         }
0415 
0416                         result = PQsetSingleRowMode(session_.conn_);
0417                         if (result != 1)
0418                         {
0419                             throw_soci_error(session_.conn_,
0420                                 "Cannot set singlerow mode");
0421                         }
0422                     }
0423                     else
0424 #endif // !SOCI_POSTGRESQL_NOSINGLEROWMODE
0425                     {
0426                         // default multi-row execution
0427 
0428                         result_.reset(PQexecParams(session_.conn_, query_.c_str(),
0429                                 static_cast<int>(paramValues.size()),
0430                                 NULL, &paramValues[0], NULL, NULL, 0));
0431                     }
0432                 }
0433 
0434                 if (numberOfExecutions > 1)
0435                 {
0436                     // there are only bulk use elements (no intos)
0437 
0438                     // preserve the number of rows affected so far.
0439                     rowsAffectedBulk_ = rowsAffectedBulkTemp;
0440 
0441                     result_.check_for_errors("Cannot execute query.");
0442 
0443                     rowsAffectedBulkTemp += get_affected_rows();
0444                 }
0445             }
0446             rowsAffectedBulk_ = rowsAffectedBulkTemp;
0447 
0448             if (numberOfExecutions > 1)
0449             {
0450                 // it was a bulk operation
0451                 result_.reset();
0452                 return ef_no_data;
0453             }
0454 
0455             // otherwise (no bulk), follow the code below
0456         }
0457         else
0458         {
0459             // there are no use elements
0460             // - execute the query without parameter information
0461             if (stType_ == st_repeatable_query)
0462             {
0463                 // this query was separately prepared
0464 
0465 #ifndef SOCI_POSTGRESQL_NOSINGLEROWMODE
0466                 if (single_row_mode_)
0467                 {
0468                     int result = PQsendQueryPrepared(session_.conn_,
0469                         statementName_.c_str(), 0, NULL, NULL, NULL, 0);
0470                     if (result != 1)
0471                     {
0472                         throw_soci_error(session_.conn_,
0473                             "Cannot execute prepared query in single-row mode");
0474                     }
0475 
0476                     result = PQsetSingleRowMode(session_.conn_);
0477                     if (result != 1)
0478                     {
0479                         throw_soci_error(session_.conn_,
0480                             "Cannot set singlerow mode");
0481                     }
0482                 }
0483                 else
0484 #endif // !SOCI_POSTGRESQL_NOSINGLEROWMODE
0485                 {
0486                     // default multi-row execution
0487 
0488                     result_.reset(PQexecPrepared(session_.conn_,
0489                             statementName_.c_str(), 0, NULL, NULL, NULL, 0));
0490                 }
0491             }
0492             else // stType_ == st_one_time_query
0493             {
0494 #ifndef SOCI_POSTGRESQL_NOSINGLEROWMODE
0495                 if (single_row_mode_)
0496                 {
0497                     int result = PQsendQuery(session_.conn_, query_.c_str());
0498                     if (result != 1)
0499                     {
0500                         throw_soci_error(session_.conn_,
0501                             "Cannot execute query in single-row mode");
0502                     }
0503 
0504                     result = PQsetSingleRowMode(session_.conn_);
0505                     if (result != 1)
0506                     {
0507                         throw_soci_error(session_.conn_,
0508                             "Cannot set single-row mode");
0509                     }
0510                 }
0511                 else
0512 #endif // !SOCI_POSTGRESQL_NOSINGLEROWMODE
0513                 {
0514                     // default multi-row execution
0515 
0516                     result_.reset(PQexec(session_.conn_, query_.c_str()));
0517                 }
0518             }
0519         }
0520     }
0521 
0522     bool process_result;
0523 #ifndef SOCI_POSTGRESQL_NOSINGLEROWMODE
0524     if (single_row_mode_)
0525     {
0526         if (justDescribed_)
0527         {
0528             // reuse the result_ that was already filled when executing the query
0529             // for the purpose of row describe
0530         }
0531         else
0532         {
0533             PGresult * res = PQgetResult(session_.conn_);
0534             result_.reset(res);
0535         }
0536 
0537         process_result = result_.check_for_data("Cannot execute query.");
0538     }
0539     else
0540 #endif // !SOCI_POSTGRESQL_NOSINGLEROWMODE
0541     {
0542         // default multi-row execution
0543 
0544         process_result = result_.check_for_data("Cannot execute query.");
0545     }
0546 
0547     justDescribed_ = false;
0548 
0549     if (process_result)
0550     {
0551         currentRow_ = 0;
0552         rowsToConsume_ = 0;
0553 
0554         numberOfRows_ = PQntuples(result_);
0555         if (numberOfRows_ == 0)
0556         {
0557             return ef_no_data;
0558         }
0559         else
0560         {
0561             if (number > 0)
0562             {
0563                 // prepare for the subsequent data consumption
0564                 return fetch(number);
0565             }
0566             else
0567             {
0568                 // execute(0) was meant to only perform the query
0569                 return ef_success;
0570             }
0571         }
0572     }
0573     else
0574     {
0575         return ef_no_data;
0576     }
0577 }
0578 
0579 statement_backend::exec_fetch_result
0580 postgresql_statement_backend::fetch(int number)
0581 {
0582 #ifndef SOCI_POSTGRESQL_NOSINGLEROWMODE
0583     if (single_row_mode_ && (number > 1))
0584     {
0585         throw soci_error("Bulk operations are not supported with single-row mode.");
0586     }
0587 #endif // !SOCI_POSTGRESQL_NOSINGLEROWMODE
0588 
0589     // Note:
0590     // In the multi-row mode this function does not actually fetch anything from anywhere
0591     // - the data was already retrieved from the server in the execute()
0592     // function, and the actual consumption of this data will take place
0593     // in the postFetch functions, called for each into element.
0594     // Here, we only prepare for this to happen (to emulate "the Oracle way").
0595     // In the single-row mode the fetch of single row of data is performed as expected.
0596 
0597     // forward the "cursor" from the last fetch
0598     currentRow_ += rowsToConsume_;
0599 
0600     if (currentRow_ >= numberOfRows_)
0601     {
0602 #ifndef SOCI_POSTGRESQL_NOSINGLEROWMODE
0603         if (single_row_mode_)
0604         {
0605             PGresult* res = PQgetResult(session_.conn_);
0606             result_.reset(res);
0607 
0608             if (res == NULL)
0609             {
0610                 return ef_no_data;
0611             }
0612 
0613             currentRow_ = 0;
0614             rowsToConsume_ = 0;
0615 
0616             numberOfRows_ = PQntuples(result_);
0617             if (numberOfRows_ == 0)
0618             {
0619                 return ef_no_data;
0620             }
0621             else
0622             {
0623                 rowsToConsume_ = 1;
0624 
0625                 return ef_success;
0626             }
0627         }
0628         else
0629 #endif // !SOCI_POSTGRESQL_NOSINGLEROWMODE
0630         {
0631             // default multi-row execution
0632 
0633             // all rows were already consumed
0634 
0635             return ef_no_data;
0636         }
0637     }
0638     else
0639     {
0640         if (currentRow_ + number > numberOfRows_)
0641         {
0642 #ifndef SOCI_POSTGRESQL_NOSINGLEROWMODE
0643             if (single_row_mode_)
0644             {
0645                 rowsToConsume_ = 1;
0646 
0647                 return ef_success;
0648             }
0649             else
0650 #endif // !SOCI_POSTGRESQL_NOSINGLEROWMODE
0651             {
0652                 // default multi-row execution
0653 
0654                 rowsToConsume_ = numberOfRows_ - currentRow_;
0655 
0656                 // this simulates the behaviour of Oracle
0657                 // - when EOF is hit, we return ef_no_data even when there are
0658                 // actually some rows fetched
0659                 return ef_no_data;
0660             }
0661         }
0662         else
0663         {
0664 #ifndef SOCI_POSTGRESQL_NOSINGLEROWMODE
0665             if (single_row_mode_)
0666             {
0667                 rowsToConsume_ = 1;
0668             }
0669             else
0670 #endif // !SOCI_POSTGRESQL_NOSINGLEROWMODE
0671             {
0672                 rowsToConsume_ = number;
0673             }
0674 
0675             return ef_success;
0676         }
0677     }
0678 }
0679 
0680 long long postgresql_statement_backend::get_affected_rows()
0681 {
0682     // PQcmdTuples() doesn't really modify the result but it takes a non-const
0683     // pointer to it, so we can't rely on implicit conversion here.
0684     const char * const resultStr = PQcmdTuples(result_.get_result());
0685     char * end;
0686     long long result = std::strtoll(resultStr, &end, 0);
0687     if (end != resultStr)
0688     {
0689         return result;
0690     }
0691     else if (rowsAffectedBulk_ >= 0)
0692     {
0693         return rowsAffectedBulk_;
0694     }
0695     else
0696     {
0697         return -1;
0698     }
0699 }
0700 
0701 int postgresql_statement_backend::get_number_of_rows()
0702 {
0703     return numberOfRows_ - currentRow_;
0704 }
0705 
0706 std::string postgresql_statement_backend::get_parameter_name(int index) const
0707 {
0708     return names_.at(index);
0709 }
0710 
0711 std::string postgresql_statement_backend::rewrite_for_procedure_call(
0712     std::string const & query)
0713 {
0714     std::string newQuery("select ");
0715     newQuery += query;
0716     return newQuery;
0717 }
0718 
0719 int postgresql_statement_backend::prepare_for_describe()
0720 {
0721     execute(1);
0722     justDescribed_ = true;
0723 
0724     int columns = PQnfields(result_);
0725     return columns;
0726 }
0727 
0728 void throw_soci_type_error(Oid typeOid, int colNum, char category, const char* typeName )
0729 {
0730     std::stringstream message;
0731     message << "unknown data type"
0732         << " for column number: " << colNum
0733         << " with type oid: " << (int)typeOid;
0734     if( category != '\0' )
0735     {
0736         message << " with category: " << category;
0737     }
0738     message << " with name: " << typeName;
0739 
0740     throw soci_error(message.str());
0741 }
0742 
0743 void postgresql_statement_backend::describe_column(int colNum, data_type & type,
0744     std::string & columnName)
0745 {
0746     // In postgresql_ column numbers start from 0
0747     int const pos = colNum - 1;
0748 
0749     unsigned long const typeOid = PQftype(result_, pos);
0750     switch (typeOid)
0751     {
0752     // Note: the following list of OIDs was taken from the pg_type table
0753     // we do not claim that this list is exchaustive or even correct.
0754 
0755                // from pg_type:
0756 
0757     case 25:   // text
0758     case 1043: // varchar
0759     case 2275: // cstring
0760     case 18:   // char
0761     case 19:   // name
0762     case 1042: // bpchar
0763     case 142:  // xml
0764     case 114:  // json
0765     case 17:   // bytea
0766     case 2950: // uuid
0767         type = dt_string;
0768         break;
0769 
0770     case 702:  // abstime
0771     case 703:  // reltime
0772     case 1082: // date
0773     case 1083: // time
0774     case 1114: // timestamp
0775     case 1184: // timestamptz
0776     case 1266: // timetz
0777         type = dt_date;
0778         break;
0779 
0780     case 700:  // float4
0781     case 701:  // float8
0782     case 1700: // numeric
0783         type = dt_double;
0784         break;
0785 
0786     case 16:   // bool
0787     case 21:   // int2
0788     case 23:   // int4
0789     case 26:   // oid
0790         type = dt_integer;
0791         break;
0792 
0793     case 20:   // int8
0794         type = dt_long_long;
0795         break;
0796 
0797     default:
0798     {
0799         auto typeCategoryIt = categoryByColumnOID_.find(typeOid);
0800         if ( typeCategoryIt == categoryByColumnOID_.end() )
0801         {
0802             std::stringstream query;
0803             query << "SELECT typcategory FROM pg_type WHERE oid=" << (int)typeOid;
0804 
0805             soci::details::postgresql_result res(session_, PQexec(session_.conn_, query.str().c_str()));
0806             if ( PQresultStatus(res.get_result()) != PGRES_TUPLES_OK )
0807             {
0808                 throw_soci_type_error(typeOid, colNum, '\0', PQfname(result_, pos));
0809             }
0810 
0811             char* typeVal = PQgetvalue(res.get_result(), 0, 0);
0812             auto iter_inserted = categoryByColumnOID_.insert( std::pair<unsigned long, char>( typeOid, typeVal[0] ) );
0813             if ( !iter_inserted.second )
0814             {
0815                 throw_soci_type_error(typeOid, colNum, typeVal[0], PQfname(result_, pos));
0816             }
0817 
0818             typeCategoryIt = iter_inserted.first;
0819         }
0820 
0821         char typeCategory = (*typeCategoryIt).second;
0822         switch ( typeCategory )
0823         {
0824             case 'D': // date type
0825             case 'E': // enum type
0826             case 'T': // time type
0827             case 'S': // string type
0828             case 'U': // user type
0829                 type = dt_string;
0830                 break;
0831 
0832             default:
0833                 throw_soci_type_error(typeOid, colNum, typeCategory, PQfname(result_, pos));
0834         }
0835     }
0836     }
0837 
0838     columnName = PQfname(result_, pos);
0839 }
0840 
0841 postgresql_standard_into_type_backend *
0842 postgresql_statement_backend::make_into_type_backend()
0843 {
0844     hasIntoElements_ = true;
0845     return new postgresql_standard_into_type_backend(*this);
0846 }
0847 
0848 postgresql_standard_use_type_backend *
0849 postgresql_statement_backend::make_use_type_backend()
0850 {
0851     hasUseElements_ = true;
0852     return new postgresql_standard_use_type_backend(*this);
0853 }
0854 
0855 postgresql_vector_into_type_backend *
0856 postgresql_statement_backend::make_vector_into_type_backend()
0857 {
0858     hasVectorIntoElements_ = true;
0859     return new postgresql_vector_into_type_backend(*this);
0860 }
0861 
0862 postgresql_vector_use_type_backend *
0863 postgresql_statement_backend::make_vector_use_type_backend()
0864 {
0865     hasVectorUseElements_ = true;
0866     return new postgresql_vector_use_type_backend(*this);
0867 }