backends/mysql/statement.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
3 // MySQL backend copyright (C) 2006 Pawel Aleksander Fedorynski
4 // Distributed under the Boost Software License, Version 1.0.
5 // (See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7 //
8 
9 #define SOCI_MYSQL_SOURCE
10 #include "soci-mysql.h"
11 #include <cctype>
12 #include <ciso646>
13 //#include <iostream>
14 
15 #ifdef _MSC_VER
16 #pragma warning(disable:4355)
17 #endif
18 
19 using namespace soci;
20 using namespace soci::details;
21 using std::string;
22 
23 
26  : session_(session), result_(NULL),
27  rowsAffectedBulk_(-1LL), justDescribed_(false),
28  hasIntoElements_(false), hasVectorIntoElements_(false),
29  hasUseElements_(false), hasVectorUseElements_(false)
30 {
31 }
32 
34 {
35  // nothing to do here.
36 }
37 
39 {
40  // 'reset' the value for a
41  // potential new execution.
42  rowsAffectedBulk_ = -1;
43 
44  if (result_ != NULL)
45  {
46  mysql_free_result(result_);
47  result_ = NULL;
48  }
49 }
50 
51 void mysql_statement_backend::prepare(std::string const & query,
52  statement_type /* eType */)
53 {
54  queryChunks_.clear();
55  enum { eNormal, eInQuotes, eInName } state = eNormal;
56 
57  std::string name;
58  queryChunks_.push_back("");
59 
60  bool escaped = false;
61  for (std::string::const_iterator it = query.begin(), end = query.end();
62  it != end; ++it)
63  {
64  switch (state)
65  {
66  case eNormal:
67  if (*it == '\'')
68  {
69  queryChunks_.back() += *it;
70  state = eInQuotes;
71  }
72  else if (*it == ':')
73  {
74  const std::string::const_iterator next_it = it + 1;
75  // Check whether this is an assignment (e.g. @x:=y)
76  // and treat it as a special case, not as a named binding.
77  if (next_it != end && *next_it == '=')
78  {
79  queryChunks_.back() += ":=";
80  ++it;
81  }
82  else
83  {
84  state = eInName;
85  }
86  }
87  else // regular character, stay in the same state
88  {
89  queryChunks_.back() += *it;
90  }
91  break;
92  case eInQuotes:
93  if (*it == '\'' && !escaped)
94  {
95  queryChunks_.back() += *it;
96  state = eNormal;
97  }
98  else // regular quoted character
99  {
100  queryChunks_.back() += *it;
101  }
102  escaped = *it == '\\' && !escaped;
103  break;
104  case eInName:
105  if (std::isalnum(*it) || *it == '_')
106  {
107  name += *it;
108  }
109  else // end of name
110  {
111  names_.push_back(name);
112  name.clear();
113  queryChunks_.push_back("");
114  queryChunks_.back() += *it;
115  state = eNormal;
116  }
117  break;
118  }
119  }
120 
121  if (state == eInName)
122  {
123  names_.push_back(name);
124  }
125 /*
126  cerr << "Chunks: ";
127  for (std::vector<std::string>::iterator i = queryChunks_.begin();
128  i != queryChunks_.end(); ++i)
129  {
130  cerr << "\"" << *i << "\" ";
131  }
132  cerr << "\nNames: ";
133  for (std::vector<std::string>::iterator i = names_.begin();
134  i != names_.end(); ++i)
135  {
136  cerr << "\"" << *i << "\" ";
137  }
138  cerr << endl;
139 */
140 }
141 
144 {
145  if (justDescribed_ == false)
146  {
147  clean_up();
148 
149  if (number > 1 && hasIntoElements_)
150  {
151  throw soci_error(
152  "Bulk use with single into elements is not supported.");
153  }
154  // number - size of vectors (into/use)
155  // numberOfExecutions - number of loops to perform
156  int numberOfExecutions = 1;
157  if (number > 0)
158  {
159  numberOfExecutions = hasUseElements_ ? 1 : number;
160  }
161 
162  std::string query;
163  if (not useByPosBuffers_.empty() or not useByNameBuffers_.empty())
164  {
165  if (not useByPosBuffers_.empty() and not useByNameBuffers_.empty())
166  {
167  throw soci_error(
168  "Binding for use elements must be either by position "
169  "or by name.");
170  }
171  long long rowsAffectedBulkTemp = 0;
172  for (int i = 0; i != numberOfExecutions; ++i)
173  {
174  std::vector<char *> paramValues;
175 
176  if (not useByPosBuffers_.empty())
177  {
178  // use elements bind by position
179  // the map of use buffers can be traversed
180  // in its natural order
181 
182  for (UseByPosBuffersMap::iterator
183  it = useByPosBuffers_.begin(),
184  end = useByPosBuffers_.end();
185  it != end; ++it)
186  {
187  char **buffers = it->second;
188  //cerr<<"i: "<<i<<", buffers[i]: "<<buffers[i]<<endl;
189  paramValues.push_back(buffers[i]);
190  }
191  }
192  else
193  {
194  // use elements bind by name
195 
196  for (std::vector<std::string>::iterator
197  it = names_.begin(), end = names_.end();
198  it != end; ++it)
199  {
200  UseByNameBuffersMap::iterator b
201  = useByNameBuffers_.find(*it);
202  if (b == useByNameBuffers_.end())
203  {
204  std::string msg(
205  "Missing use element for bind by name (");
206  msg += *it;
207  msg += ").";
208  throw soci_error(msg);
209  }
210  char **buffers = b->second;
211  paramValues.push_back(buffers[i]);
212  }
213  }
214  //cerr << "queryChunks_.size(): "<<queryChunks_.size()<<endl;
215  //cerr << "paramValues.size(): "<<paramValues.size()<<endl;
216  if (queryChunks_.size() != paramValues.size()
217  and queryChunks_.size() != paramValues.size() + 1)
218  {
219  throw soci_error("Wrong number of parameters.");
220  }
221 
222  std::vector<std::string>::const_iterator ci
223  = queryChunks_.begin();
224  for (std::vector<char*>::const_iterator
225  pi = paramValues.begin(), end = paramValues.end();
226  pi != end; ++ci, ++pi)
227  {
228  query += *ci;
229  query += *pi;
230  }
231  if (ci != queryChunks_.end())
232  {
233  query += *ci;
234  }
235  if (numberOfExecutions > 1)
236  {
237  // bulk operation
238  //std::cerr << "bulk operation:\n" << query << std::endl;
239  if (0 != mysql_real_query(session_.conn_, query.c_str(),
240  query.size()))
241  {
242  // preserve the number of rows affected so far.
243  rowsAffectedBulk_ = rowsAffectedBulkTemp;
244  throw mysql_soci_error(mysql_error(session_.conn_),
245  mysql_errno(session_.conn_));
246  }
247  else
248  {
249  rowsAffectedBulkTemp += static_cast<long long>(mysql_affected_rows(session_.conn_));
250  }
251  if (mysql_field_count(session_.conn_) != 0)
252  {
253  throw soci_error("The query shouldn't have returned"
254  " any data but it did.");
255  }
256  query.clear();
257  }
258  }
259  rowsAffectedBulk_ = rowsAffectedBulkTemp;
260  if (numberOfExecutions > 1)
261  {
262  // bulk
263  return ef_no_data;
264  }
265  }
266  else
267  {
268  query = queryChunks_.front();
269  }
270 
271  //std::cerr << query << std::endl;
272  if (0 != mysql_real_query(session_.conn_, query.c_str(),
273  query.size()))
274  {
275  throw mysql_soci_error(mysql_error(session_.conn_),
276  mysql_errno(session_.conn_));
277  }
278  result_ = mysql_store_result(session_.conn_);
279  if (result_ == NULL and mysql_field_count(session_.conn_) != 0)
280  {
281  throw mysql_soci_error(mysql_error(session_.conn_),
282  mysql_errno(session_.conn_));
283  }
284  if (result_ != NULL)
285  {
286  // Cache the rows offsets to have random access to the rows later.
287  // [mysql_data_seek() is O(n) so we don't want to use it].
288  int numrows = static_cast<int>(mysql_num_rows(result_));
289  resultRowOffsets_.resize(numrows);
290  for (int i = 0; i < numrows; i++)
291  {
292  resultRowOffsets_[i] = mysql_row_tell(result_);
293  mysql_fetch_row(result_);
294  }
295  }
296  }
297  else
298  {
299  justDescribed_ = false;
300  }
301 
302  if (result_ != NULL)
303  {
304  currentRow_ = 0;
305  rowsToConsume_ = 0;
306 
307  numberOfRows_ = static_cast<int>(mysql_num_rows(result_));
308  if (numberOfRows_ == 0)
309  {
310  return ef_no_data;
311  }
312  else
313  {
314  if (number > 0)
315  {
316  // prepare for the subsequent data consumption
317  return fetch(number);
318  }
319  else
320  {
321  // execute(0) was meant to only perform the query
322  return ef_success;
323  }
324  }
325  }
326  else
327  {
328  // it was not a SELECT
329  return ef_no_data;
330  }
331 }
332 
335 {
336  // Note: This function does not actually fetch anything from anywhere
337  // - the data was already retrieved from the server in the execute()
338  // function, and the actual consumption of this data will take place
339  // in the postFetch functions, called for each into element.
340  // Here, we only prepare for this to happen (to emulate "the Oracle way").
341 
342  // forward the "cursor" from the last fetch
344 
345  if (currentRow_ >= numberOfRows_)
346  {
347  // all rows were already consumed
348  return ef_no_data;
349  }
350  else
351  {
352  if (currentRow_ + number > numberOfRows_)
353  {
355 
356  // this simulates the behaviour of Oracle
357  // - when EOF is hit, we return ef_no_data even when there are
358  // actually some rows fetched
359  return ef_no_data;
360  }
361  else
362  {
363  rowsToConsume_ = number;
364  return ef_success;
365  }
366  }
367 }
368 
370 {
371  if (rowsAffectedBulk_ >= 0)
372  {
373  return rowsAffectedBulk_;
374  }
375  return static_cast<long long>(mysql_affected_rows(session_.conn_));
376 }
377 
379 {
380  return numberOfRows_ - currentRow_;
381 }
382 
384  std::string const &query)
385 {
386  std::string newQuery("select ");
387  newQuery += query;
388  return newQuery;
389 }
390 
392 {
393  execute(1);
394  justDescribed_ = true;
395 
396  int columns = mysql_field_count(session_.conn_);
397  return columns;
398 }
399 
401  data_type & type, std::string & columnName)
402 {
403  int pos = colNum - 1;
404  MYSQL_FIELD *field = mysql_fetch_field_direct(result_, pos);
405  switch (field->type)
406  {
407  case FIELD_TYPE_CHAR: //MYSQL_TYPE_TINY:
408  case FIELD_TYPE_SHORT: //MYSQL_TYPE_SHORT:
409  case FIELD_TYPE_INT24: //MYSQL_TYPE_INT24:
410  type = dt_integer;
411  break;
412  case FIELD_TYPE_LONG: //MYSQL_TYPE_LONG:
413  type = field->flags & UNSIGNED_FLAG ? dt_long_long
414  : dt_integer;
415  break;
416  case FIELD_TYPE_LONGLONG: //MYSQL_TYPE_LONGLONG:
417  type = field->flags & UNSIGNED_FLAG ? dt_unsigned_long_long :
418  dt_long_long;
419  break;
420  case FIELD_TYPE_FLOAT: //MYSQL_TYPE_FLOAT:
421  case FIELD_TYPE_DOUBLE: //MYSQL_TYPE_DOUBLE:
422  case FIELD_TYPE_DECIMAL: //MYSQL_TYPE_DECIMAL:
423  // Prior to MySQL v. 5.x there was no column type corresponding
424  // to MYSQL_TYPE_NEWDECIMAL. However, MySQL server 5.x happily
425  // sends field type number 246, no matter which version of libraries
426  // the client is using.
427  case 246: //MYSQL_TYPE_NEWDECIMAL:
428  type = dt_double;
429  break;
430  case FIELD_TYPE_TIMESTAMP: //MYSQL_TYPE_TIMESTAMP:
431  case FIELD_TYPE_DATE: //MYSQL_TYPE_DATE:
432  case FIELD_TYPE_TIME: //MYSQL_TYPE_TIME:
433  case FIELD_TYPE_DATETIME: //MYSQL_TYPE_DATETIME:
434  case FIELD_TYPE_YEAR: //MYSQL_TYPE_YEAR:
435  case FIELD_TYPE_NEWDATE: //MYSQL_TYPE_NEWDATE:
436  type = dt_date;
437  break;
438 // case MYSQL_TYPE_VARCHAR:
439  case FIELD_TYPE_VAR_STRING: //MYSQL_TYPE_VAR_STRING:
440  case FIELD_TYPE_STRING: //MYSQL_TYPE_STRING:
441  case FIELD_TYPE_BLOB: // TEXT OR BLOB
442  case FIELD_TYPE_TINY_BLOB:
443  case FIELD_TYPE_MEDIUM_BLOB:
444  case FIELD_TYPE_LONG_BLOB:
445  type = dt_string;
446  break;
447  default:
448  //std::cerr << "field->type: " << field->type << std::endl;
449  throw soci_error("Unknown data type.");
450  }
451  columnName = field->name;
452 }
453 
456 {
457  hasIntoElements_ = true;
458  return new mysql_standard_into_type_backend(*this);
459 }
460 
463 {
464  hasUseElements_ = true;
465  return new mysql_standard_use_type_backend(*this);
466 }
467 
470 {
471  hasVectorIntoElements_ = true;
472  return new mysql_vector_into_type_backend(*this);
473 }
474 
477 {
478  hasVectorUseElements_ = true;
479  return new mysql_vector_use_type_backend(*this);
480 }
virtual exec_fetch_result execute(int number)
virtual void prepare(std::string const &query, details::statement_type eType)
virtual mysql_vector_use_type_backend * make_vector_use_type_backend()
std::vector< MYSQL_ROW_OFFSET > resultRowOffsets_
Definition: soci-mysql.h:190
virtual std::string rewrite_for_procedure_call(std::string const &query)
mysql_session_backend & session_
Definition: soci-mysql.h:168
std::vector< std::string > names_
Definition: soci-mysql.h:177
virtual mysql_vector_into_type_backend * make_vector_into_type_backend()
virtual mysql_standard_use_type_backend * make_use_type_backend()
UseByNameBuffersMap useByNameBuffers_
Definition: soci-mysql.h:204
virtual mysql_standard_into_type_backend * make_into_type_backend()
std::vector< std::string > queryChunks_
Definition: soci-mysql.h:176
virtual void describe_column(int colNum, data_type &dtype, std::string &columnName)
mysql_statement_backend(mysql_session_backend &session)
virtual exec_fetch_result fetch(int number)
UseByPosBuffersMap useByPosBuffers_
Definition: soci-mysql.h:201


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