Warning, file /sdk/codevis/thirdparty/soci/src/core/statement.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
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_SOURCE 0009 #include "soci/statement.h" 0010 #include "soci/session.h" 0011 #include "soci/into-type.h" 0012 #include "soci/use-type.h" 0013 #include "soci/values.h" 0014 #include "soci-compiler.h" 0015 #include <ctime> 0016 #include <cctype> 0017 0018 using namespace soci; 0019 using namespace soci::details; 0020 0021 0022 statement_impl::statement_impl(session & s) 0023 : session_(s), refCount_(1), row_(0), 0024 fetchSize_(1), initialFetchSize_(1), 0025 alreadyDescribed_(false) 0026 { 0027 backEnd_ = s.make_statement_backend(); 0028 } 0029 0030 statement_impl::statement_impl(prepare_temp_type const & prep) 0031 : session_(prep.get_prepare_info()->session_), 0032 refCount_(1), row_(0), fetchSize_(1), alreadyDescribed_(false) 0033 { 0034 backEnd_ = session_.make_statement_backend(); 0035 0036 ref_counted_prepare_info * prepInfo = prep.get_prepare_info(); 0037 0038 // take all bind/define info 0039 intos_.swap(prepInfo->intos_); 0040 uses_.swap(prepInfo->uses_); 0041 0042 // allocate handle 0043 alloc(); 0044 0045 // prepare the statement 0046 query_ = prepInfo->get_query(); 0047 try 0048 { 0049 prepare(query_); 0050 } 0051 catch(...) 0052 { 0053 clean_up(); 0054 throw; 0055 } 0056 0057 define_and_bind(); 0058 } 0059 0060 statement_impl::~statement_impl() 0061 { 0062 clean_up(); 0063 } 0064 0065 void statement_impl::alloc() 0066 { 0067 backEnd_->alloc(); 0068 } 0069 0070 void statement_impl::bind(values & values) 0071 { 0072 std::size_t cnt = 0; 0073 0074 try 0075 { 0076 for (std::vector<details::standard_use_type*>::iterator it = 0077 values.uses_.begin(); it != values.uses_.end(); ++it) 0078 { 0079 // only bind those variables which are: 0080 // - either named and actually referenced in the statement, 0081 // - or positional 0082 0083 std::string const& useName = (*it)->get_name(); 0084 if (useName.empty()) 0085 { 0086 // positional use element 0087 0088 int position = static_cast<int>(uses_.size()); 0089 (*it)->bind(*this, position); 0090 uses_.push_back(*it); 0091 indicators_.push_back(values.indicators_[cnt]); 0092 } 0093 else 0094 { 0095 // named use element - check if it is used 0096 std::string const placeholder = ":" + useName; 0097 0098 std::size_t pos = query_.find(placeholder); 0099 while (pos != std::string::npos) 0100 { 0101 // Retrieve next char after placeholder 0102 // make sure we do not go out of range on the string 0103 const char nextChar = (pos + placeholder.size()) < query_.size() ? 0104 query_[pos + placeholder.size()] : '\0'; 0105 0106 if (std::isalnum(nextChar)) 0107 { 0108 // We got a partial match only, 0109 // keep looking for the placeholder 0110 pos = query_.find(placeholder, pos + placeholder.size()); 0111 } 0112 else 0113 { 0114 int position = static_cast<int>(uses_.size()); 0115 (*it)->bind(*this, position); 0116 uses_.push_back(*it); 0117 indicators_.push_back(values.indicators_[cnt]); 0118 // Ok we found it, done 0119 break; 0120 } 0121 } 0122 // In case we couldn't find the placeholder 0123 if (pos == std::string::npos) 0124 { 0125 values.add_unused(*it, values.indicators_[cnt]); 0126 } 0127 } 0128 0129 cnt++; 0130 } 0131 } 0132 catch (...) 0133 { 0134 for (std::size_t i = ++cnt; i != values.uses_.size(); ++i) 0135 { 0136 values.add_unused(values.uses_[i], values.indicators_[i]); 0137 } 0138 0139 rethrow_current_exception_with_context("binding parameters of"); 0140 } 0141 } 0142 0143 void statement_impl::bind_clean_up() 0144 { 0145 // deallocate all bind and define objects 0146 std::size_t const isize = intos_.size(); 0147 for (std::size_t i = isize; i != 0; --i) 0148 { 0149 intos_[i - 1]->clean_up(); 0150 delete intos_[i - 1]; 0151 intos_.resize(i - 1); 0152 } 0153 0154 std::size_t const ifrsize = intosForRow_.size(); 0155 for (std::size_t i = ifrsize; i != 0; --i) 0156 { 0157 intosForRow_[i - 1]->clean_up(); 0158 delete intosForRow_[i - 1]; 0159 intosForRow_.resize(i - 1); 0160 } 0161 0162 std::size_t const usize = uses_.size(); 0163 for (std::size_t i = usize; i != 0; --i) 0164 { 0165 uses_[i - 1]->clean_up(); 0166 delete uses_[i - 1]; 0167 uses_.resize(i - 1); 0168 } 0169 0170 std::size_t const indsize = indicators_.size(); 0171 for (std::size_t i = 0; i != indsize; ++i) 0172 { 0173 delete indicators_[i]; 0174 indicators_[i] = NULL; 0175 } 0176 indicators_.clear(); 0177 0178 row_ = NULL; 0179 alreadyDescribed_ = false; 0180 } 0181 0182 void statement_impl::clean_up() 0183 { 0184 bind_clean_up(); 0185 if (backEnd_ != NULL) 0186 { 0187 backEnd_->clean_up(); 0188 delete backEnd_; 0189 backEnd_ = NULL; 0190 } 0191 } 0192 0193 void statement_impl::prepare(std::string const & query, 0194 statement_type eType) 0195 { 0196 try 0197 { 0198 query_ = query; 0199 session_.log_query(query); 0200 0201 backEnd_->prepare(query, eType); 0202 } 0203 catch (...) 0204 { 0205 rethrow_current_exception_with_context("preparing"); 0206 } 0207 } 0208 0209 void statement_impl::define_and_bind() 0210 { 0211 int definePosition = 1; 0212 std::size_t const isize = intos_.size(); 0213 for (std::size_t i = 0; i != isize; ++i) 0214 { 0215 intos_[i]->define(*this, definePosition); 0216 } 0217 0218 // if there are some implicite into elements 0219 // injected by the row description process, 0220 // they should be defined in the later phase, 0221 // starting at the position where the above loop finished 0222 definePositionForRow_ = definePosition; 0223 0224 int bindPosition = 1; 0225 std::size_t const usize = uses_.size(); 0226 for (std::size_t i = 0; i != usize; ++i) 0227 { 0228 uses_[i]->bind(*this, bindPosition); 0229 } 0230 } 0231 0232 void statement_impl::define_for_row() 0233 { 0234 std::size_t const isize = intosForRow_.size(); 0235 for (std::size_t i = 0; i != isize; ++i) 0236 { 0237 intosForRow_[i]->define(*this, definePositionForRow_); 0238 } 0239 } 0240 0241 void statement_impl::undefine_and_bind() 0242 { 0243 std::size_t const isize = intos_.size(); 0244 for (std::size_t i = isize; i != 0; --i) 0245 { 0246 intos_[i - 1]->clean_up(); 0247 } 0248 0249 std::size_t const ifrsize = intosForRow_.size(); 0250 for (std::size_t i = ifrsize; i != 0; --i) 0251 { 0252 intosForRow_[i - 1]->clean_up(); 0253 } 0254 0255 std::size_t const usize = uses_.size(); 0256 for (std::size_t i = usize; i != 0; --i) 0257 { 0258 uses_[i - 1]->clean_up(); 0259 } 0260 } 0261 0262 bool statement_impl::execute(bool withDataExchange) 0263 { 0264 try 0265 { 0266 initialFetchSize_ = intos_size(); 0267 0268 if (intos_.empty() == false && initialFetchSize_ == 0) 0269 { 0270 // this can happen only with into-vectors elements 0271 // and is not allowed when calling execute 0272 throw soci_error("Vectors of size 0 are not allowed."); 0273 } 0274 0275 fetchSize_ = initialFetchSize_; 0276 0277 // pre-use should be executed before inspecting the sizes of use 0278 // elements, as they can be resized in type conversion routines 0279 0280 pre_use(); 0281 0282 std::size_t const bindSize = uses_size(); 0283 0284 if (bindSize > 1 && fetchSize_ > 1) 0285 { 0286 throw soci_error( 0287 "Bulk insert/update and bulk select not allowed in same query"); 0288 } 0289 0290 // looks like a hack and it is - row description should happen 0291 // *after* the use elements were completely prepared 0292 // and *before* the into elements are touched, so that the row 0293 // description process can inject more into elements for 0294 // implicit data exchange 0295 if (row_ != NULL && alreadyDescribed_ == false) 0296 { 0297 describe(); 0298 define_for_row(); 0299 } 0300 0301 int num = 0; 0302 if (withDataExchange) 0303 { 0304 num = 1; 0305 0306 pre_fetch(); 0307 0308 if (static_cast<int>(fetchSize_) > num) 0309 { 0310 num = static_cast<int>(fetchSize_); 0311 } 0312 if (static_cast<int>(bindSize) > num) 0313 { 0314 num = static_cast<int>(bindSize); 0315 } 0316 } 0317 0318 pre_exec(num); 0319 0320 statement_backend::exec_fetch_result res = backEnd_->execute(num); 0321 0322 bool gotData = false; 0323 0324 if (res == statement_backend::ef_success) 0325 { 0326 // the "success" means that the statement executed correctly 0327 // and for select statement this also means that some rows were read 0328 0329 if (num > 0) 0330 { 0331 gotData = true; 0332 0333 // ensure into vectors have correct size 0334 resize_intos(static_cast<std::size_t>(num)); 0335 } 0336 } 0337 else // res == ef_no_data 0338 { 0339 // the "no data" means that the end-of-rowset condition was hit 0340 // but still some rows might have been read (the last bunch of rows) 0341 // it can also mean that the statement did not produce any results 0342 0343 gotData = fetchSize_ > 1 ? resize_intos() : false; 0344 } 0345 0346 if (num > 0) 0347 { 0348 post_fetch(gotData, false); 0349 } 0350 0351 post_use(gotData); 0352 0353 session_.set_got_data(gotData); 0354 return gotData; 0355 } 0356 catch (...) 0357 { 0358 rethrow_current_exception_with_context("executing"); 0359 } 0360 } 0361 0362 long long statement_impl::get_affected_rows() 0363 { 0364 try 0365 { 0366 return backEnd_->get_affected_rows(); 0367 } 0368 catch (...) 0369 { 0370 rethrow_current_exception_with_context("getting the number of rows affected by"); 0371 } 0372 } 0373 0374 bool statement_impl::fetch() 0375 { 0376 try 0377 { 0378 if (fetchSize_ == 0) 0379 { 0380 truncate_intos(); 0381 session_.set_got_data(false); 0382 return false; 0383 } 0384 0385 bool gotData = false; 0386 0387 // vectors might have been resized between fetches 0388 std::size_t const newFetchSize = intos_size(); 0389 if (newFetchSize > initialFetchSize_) 0390 { 0391 // this is not allowed, because most likely caused reallocation 0392 // of the vector - this would require complete re-bind 0393 0394 throw soci_error( 0395 "Increasing the size of the output vector is not supported."); 0396 } 0397 else if (newFetchSize == 0) 0398 { 0399 session_.set_got_data(false); 0400 return false; 0401 } 0402 else 0403 { 0404 // the output vector was downsized or remains the same as before 0405 fetchSize_ = newFetchSize; 0406 } 0407 0408 statement_backend::exec_fetch_result const res = backEnd_->fetch(static_cast<int>(fetchSize_)); 0409 if (res == statement_backend::ef_success) 0410 { 0411 // the "success" means that some number of rows was read 0412 // and that it is not yet the end-of-rowset (there are more rows) 0413 0414 gotData = true; 0415 0416 // ensure into vectors have correct size 0417 resize_intos(fetchSize_); 0418 } 0419 else // res == ef_no_data 0420 { 0421 // end-of-rowset condition 0422 0423 if (fetchSize_ > 1) 0424 { 0425 // but still the last bunch of rows might have been read 0426 gotData = resize_intos(); 0427 fetchSize_ = 0; 0428 } 0429 else 0430 { 0431 truncate_intos(); 0432 gotData = false; 0433 } 0434 } 0435 0436 post_fetch(gotData, true); 0437 session_.set_got_data(gotData); 0438 return gotData; 0439 } 0440 catch (...) 0441 { 0442 rethrow_current_exception_with_context("fetching data from"); 0443 } 0444 } 0445 0446 std::size_t statement_impl::intos_size() 0447 { 0448 // this function does not need to take into account intosForRow_ elements, 0449 // since their sizes are always 1 (which is the same and the primary 0450 // into(row) element, which has injected them) 0451 0452 std::size_t intos_size = 0; 0453 std::size_t const isize = intos_.size(); 0454 for (std::size_t i = 0; i != isize; ++i) 0455 { 0456 if (i==0) 0457 { 0458 intos_size = intos_[i]->size(); 0459 } 0460 else if (intos_size != intos_[i]->size()) 0461 { 0462 std::ostringstream msg; 0463 msg << "Bind variable size mismatch (into[" 0464 << static_cast<unsigned long>(i) << "] has size " 0465 << static_cast<unsigned long>(intos_[i]->size()) 0466 << ", into[0] has size " 0467 << static_cast<unsigned long>(intos_size); 0468 throw soci_error(msg.str()); 0469 } 0470 } 0471 return intos_size; 0472 } 0473 0474 std::size_t statement_impl::uses_size() 0475 { 0476 std::size_t usesSize = 0; 0477 std::size_t const usize = uses_.size(); 0478 for (std::size_t i = 0; i != usize; ++i) 0479 { 0480 if (i==0) 0481 { 0482 usesSize = uses_[i]->size(); 0483 if (usesSize == 0) 0484 { 0485 // this can happen only for vectors 0486 throw soci_error("Vectors of size 0 are not allowed."); 0487 } 0488 } 0489 else if (usesSize != uses_[i]->size()) 0490 { 0491 std::ostringstream msg; 0492 msg << "Bind variable size mismatch (use[" 0493 << static_cast<unsigned long>(i) << "] has size " 0494 << static_cast<unsigned long>(uses_[i]->size()) 0495 << ", use[0] has size " 0496 << static_cast<unsigned long>(usesSize); 0497 throw soci_error(msg.str()); 0498 } 0499 } 0500 return usesSize; 0501 } 0502 0503 bool statement_impl::resize_intos(std::size_t upperBound) 0504 { 0505 // this function does not need to take into account the intosForRow_ 0506 // elements, since they are never used for bulk operations 0507 0508 int rows = backEnd_->get_number_of_rows(); 0509 if (rows < 0) 0510 { 0511 rows = 0; 0512 } 0513 if (upperBound != 0 && upperBound < static_cast<std::size_t>(rows)) 0514 { 0515 rows = static_cast<int>(upperBound); 0516 } 0517 0518 std::size_t const isize = intos_.size(); 0519 for (std::size_t i = 0; i != isize; ++i) 0520 { 0521 intos_[i]->resize((std::size_t)rows); 0522 } 0523 0524 return rows > 0 ? true : false; 0525 } 0526 0527 void statement_impl::truncate_intos() 0528 { 0529 std::size_t const isize = intos_.size(); 0530 for (std::size_t i = 0; i != isize; ++i) 0531 { 0532 intos_[i]->resize(0); 0533 } 0534 } 0535 0536 void statement_impl::pre_exec(int num) 0537 { 0538 std::size_t const isize = intos_.size(); 0539 for (std::size_t i = 0; i != isize; ++i) 0540 { 0541 intos_[i]->pre_exec(num); 0542 } 0543 0544 std::size_t const ifrsize = intosForRow_.size(); 0545 for (std::size_t i = 0; i != ifrsize; ++i) 0546 { 0547 intosForRow_[i]->pre_exec(num); 0548 } 0549 0550 std::size_t const usize = uses_.size(); 0551 for (std::size_t i = 0; i != usize; ++i) 0552 { 0553 uses_[i]->pre_exec(num); 0554 } 0555 } 0556 0557 void statement_impl::pre_fetch() 0558 { 0559 std::size_t const isize = intos_.size(); 0560 for (std::size_t i = 0; i != isize; ++i) 0561 { 0562 intos_[i]->pre_fetch(); 0563 } 0564 0565 std::size_t const ifrsize = intosForRow_.size(); 0566 for (std::size_t i = 0; i != ifrsize; ++i) 0567 { 0568 intosForRow_[i]->pre_fetch(); 0569 } 0570 } 0571 0572 void statement_impl::pre_use() 0573 { 0574 std::size_t const usize = uses_.size(); 0575 for (std::size_t i = 0; i != usize; ++i) 0576 { 0577 uses_[i]->pre_use(); 0578 } 0579 } 0580 0581 void statement_impl::post_fetch(bool gotData, bool calledFromFetch) 0582 { 0583 // first iterate over intosForRow_ elements, since the Row element 0584 // (which is among the intos_ elements) might depend on the 0585 // values of those implicitly injected elements 0586 0587 std::size_t const ifrsize = intosForRow_.size(); 0588 for (std::size_t i = 0; i != ifrsize; ++i) 0589 { 0590 intosForRow_[i]->post_fetch(gotData, calledFromFetch); 0591 } 0592 0593 std::size_t const isize = intos_.size(); 0594 for (std::size_t i = 0; i != isize; ++i) 0595 { 0596 try 0597 { 0598 intos_[i]->post_fetch(gotData, calledFromFetch); 0599 } 0600 catch (soci_error& e) 0601 { 0602 // Provide the parameter number in the error message as the 0603 // exceptions thrown by the backend only say what went wrong, but 0604 // not where. 0605 std::ostringstream oss; 0606 oss << "for the parameter number " << i + 1; 0607 e.add_context(oss.str()); 0608 0609 throw; 0610 } 0611 } 0612 } 0613 0614 void statement_impl::post_use(bool gotData) 0615 { 0616 // iterate in reverse order here in case the first item 0617 // is an UseType<Values> (since it depends on the other UseTypes) 0618 for (std::size_t i = uses_.size(); i != 0; --i) 0619 { 0620 uses_[i-1]->post_use(gotData); 0621 } 0622 } 0623 0624 namespace soci 0625 { 0626 namespace details 0627 { 0628 0629 // Map data_types to stock types for dynamic result set support 0630 0631 template<> 0632 void statement_impl::bind_into<dt_string>() 0633 { 0634 into_row<std::string>(); 0635 } 0636 0637 template<> 0638 void statement_impl::bind_into<dt_double>() 0639 { 0640 into_row<double>(); 0641 } 0642 0643 template<> 0644 void statement_impl::bind_into<dt_integer>() 0645 { 0646 into_row<int>(); 0647 } 0648 0649 template<> 0650 void statement_impl::bind_into<dt_long_long>() 0651 { 0652 into_row<long long>(); 0653 } 0654 0655 template<> 0656 void statement_impl::bind_into<dt_unsigned_long_long>() 0657 { 0658 into_row<unsigned long long>(); 0659 } 0660 0661 template<> 0662 void statement_impl::bind_into<dt_date>() 0663 { 0664 into_row<std::tm>(); 0665 } 0666 0667 void statement_impl::describe() 0668 { 0669 row_->clean_up(); 0670 0671 int const numcols = backEnd_->prepare_for_describe(); 0672 for (int i = 1; i <= numcols; ++i) 0673 { 0674 data_type dtype; 0675 std::string columnName; 0676 0677 backEnd_->describe_column(i, dtype, columnName); 0678 0679 column_properties props; 0680 props.set_name(columnName); 0681 props.set_data_type(dtype); 0682 0683 switch (dtype) 0684 { 0685 case dt_string: 0686 bind_into<dt_string>(); 0687 break; 0688 case dt_blob: 0689 bind_into<dt_string>(); 0690 break; 0691 case dt_xml: 0692 bind_into<dt_string>(); 0693 break; 0694 case dt_double: 0695 bind_into<dt_double>(); 0696 break; 0697 case dt_integer: 0698 bind_into<dt_integer>(); 0699 break; 0700 case dt_long_long: 0701 bind_into<dt_long_long>(); 0702 break; 0703 case dt_unsigned_long_long: 0704 bind_into<dt_unsigned_long_long>(); 0705 break; 0706 case dt_date: 0707 bind_into<dt_date>(); 0708 break; 0709 default: 0710 std::ostringstream msg; 0711 msg << "db column type " << dtype 0712 <<" not supported for dynamic selects"<<std::endl; 0713 throw soci_error(msg.str()); 0714 } 0715 row_->add_properties(props); 0716 } 0717 0718 alreadyDescribed_ = true; 0719 } 0720 0721 } // namespace details 0722 } // namespace soci 0723 0724 void statement_impl::set_row(row * r) 0725 { 0726 if (row_ != NULL) 0727 { 0728 throw soci_error( 0729 "Only one Row element allowed in a single statement."); 0730 } 0731 0732 row_ = r; 0733 row_->uppercase_column_names(session_.get_uppercase_column_names()); 0734 } 0735 0736 std::string statement_impl::rewrite_for_procedure_call(std::string const & query) 0737 { 0738 return backEnd_->rewrite_for_procedure_call(query); 0739 } 0740 0741 void statement_impl::inc_ref() 0742 { 0743 ++refCount_; 0744 } 0745 0746 void statement_impl::dec_ref() 0747 { 0748 if (--refCount_ == 0) 0749 { 0750 delete this; 0751 } 0752 } 0753 0754 standard_into_type_backend * 0755 statement_impl::make_into_type_backend() 0756 { 0757 return backEnd_->make_into_type_backend(); 0758 } 0759 0760 standard_use_type_backend * 0761 statement_impl::make_use_type_backend() 0762 { 0763 return backEnd_->make_use_type_backend(); 0764 } 0765 0766 vector_into_type_backend * 0767 statement_impl::make_vector_into_type_backend() 0768 { 0769 return backEnd_->make_vector_into_type_backend(); 0770 } 0771 0772 vector_use_type_backend * 0773 statement_impl::make_vector_use_type_backend() 0774 { 0775 return backEnd_->make_vector_use_type_backend(); 0776 } 0777 0778 SOCI_NORETURN 0779 statement_impl::rethrow_current_exception_with_context(char const* operation) 0780 { 0781 try 0782 { 0783 throw; 0784 } 0785 catch (soci_error& e) 0786 { 0787 if (!query_.empty()) 0788 { 0789 std::ostringstream oss; 0790 oss << "while " << operation << " \"" << query_ << "\""; 0791 0792 if (!uses_.empty()) 0793 { 0794 oss << " with "; 0795 0796 std::size_t const usize = uses_.size(); 0797 for (std::size_t i = 0; i != usize; ++i) 0798 { 0799 if (i != 0) 0800 oss << ", "; 0801 0802 details::use_type_base const& u = *uses_[i]; 0803 0804 // Use the name specified in the "use()" call if any, 0805 // otherwise get the name of the matching parameter from 0806 // the query itself, as parsed by the backend. 0807 std::string name = u.get_name(); 0808 if (name.empty()) 0809 name = backEnd_->get_parameter_name(static_cast<int>(i)); 0810 0811 oss << ":"; 0812 if (!name.empty()) 0813 oss << name; 0814 else 0815 oss << (i + 1); 0816 oss << "="; 0817 0818 u.dump_value(oss); 0819 } 0820 } 0821 0822 e.add_context(oss.str()); 0823 } 0824 0825 throw; 0826 } 0827 }