core/statement.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2004-2008 Maciej Sobczak, Stephen Hutton
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 //
7 
8 #define SOCI_SOURCE
9 #include "statement.h"
10 #include "session.h"
11 #include "into-type.h"
12 #include "use-type.h"
13 #include "values.h"
14 #include <ctime>
15 #include <cctype>
16 
17 #ifdef _MSC_VER
18 #pragma warning(disable:4355)
19 #endif
20 
21 using namespace soci;
22 using namespace soci::details;
23 
24 
26  : session_(s), refCount_(1), row_(0),
27  fetchSize_(1), initialFetchSize_(1),
28  alreadyDescribed_(false)
29 {
31 }
32 
34  : session_(prep.get_prepare_info()->session_),
35  refCount_(1), row_(0), fetchSize_(1), alreadyDescribed_(false)
36 {
38 
39  ref_counted_prepare_info * prepInfo = prep.get_prepare_info();
40 
41  // take all bind/define info
42  intos_.swap(prepInfo->intos_);
43  uses_.swap(prepInfo->uses_);
44 
45  // allocate handle
46  alloc();
47 
48  // prepare the statement
49  query_ = prepInfo->get_query();
50  try
51  {
52  prepare(query_);
53  }
54  catch(...)
55  {
56  clean_up();
57  throw;
58  }
59 
61 }
62 
64 {
65  clean_up();
66 }
67 
69 {
70  backEnd_->alloc();
71 }
72 
74 {
75  std::size_t cnt = 0;
76 
77  try
78  {
79  for (std::vector<details::standard_use_type*>::iterator it =
80  values.uses_.begin(); it != values.uses_.end(); ++it)
81  {
82  // only bind those variables which are:
83  // - either named and actually referenced in the statement,
84  // - or positional
85 
86  std::string const& useName = (*it)->get_name();
87  if (useName.empty())
88  {
89  // positional use element
90 
91  int position = static_cast<int>(uses_.size());
92  (*it)->bind(*this, position);
93  uses_.push_back(*it);
94  indicators_.push_back(values.indicators_[cnt]);
95  }
96  else
97  {
98  // named use element - check if it is used
99  std::string const placeholder = ":" + useName;
100 
101  std::size_t pos = query_.find(placeholder);
102  while (pos != std::string::npos)
103  {
104  // Retrieve next char after placeholder
105  // make sure we do not go out of range on the string
106  const char nextChar = (pos + placeholder.size()) < query_.size() ?
107  query_[pos + placeholder.size()] : '\0';
108 
109  if (std::isalnum(nextChar))
110  {
111  // We got a partial match only,
112  // keep looking for the placeholder
113  pos = query_.find(placeholder, pos + placeholder.size());
114  }
115  else
116  {
117  int position = static_cast<int>(uses_.size());
118  (*it)->bind(*this, position);
119  uses_.push_back(*it);
120  indicators_.push_back(values.indicators_[cnt]);
121  // Ok we found it, done
122  break;
123  }
124  }
125  // In case we couldn't find the placeholder
126  if (pos == std::string::npos)
127  {
128  values.add_unused(*it, values.indicators_[cnt]);
129  }
130  }
131 
132  cnt++;
133  }
134  }
135  catch (...)
136  {
137  for (std::size_t i = ++cnt; i != values.uses_.size(); ++i)
138  {
139  values.add_unused(values.uses_[i], values.indicators_[i]);
140  }
141  throw;
142  }
143 }
144 
146 {
147  // deallocate all bind and define objects
148  std::size_t const isize = intos_.size();
149  for (std::size_t i = isize; i != 0; --i)
150  {
151  intos_[i - 1]->clean_up();
152  delete intos_[i - 1];
153  intos_.resize(i - 1);
154  }
155 
156  std::size_t const ifrsize = intosForRow_.size();
157  for (std::size_t i = ifrsize; i != 0; --i)
158  {
159  intosForRow_[i - 1]->clean_up();
160  delete intosForRow_[i - 1];
161  intosForRow_.resize(i - 1);
162  }
163 
164  std::size_t const usize = uses_.size();
165  for (std::size_t i = usize; i != 0; --i)
166  {
167  uses_[i - 1]->clean_up();
168  delete uses_[i - 1];
169  uses_.resize(i - 1);
170  }
171 
172  std::size_t const indsize = indicators_.size();
173  for (std::size_t i = 0; i != indsize; ++i)
174  {
175  delete indicators_[i];
176  indicators_[i] = NULL;
177  }
178 
179  if (backEnd_ != NULL)
180  {
181  backEnd_->clean_up();
182  delete backEnd_;
183  backEnd_ = NULL;
184  }
185 }
186 
187 void statement_impl::prepare(std::string const & query,
188  statement_type eType)
189 {
190  query_ = query;
191  session_.log_query(query);
192 
193  backEnd_->prepare(query, eType);
194 }
195 
197 {
198  int definePosition = 1;
199  std::size_t const isize = intos_.size();
200  for (std::size_t i = 0; i != isize; ++i)
201  {
202  intos_[i]->define(*this, definePosition);
203  }
204 
205  // if there are some implicite into elements
206  // injected by the row description process,
207  // they should be defined in the later phase,
208  // starting at the position where the above loop finished
209  definePositionForRow_ = definePosition;
210 
211  int bindPosition = 1;
212  std::size_t const usize = uses_.size();
213  for (std::size_t i = 0; i != usize; ++i)
214  {
215  uses_[i]->bind(*this, bindPosition);
216  }
217 }
218 
220 {
221  std::size_t const isize = intosForRow_.size();
222  for (std::size_t i = 0; i != isize; ++i)
223  {
224  intosForRow_[i]->define(*this, definePositionForRow_);
225  }
226 }
227 
229 {
230  std::size_t const isize = intos_.size();
231  for (std::size_t i = isize; i != 0; --i)
232  {
233  intos_[i - 1]->clean_up();
234  }
235 
236  std::size_t const ifrsize = intosForRow_.size();
237  for (std::size_t i = ifrsize; i != 0; --i)
238  {
239  intosForRow_[i - 1]->clean_up();
240  }
241 
242  std::size_t const usize = uses_.size();
243  for (std::size_t i = usize; i != 0; --i)
244  {
245  uses_[i - 1]->clean_up();
246  }
247 }
248 
249 bool statement_impl::execute(bool withDataExchange)
250 {
252 
253  if (intos_.empty() == false && initialFetchSize_ == 0)
254  {
255  // this can happen only with into-vectors elements
256  // and is not allowed when calling execute
257  throw soci_error("Vectors of size 0 are not allowed.");
258  }
259 
261 
262  // pre-use should be executed before inspecting the sizes of use
263  // elements, as they can be resized in type conversion routines
264 
265  pre_use();
266 
267  std::size_t const bindSize = uses_size();
268 
269  if (bindSize > 1 && fetchSize_ > 1)
270  {
271  throw soci_error(
272  "Bulk insert/update and bulk select not allowed in same query");
273  }
274 
275  // looks like a hack and it is - row description should happen
276  // *after* the use elements were completely prepared
277  // and *before* the into elements are touched, so that the row
278  // description process can inject more into elements for
279  // implicit data exchange
280  if (row_ != NULL && alreadyDescribed_ == false)
281  {
282  describe();
283  define_for_row();
284  }
285 
286  int num = 0;
287  if (withDataExchange)
288  {
289  num = 1;
290 
291  pre_fetch();
292 
293  if (static_cast<int>(fetchSize_) > num)
294  {
295  num = static_cast<int>(fetchSize_);
296  }
297  if (static_cast<int>(bindSize) > num)
298  {
299  num = static_cast<int>(bindSize);
300  }
301  }
302 
304 
305  bool gotData = false;
306 
308  {
309  // the "success" means that the statement executed correctly
310  // and for select statement this also means that some rows were read
311 
312  if (num > 0)
313  {
314  gotData = true;
315 
316  // ensure into vectors have correct size
317  resize_intos(static_cast<std::size_t>(num));
318  }
319  }
320  else // res == ef_no_data
321  {
322  // the "no data" means that the end-of-rowset condition was hit
323  // but still some rows might have been read (the last bunch of rows)
324  // it can also mean that the statement did not produce any results
325 
326  gotData = fetchSize_ > 1 ? resize_intos() : false;
327  }
328 
329  if (num > 0)
330  {
331  post_fetch(gotData, false);
332  }
333 
334  post_use(gotData);
335 
336  session_.set_got_data(gotData);
337  return gotData;
338 }
339 
341 {
342  return backEnd_->get_affected_rows();
343 }
344 
346 {
347  if (fetchSize_ == 0)
348  {
349  truncate_intos();
350  session_.set_got_data(false);
351  return false;
352  }
353 
354  bool gotData = false;
355 
356  // vectors might have been resized between fetches
357  std::size_t const newFetchSize = intos_size();
358  if (newFetchSize > initialFetchSize_)
359  {
360  // this is not allowed, because most likely caused reallocation
361  // of the vector - this would require complete re-bind
362 
363  throw soci_error(
364  "Increasing the size of the output vector is not supported.");
365  }
366  else if (newFetchSize == 0)
367  {
368  session_.set_got_data(false);
369  return false;
370  }
371  else
372  {
373  // the output vector was downsized or remains the same as before
374  fetchSize_ = newFetchSize;
375  }
376 
377  statement_backend::exec_fetch_result const res = backEnd_->fetch(static_cast<int>(fetchSize_));
379  {
380  // the "success" means that some number of rows was read
381  // and that it is not yet the end-of-rowset (there are more rows)
382 
383  gotData = true;
384 
385  // ensure into vectors have correct size
387  }
388  else // res == ef_no_data
389  {
390  // end-of-rowset condition
391 
392  if (fetchSize_ > 1)
393  {
394  // but still the last bunch of rows might have been read
395  gotData = resize_intos();
396  fetchSize_ = 0;
397  }
398  else
399  {
400  truncate_intos();
401  gotData = false;
402  }
403  }
404 
405  post_fetch(gotData, true);
406  session_.set_got_data(gotData);
407  return gotData;
408 }
409 
411 {
412  // this function does not need to take into account intosForRow_ elements,
413  // since their sizes are always 1 (which is the same and the primary
414  // into(row) element, which has injected them)
415 
416  std::size_t intos_size = 0;
417  std::size_t const isize = intos_.size();
418  for (std::size_t i = 0; i != isize; ++i)
419  {
420  if (i==0)
421  {
422  intos_size = intos_[i]->size();
423  }
424  else if (intos_size != intos_[i]->size())
425  {
426  std::ostringstream msg;
427  msg << "Bind variable size mismatch (into["
428  << static_cast<unsigned long>(i) << "] has size "
429  << static_cast<unsigned long>(intos_[i]->size())
430  << ", into[0] has size "
431  << static_cast<unsigned long>(intos_size);
432  throw soci_error(msg.str());
433  }
434  }
435  return intos_size;
436 }
437 
439 {
440  std::size_t usesSize = 0;
441  std::size_t const usize = uses_.size();
442  for (std::size_t i = 0; i != usize; ++i)
443  {
444  if (i==0)
445  {
446  usesSize = uses_[i]->size();
447  if (usesSize == 0)
448  {
449  // this can happen only for vectors
450  throw soci_error("Vectors of size 0 are not allowed.");
451  }
452  }
453  else if (usesSize != uses_[i]->size())
454  {
455  std::ostringstream msg;
456  msg << "Bind variable size mismatch (use["
457  << static_cast<unsigned long>(i) << "] has size "
458  << static_cast<unsigned long>(uses_[i]->size())
459  << ", use[0] has size "
460  << static_cast<unsigned long>(usesSize);
461  throw soci_error(msg.str());
462  }
463  }
464  return usesSize;
465 }
466 
467 bool statement_impl::resize_intos(std::size_t upperBound)
468 {
469  // this function does not need to take into account the intosForRow_
470  // elements, since they are never used for bulk operations
471 
472  int rows = backEnd_->get_number_of_rows();
473  if (rows < 0)
474  {
475  rows = 0;
476  }
477  if (upperBound != 0 && upperBound < static_cast<std::size_t>(rows))
478  {
479  rows = static_cast<int>(upperBound);
480  }
481 
482  std::size_t const isize = intos_.size();
483  for (std::size_t i = 0; i != isize; ++i)
484  {
485  intos_[i]->resize((std::size_t)rows);
486  }
487 
488  return rows > 0 ? true : false;
489 }
490 
492 {
493  std::size_t const isize = intos_.size();
494  for (std::size_t i = 0; i != isize; ++i)
495  {
496  intos_[i]->resize(0);
497  }
498 }
499 
501 {
502  std::size_t const isize = intos_.size();
503  for (std::size_t i = 0; i != isize; ++i)
504  {
505  intos_[i]->pre_fetch();
506  }
507 
508  std::size_t const ifrsize = intosForRow_.size();
509  for (std::size_t i = 0; i != ifrsize; ++i)
510  {
511  intosForRow_[i]->pre_fetch();
512  }
513 }
514 
516 {
517  std::size_t const usize = uses_.size();
518  for (std::size_t i = 0; i != usize; ++i)
519  {
520  uses_[i]->pre_use();
521  }
522 }
523 
524 void statement_impl::post_fetch(bool gotData, bool calledFromFetch)
525 {
526  // first iterate over intosForRow_ elements, since the Row element
527  // (which is among the intos_ elements) might depend on the
528  // values of those implicitly injected elements
529 
530  std::size_t const ifrsize = intosForRow_.size();
531  for (std::size_t i = 0; i != ifrsize; ++i)
532  {
533  intosForRow_[i]->post_fetch(gotData, calledFromFetch);
534  }
535 
536  std::size_t const isize = intos_.size();
537  for (std::size_t i = 0; i != isize; ++i)
538  {
539  intos_[i]->post_fetch(gotData, calledFromFetch);
540  }
541 }
542 
543 void statement_impl::post_use(bool gotData)
544 {
545  // iterate in reverse order here in case the first item
546  // is an UseType<Values> (since it depends on the other UseTypes)
547  for (std::size_t i = uses_.size(); i != 0; --i)
548  {
549  uses_[i-1]->post_use(gotData);
550  }
551 }
552 
553 namespace soci
554 {
555 namespace details
556 {
557 
558 // Map data_types to stock types for dynamic result set support
559 
560 template<>
561 void statement_impl::bind_into<dt_string>()
562 {
563  into_row<std::string>();
564 }
565 
566 template<>
567 void statement_impl::bind_into<dt_double>()
568 {
569  into_row<double>();
570 }
571 
572 template<>
573 void statement_impl::bind_into<dt_integer>()
574 {
575  into_row<int>();
576 }
577 
578 template<>
579 void statement_impl::bind_into<dt_long_long>()
580 {
581  into_row<long long>();
582 }
583 
584 template<>
585 void statement_impl::bind_into<dt_unsigned_long_long>()
586 {
587  into_row<unsigned long long>();
588 }
589 
590 template<>
591 void statement_impl::bind_into<dt_date>()
592 {
593  into_row<std::tm>();
594 }
595 
597 {
598  row_->clean_up();
599 
600  int const numcols = backEnd_->prepare_for_describe();
601  for (int i = 1; i <= numcols; ++i)
602  {
603  data_type dtype;
604  std::string columnName;
605 
606  backEnd_->describe_column(i, dtype, columnName);
607 
608  column_properties props;
609  props.set_name(columnName);
610  props.set_data_type(dtype);
611 
612  switch (dtype)
613  {
614  case dt_string:
615  bind_into<dt_string>();
616  break;
617  case dt_double:
618  bind_into<dt_double>();
619  break;
620  case dt_integer:
621  bind_into<dt_integer>();
622  break;
623  case dt_long_long:
624  bind_into<dt_long_long>();
625  break;
627  bind_into<dt_unsigned_long_long>();
628  break;
629  case dt_date:
630  bind_into<dt_date>();
631  break;
632  default:
633  std::ostringstream msg;
634  msg << "db column type " << dtype
635  <<" not supported for dynamic selects"<<std::endl;
636  throw soci_error(msg.str());
637  }
638  row_->add_properties(props);
639  }
640 
641  alreadyDescribed_ = true;
642 }
643 
644 } // namespace details
645 } // namespace soci
646 
648 {
649  if (row_ != NULL)
650  {
651  throw soci_error(
652  "Only one Row element allowed in a single statement.");
653  }
654 
655  row_ = r;
657 }
658 
659 std::string statement_impl::rewrite_for_procedure_call(std::string const & query)
660 {
661  return backEnd_->rewrite_for_procedure_call(query);
662 }
663 
665 {
666  ++refCount_;
667 }
668 
670 {
671  if (--refCount_ == 0)
672  {
673  delete this;
674  }
675 }
676 
679 {
681 }
682 
685 {
687 }
688 
691 {
693 }
694 
697 {
699 }
standard_use_type_backend * make_use_type_backend()
bool execute(bool withDataExchange=false)
virtual vector_into_type_backend * make_vector_into_type_backend()=0
virtual exec_fetch_result execute(int number)=0
std::vector< indicator * > indicators_
Definition: statement.h:92
void log_query(std::string const &query)
vector_into_type_backend * make_vector_into_type_backend()
virtual void describe_column(int colNum, data_type &dtype, std::string &column_name)=0
into_type_vector intosForRow_
Definition: statement.h:104
Definition: row.h:41
void set_got_data(bool gotData)
virtual standard_use_type_backend * make_use_type_backend()=0
vector_use_type_backend * make_vector_use_type_backend()
void prepare(std::string const &query, statement_type eType=st_repeatable_query)
virtual long long get_affected_rows()=0
standard_into_type_backend * make_into_type_backend()
std::string rewrite_for_procedure_call(std::string const &query)
virtual exec_fetch_result fetch(int number)=0
bool resize_intos(std::size_t upperBound=0)
soci::details::statement_backend * backEnd_
Definition: statement.h:155
void add_unused(details::use_type_base *u, indicator *i)
Definition: values.h:317
void post_fetch(bool gotData, bool calledFromFetch)
std::vector< indicator * > indicators_
Definition: values.h:243
details::statement_backend * make_statement_backend()
std::vector< details::standard_use_type * > uses_
Definition: values.h:241
virtual vector_use_type_backend * make_vector_use_type_backend()=0
virtual standard_into_type_backend * make_into_type_backend()=0
ref_counted_prepare_info * get_prepare_info() const
into_type_vector intos_
Definition: statement.h:90
bool get_uppercase_column_names() const
virtual int prepare_for_describe()=0
void uppercase_column_names(bool forceToUpper)
Definition: row.cpp:29
void clean_up()
Definition: row.cpp:65
virtual void prepare(std::string const &query, statement_type eType)=0
void set_data_type(data_type dataType)
Definition: row.h:34
void set_name(std::string const &name)
Definition: row.h:33
void add_properties(column_properties const &cp)
Definition: row.cpp:34
virtual std::string rewrite_for_procedure_call(std::string const &query)=0


asr_lib_ism
Author(s): Hanselmann Fabian, Heller Florian, Heizmann Heinrich, Kübler Marcel, Mehlhaus Jonas, Meißner Pascal, Qattan Mohamad, Reckling Reno, Stroh Daniel
autogenerated on Wed Jan 8 2020 04:02:41