SOCI Logo Get SOCI at SourceForge.net. Fast, secure and Free Open Source software downloads

statement.cpp

Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
SourceForge Logo

Generated on Sun Oct 3 2010 17:42:16 for EXTRAS-SOCI by Doxygen 1.7.1