00001 // 00002 // Copyright (C) 2004-2008 Maciej Sobczak, Stephen Hutton 00003 // Distributed under the Boost Software License, Version 1.0. 00004 // (See accompanying file LICENSE_1_0.txt or copy at 00005 // http://www.boost.org/LICENSE_1_0.txt) 00006 // 00007 00008 #define SOCI_SOURCE 00009 #include "statement.h" 00010 #include "session.h" 00011 #include "into-type.h" 00012 #include "use-type.h" 00013 #include "values.h" 00014 #include <ctime> 00015 00016 #ifdef _MSC_VER 00017 #pragma warning(disable:4355) 00018 #endif 00019 00020 using namespace soci; 00021 using namespace soci::details; 00022 00023 void statement::exchange(into_type_ptr const & i) 00024 { 00025 impl_->exchange(i); 00026 } 00027 00028 void statement::exchange(use_type_ptr const & u) 00029 { 00030 impl_->exchange(u); 00031 } 00032 00033 statement_impl::statement_impl(session & s) 00034 : session_(s), refCount_(1), row_(0), 00035 fetchSize_(1), initialFetchSize_(1), 00036 alreadyDescribed_(false) 00037 { 00038 backEnd_ = s.make_statement_backend(); 00039 } 00040 00041 statement_impl::statement_impl(prepare_temp_type const & prep) 00042 : session_(prep.get_prepare_info()->session_), 00043 refCount_(1), row_(0), fetchSize_(1), alreadyDescribed_(false) 00044 { 00045 backEnd_ = session_.make_statement_backend(); 00046 00047 ref_counted_prepare_info * prepInfo = prep.get_prepare_info(); 00048 00049 // take all bind/define info 00050 intos_.swap(prepInfo->intos_); 00051 uses_.swap(prepInfo->uses_); 00052 00053 // allocate handle 00054 alloc(); 00055 00056 // prepare the statement 00057 query_ = prepInfo->get_query(); 00058 prepare(query_); 00059 00060 define_and_bind(); 00061 } 00062 00063 statement_impl::~statement_impl() 00064 { 00065 clean_up(); 00066 } 00067 00068 void statement_impl::alloc() 00069 { 00070 backEnd_->alloc(); 00071 } 00072 00073 void statement_impl::bind(values & values) 00074 { 00075 std::size_t cnt = 0; 00076 00077 try 00078 { 00079 for (std::vector<details::standard_use_type *>::iterator it = 00080 values.uses_.begin(); it != values.uses_.end(); ++it) 00081 { 00082 // only bind those variables which are: 00083 // - either named and actually referenced in the statement, 00084 // - or positional 00085 00086 std::string const & useName = (*it)->get_name(); 00087 if (useName.empty()) 00088 { 00089 // positional use element 00090 00091 int position = static_cast<int>(uses_.size()); 00092 (*it)->bind(*this, position); 00093 uses_.push_back(*it); 00094 indicators_.push_back(values.indicators_[cnt]); 00095 } 00096 else 00097 { 00098 // named use element - check if it is used 00099 const std::string placeholder = ":" + useName; 00100 00101 std::size_t pos = query_.find(placeholder); 00102 if (pos != std::string::npos) 00103 { 00104 const char nextChar = query_[pos + placeholder.size()]; 00105 if (nextChar == ' ' || nextChar == ',' || 00106 nextChar == '\0' || nextChar == ')') 00107 { 00108 int position = static_cast<int>(uses_.size()); 00109 (*it)->bind(*this, position); 00110 uses_.push_back(*it); 00111 indicators_.push_back(values.indicators_[cnt]); 00112 } 00113 else 00114 { 00115 values.add_unused(*it, values.indicators_[cnt]); 00116 } 00117 } 00118 else 00119 { 00120 values.add_unused(*it, values.indicators_[cnt]); 00121 } 00122 } 00123 00124 cnt++; 00125 } 00126 } 00127 catch (...) 00128 { 00129 for (std::size_t i = ++cnt; i != values.uses_.size(); ++i) 00130 { 00131 values.add_unused(values.uses_[i], values.indicators_[i]); 00132 } 00133 throw; 00134 } 00135 } 00136 00137 void statement_impl::exchange(into_type_ptr const & i) 00138 { 00139 intos_.push_back(i.get()); 00140 i.release(); 00141 } 00142 00143 void statement_impl::exchange_for_row(into_type_ptr const & i) 00144 { 00145 intosForRow_.push_back(i.get()); 00146 i.release(); 00147 } 00148 00149 void statement_impl::exchange_for_rowset(into_type_ptr const & i) 00150 { 00151 if (intos_.empty() == false) 00152 { 00153 throw soci_error("Explicit into elements not allowed with rowset."); 00154 } 00155 00156 into_type_base * p = i.get(); 00157 intos_.push_back(p); 00158 i.release(); 00159 00160 int definePosition = 1; 00161 p->define(*this, definePosition); 00162 definePositionForRow_ = definePosition; 00163 } 00164 00165 void statement_impl::exchange(use_type_ptr const & u) 00166 { 00167 uses_.push_back(u.get()); 00168 u.release(); 00169 } 00170 00171 void statement_impl::clean_up() 00172 { 00173 // deallocate all bind and define objects 00174 std::size_t const isize = intos_.size(); 00175 for (std::size_t i = isize; i != 0; --i) 00176 { 00177 intos_[i - 1]->clean_up(); 00178 delete intos_[i - 1]; 00179 intos_.resize(i - 1); 00180 } 00181 00182 std::size_t const ifrsize = intosForRow_.size(); 00183 for (std::size_t i = ifrsize; i != 0; --i) 00184 { 00185 intosForRow_[i - 1]->clean_up(); 00186 delete intosForRow_[i - 1]; 00187 intosForRow_.resize(i - 1); 00188 } 00189 00190 std::size_t const usize = uses_.size(); 00191 for (std::size_t i = usize; i != 0; --i) 00192 { 00193 uses_[i - 1]->clean_up(); 00194 delete uses_[i - 1]; 00195 uses_.resize(i - 1); 00196 } 00197 00198 std::size_t const indsize = indicators_.size(); 00199 for (std::size_t i = 0; i != indsize; ++i) 00200 { 00201 delete indicators_[i]; 00202 indicators_[i] = NULL; 00203 } 00204 00205 if (backEnd_ != NULL) 00206 { 00207 backEnd_->clean_up(); 00208 delete backEnd_; 00209 backEnd_ = NULL; 00210 } 00211 } 00212 00213 void statement_impl::prepare(std::string const & query, 00214 statement_type eType) 00215 { 00216 query_ = query; 00217 session_.log_query(query); 00218 00219 backEnd_->prepare(query, eType); 00220 } 00221 00222 void statement_impl::define_and_bind() 00223 { 00224 int definePosition = 1; 00225 std::size_t const isize = intos_.size(); 00226 for (std::size_t i = 0; i != isize; ++i) 00227 { 00228 intos_[i]->define(*this, definePosition); 00229 } 00230 00231 // if there are some implicite into elements 00232 // injected by the row description process, 00233 // they should be defined in the later phase, 00234 // starting at the position where the above loop finished 00235 definePositionForRow_ = definePosition; 00236 00237 int bindPosition = 1; 00238 std::size_t const usize = uses_.size(); 00239 for (std::size_t i = 0; i != usize; ++i) 00240 { 00241 uses_[i]->bind(*this, bindPosition); 00242 } 00243 } 00244 00245 void statement_impl::define_for_row() 00246 { 00247 std::size_t const isize = intosForRow_.size(); 00248 for (std::size_t i = 0; i != isize; ++i) 00249 { 00250 intosForRow_[i]->define(*this, definePositionForRow_); 00251 } 00252 } 00253 00254 void statement_impl::undefine_and_bind() 00255 { 00256 std::size_t const isize = intos_.size(); 00257 for (std::size_t i = isize; i != 0; --i) 00258 { 00259 intos_[i - 1]->clean_up(); 00260 } 00261 00262 std::size_t const ifrsize = intosForRow_.size(); 00263 for (std::size_t i = ifrsize; i != 0; --i) 00264 { 00265 intosForRow_[i - 1]->clean_up(); 00266 } 00267 00268 std::size_t const usize = uses_.size(); 00269 for (std::size_t i = usize; i != 0; --i) 00270 { 00271 uses_[i - 1]->clean_up(); 00272 } 00273 } 00274 00275 bool statement_impl::execute(bool withDataExchange) 00276 { 00277 initialFetchSize_ = intos_size(); 00278 00279 if (intos_.empty() == false && initialFetchSize_ == 0) 00280 { 00281 // this can happen only with into-vectors elements 00282 // and is not allowed when calling execute 00283 throw soci_error("Vectors of size 0 are not allowed."); 00284 } 00285 00286 fetchSize_ = initialFetchSize_; 00287 00288 // pre-use should be executed before inspecting the sizes of use 00289 // elements, as they can be resized in type conversion routines 00290 00291 pre_use(); 00292 00293 std::size_t bindSize = uses_size(); 00294 00295 if (bindSize > 1 && fetchSize_ > 1) 00296 { 00297 throw soci_error( 00298 "Bulk insert/update and bulk select not allowed in same query"); 00299 } 00300 00301 // looks like a hack and it is - row description should happen 00302 // *after* the use elements were completely prepared 00303 // and *before* the into elements are touched, so that the row 00304 // description process can inject more into elements for 00305 // implicit data exchange 00306 if (row_ != NULL && alreadyDescribed_ == false) 00307 { 00308 describe(); 00309 define_for_row(); 00310 } 00311 00312 int num = 0; 00313 if (withDataExchange) 00314 { 00315 num = 1; 00316 00317 pre_fetch(); 00318 00319 if (static_cast<int>(fetchSize_) > num) 00320 { 00321 num = static_cast<int>(fetchSize_); 00322 } 00323 if (static_cast<int>(bindSize) > num) 00324 { 00325 num = static_cast<int>(bindSize); 00326 } 00327 } 00328 00329 statement_backend::exec_fetch_result res = backEnd_->execute(num); 00330 00331 bool gotData = false; 00332 00333 if (res == statement_backend::ef_success) 00334 { 00335 // the "success" means that the statement executed correctly 00336 // and for select statement this also means that some rows were read 00337 00338 if (num > 0) 00339 { 00340 gotData = true; 00341 00342 // ensure into vectors have correct size 00343 resize_intos(static_cast<std::size_t>(num)); 00344 } 00345 } 00346 else // res == ef_no_data 00347 { 00348 // the "no data" means that the end-of-rowset condition was hit 00349 // but still some rows might have been read (the last bunch of rows) 00350 // it can also mean that the statement did not produce any results 00351 00352 gotData = fetchSize_ > 1 ? resize_intos() : false; 00353 } 00354 00355 if (num > 0) 00356 { 00357 post_fetch(gotData, false); 00358 } 00359 00360 post_use(gotData); 00361 00362 session_.set_got_data(gotData); 00363 return gotData; 00364 } 00365 00366 bool statement_impl::fetch() 00367 { 00368 if (fetchSize_ == 0) 00369 { 00370 truncate_intos(); 00371 session_.set_got_data(false); 00372 return false; 00373 } 00374 00375 bool gotData = false; 00376 00377 // vectors might have been resized between fetches 00378 std::size_t newFetchSize = intos_size(); 00379 if (newFetchSize > initialFetchSize_) 00380 { 00381 // this is not allowed, because most likely caused reallocation 00382 // of the vector - this would require complete re-bind 00383 00384 throw soci_error( 00385 "Increasing the size of the output vector is not supported."); 00386 } 00387 else if (newFetchSize == 0) 00388 { 00389 session_.set_got_data(false); 00390 return false; 00391 } 00392 else 00393 { 00394 // the output vector was downsized or remains the same as before 00395 fetchSize_ = newFetchSize; 00396 } 00397 00398 statement_backend::exec_fetch_result res = 00399 backEnd_->fetch(static_cast<int>(fetchSize_)); 00400 if (res == statement_backend::ef_success) 00401 { 00402 // the "success" means that some number of rows was read 00403 // and that it is not yet the end-of-rowset (there are more rows) 00404 00405 gotData = true; 00406 00407 // ensure into vectors have correct size 00408 resize_intos(fetchSize_); 00409 } 00410 else // res == ef_no_data 00411 { 00412 // end-of-rowset condition 00413 00414 if (fetchSize_ > 1) 00415 { 00416 // but still the last bunch of rows might have been read 00417 gotData = resize_intos(); 00418 fetchSize_ = 0; 00419 } 00420 else 00421 { 00422 truncate_intos(); 00423 gotData = false; 00424 } 00425 } 00426 00427 post_fetch(gotData, true); 00428 session_.set_got_data(gotData); 00429 return gotData; 00430 } 00431 00432 std::size_t statement_impl::intos_size() 00433 { 00434 // this function does not need to take into account intosForRow_ elements, 00435 // since their sizes are always 1 (which is the same and the primary 00436 // into(row) element, which has injected them) 00437 00438 std::size_t intos_size = 0; 00439 std::size_t const isize = intos_.size(); 00440 for (std::size_t i = 0; i != isize; ++i) 00441 { 00442 if (i==0) 00443 { 00444 intos_size = intos_[i]->size(); 00445 } 00446 else if (intos_size != intos_[i]->size()) 00447 { 00448 std::ostringstream msg; 00449 msg << "Bind variable size mismatch (into[" 00450 << static_cast<unsigned long>(i) << "] has size " 00451 << static_cast<unsigned long>(intos_[i]->size()) 00452 << ", into[0] has size " 00453 << static_cast<unsigned long>(intos_size); 00454 throw soci_error(msg.str()); 00455 } 00456 } 00457 return intos_size; 00458 } 00459 00460 std::size_t statement_impl::uses_size() 00461 { 00462 std::size_t usesSize = 0; 00463 std::size_t const usize = uses_.size(); 00464 for (std::size_t i = 0; i != usize; ++i) 00465 { 00466 if (i==0) 00467 { 00468 usesSize = uses_[i]->size(); 00469 if (usesSize == 0) 00470 { 00471 // this can happen only for vectors 00472 throw soci_error("Vectors of size 0 are not allowed."); 00473 } 00474 } 00475 else if (usesSize != uses_[i]->size()) 00476 { 00477 std::ostringstream msg; 00478 msg << "Bind variable size mismatch (use[" 00479 << static_cast<unsigned long>(i) << "] has size " 00480 << static_cast<unsigned long>(uses_[i]->size()) 00481 << ", use[0] has size " 00482 << static_cast<unsigned long>(usesSize); 00483 throw soci_error(msg.str()); 00484 } 00485 } 00486 return usesSize; 00487 } 00488 00489 bool statement_impl::resize_intos(std::size_t upperBound) 00490 { 00491 // this function does not need to take into account the intosForRow_ 00492 // elements, since they are never used for bulk operations 00493 00494 std::size_t rows = backEnd_->get_number_of_rows(); 00495 if (upperBound != 0 && upperBound < rows) 00496 { 00497 rows = upperBound; 00498 } 00499 00500 std::size_t const isize = intos_.size(); 00501 for (std::size_t i = 0; i != isize; ++i) 00502 { 00503 intos_[i]->resize(rows); 00504 } 00505 00506 return rows > 0 ? true : false; 00507 } 00508 00509 void statement_impl::truncate_intos() 00510 { 00511 std::size_t const isize = intos_.size(); 00512 for (std::size_t i = 0; i != isize; ++i) 00513 { 00514 intos_[i]->resize(0); 00515 } 00516 } 00517 00518 void statement_impl::pre_fetch() 00519 { 00520 std::size_t const isize = intos_.size(); 00521 for (std::size_t i = 0; i != isize; ++i) 00522 { 00523 intos_[i]->pre_fetch(); 00524 } 00525 00526 std::size_t const ifrsize = intosForRow_.size(); 00527 for (std::size_t i = 0; i != ifrsize; ++i) 00528 { 00529 intosForRow_[i]->pre_fetch(); 00530 } 00531 } 00532 00533 void statement_impl::pre_use() 00534 { 00535 std::size_t const usize = uses_.size(); 00536 for (std::size_t i = 0; i != usize; ++i) 00537 { 00538 uses_[i]->pre_use(); 00539 } 00540 } 00541 00542 void statement_impl::post_fetch(bool gotData, bool calledFromFetch) 00543 { 00544 // first iterate over intosForRow_ elements, since the Row element 00545 // (which is among the intos_ elements) might depend on the 00546 // values of those implicitly injected elements 00547 00548 std::size_t const ifrsize = intosForRow_.size(); 00549 for (std::size_t i = 0; i != ifrsize; ++i) 00550 { 00551 intosForRow_[i]->post_fetch(gotData, calledFromFetch); 00552 } 00553 00554 std::size_t const isize = intos_.size(); 00555 for (std::size_t i = 0; i != isize; ++i) 00556 { 00557 intos_[i]->post_fetch(gotData, calledFromFetch); 00558 } 00559 } 00560 00561 void statement_impl::post_use(bool gotData) 00562 { 00563 // iterate in reverse order here in case the first item 00564 // is an UseType<Values> (since it depends on the other UseTypes) 00565 for (std::size_t i = uses_.size(); i != 0; --i) 00566 { 00567 uses_[i-1]->post_use(gotData); 00568 } 00569 } 00570 00571 namespace soci 00572 { 00573 namespace details 00574 { 00575 00576 // Map data_types to stock types for dynamic result set support 00577 00578 template<> 00579 void statement_impl::bind_into<dt_string>() 00580 { 00581 into_row<std::string>(); 00582 } 00583 00584 template<> 00585 void statement_impl::bind_into<dt_double>() 00586 { 00587 into_row<double>(); 00588 } 00589 00590 template<> 00591 void statement_impl::bind_into<dt_integer>() 00592 { 00593 into_row<int>(); 00594 } 00595 00596 template<> 00597 void statement_impl::bind_into<dt_unsigned_long>() 00598 { 00599 into_row<unsigned long>(); 00600 } 00601 00602 template<> 00603 void statement_impl::bind_into<dt_long_long>() 00604 { 00605 into_row<long long>(); 00606 } 00607 00608 template<> 00609 void statement_impl::bind_into<dt_date>() 00610 { 00611 into_row<std::tm>(); 00612 } 00613 00614 void statement_impl::describe() 00615 { 00616 row_->clean_up(); 00617 00618 int numcols = backEnd_->prepare_for_describe(); 00619 00620 for (int i = 1; i <= numcols; ++i) 00621 { 00622 data_type dtype; 00623 std::string columnName; 00624 00625 backEnd_->describe_column(i, dtype, columnName); 00626 00627 column_properties props; 00628 props.set_name(columnName); 00629 00630 props.set_data_type(dtype); 00631 switch (dtype) 00632 { 00633 case dt_string: 00634 bind_into<dt_string>(); 00635 break; 00636 case dt_double: 00637 bind_into<dt_double>(); 00638 break; 00639 case dt_integer: 00640 bind_into<dt_integer>(); 00641 break; 00642 case dt_unsigned_long: 00643 bind_into<dt_unsigned_long>(); 00644 break; 00645 case dt_long_long: 00646 bind_into<dt_long_long>(); 00647 break; 00648 case dt_date: 00649 bind_into<dt_date>(); 00650 break; 00651 default: 00652 std::ostringstream msg; 00653 msg << "db column type " << dtype 00654 <<" not supported for dynamic selects"<<std::endl; 00655 throw soci_error(msg.str()); 00656 } 00657 row_->add_properties(props); 00658 } 00659 00660 alreadyDescribed_ = true; 00661 } 00662 00663 } // namespace details 00664 } // namespace soci 00665 00666 void statement_impl::set_row(row * r) 00667 { 00668 if (row_ != NULL) 00669 { 00670 throw soci_error( 00671 "Only one Row element allowed in a single statement."); 00672 } 00673 00674 row_ = r; 00675 row_->uppercase_column_names(session_.get_uppercase_column_names()); 00676 } 00677 00678 std::string statement_impl::rewrite_for_procedure_call(std::string const & query) 00679 { 00680 return backEnd_->rewrite_for_procedure_call(query); 00681 } 00682 00683 void statement_impl::inc_ref() 00684 { 00685 ++refCount_; 00686 } 00687 00688 void statement_impl::dec_ref() 00689 { 00690 if (--refCount_ == 0) 00691 { 00692 delete this; 00693 } 00694 } 00695 00696 standard_into_type_backend * 00697 statement_impl::make_into_type_backend() 00698 { 00699 return backEnd_->make_into_type_backend(); 00700 } 00701 00702 standard_use_type_backend * 00703 statement_impl::make_use_type_backend() 00704 { 00705 return backEnd_->make_use_type_backend(); 00706 } 00707 00708 vector_into_type_backend * 00709 statement_impl::make_vector_into_type_backend() 00710 { 00711 return backEnd_->make_vector_into_type_backend(); 00712 } 00713 00714 vector_use_type_backend * 00715 statement_impl::make_vector_use_type_backend() 00716 { 00717 return backEnd_->make_vector_use_type_backend(); 00718 }
Generated on Sun Oct 3 2010 17:42:16 for EXTRAS-SOCI by Doxygen 1.7.1