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 ¶mValues[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 ¶mValues[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, ¶mValues[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, ¶mValues[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 }