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 }