backends/firebird/statement.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski
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_FIREBIRD_SOURCE
9 #include "soci-firebird.h"
10 #include "error-firebird.h"
11 #include <cctype>
12 #include <sstream>
13 #include <iostream>
14 
15 using namespace soci;
16 using namespace soci::details;
17 using namespace soci::details::firebird;
18 
20  : session_(session), stmtp_(0), sqldap_(NULL), sqlda2p_(NULL),
21  boundByName_(false), boundByPos_(false), rowsFetched_(0), endOfRowSet_(false), rowsAffectedBulk_(-1LL),
22  intoType_(eStandard), useType_(eStandard), procedure_(false)
23 {}
24 
25 void firebird_statement_backend::prepareSQLDA(XSQLDA ** sqldap, int size)
26 {
27  if (*sqldap != NULL)
28  {
29  *sqldap = reinterpret_cast<XSQLDA*>(realloc(*sqldap, XSQLDA_LENGTH(size)));
30  }
31  else
32  {
33  *sqldap = reinterpret_cast<XSQLDA*>(malloc(XSQLDA_LENGTH(size)));
34  }
35 
36  (*sqldap)->sqln = size;
37  (*sqldap)->version = 1;
38 }
39 
41 {
42  ISC_STATUS stat[stat_size];
43 
44  if (isc_dsql_allocate_statement(stat, &session_.dbhp_, &stmtp_))
45  {
46  throw_iscerror(stat);
47  }
48 }
49 
51 {
52  rowsAffectedBulk_ = -1LL;
53 
54  ISC_STATUS stat[stat_size];
55 
56  if (stmtp_ != NULL)
57  {
58  if (isc_dsql_free_statement(stat, &stmtp_, DSQL_drop))
59  {
60  throw_iscerror(stat);
61  }
62  stmtp_ = NULL;
63  }
64 
65  if (sqldap_ != NULL)
66  {
67  free(sqldap_);
68  sqldap_ = NULL;
69  }
70 
71  if (sqlda2p_ != NULL)
72  {
73  free(sqlda2p_);
74  sqlda2p_ = NULL;
75  }
76 }
77 
79  std::string const & src, std::vector<char> & dst)
80 {
81  std::vector<char>::iterator dst_it = dst.begin();
82 
83  // rewrite the query by transforming all named parameters into
84  // the Firebird question marks (:abc -> ?, etc.)
85 
86  enum { eNormal, eInQuotes, eInName } state = eNormal;
87 
88  std::string name;
89  int position = 0;
90 
91  for (std::string::const_iterator it = src.begin(), end = src.end();
92  it != end; ++it)
93  {
94  switch (state)
95  {
96  case eNormal:
97  if (*it == '\'')
98  {
99  *dst_it++ = *it;
100  state = eInQuotes;
101  }
102  else if (*it == ':')
103  {
104  state = eInName;
105  }
106  else // regular character, stay in the same state
107  {
108  *dst_it++ = *it;
109  }
110  break;
111  case eInQuotes:
112  if (*it == '\'')
113  {
114  *dst_it++ = *it;
115  state = eNormal;
116  }
117  else // regular quoted character
118  {
119  *dst_it++ = *it;
120  }
121  break;
122  case eInName:
123  if (std::isalnum(*it) || *it == '_')
124  {
125  name += *it;
126  }
127  else // end of name
128  {
129  names_.insert(std::pair<std::string, int>(name, position++));
130  name.clear();
131  *dst_it++ = '?';
132  *dst_it++ = *it;
133  state = eNormal;
134  }
135  break;
136  }
137  }
138 
139  if (state == eInName)
140  {
141  names_.insert(std::pair<std::string, int>(name, position++));
142  *dst_it++ = '?';
143  }
144 
145  *dst_it = '\0';
146 }
147 
148 namespace
149 {
150  int statementType(isc_stmt_handle stmt)
151  {
152  int stype;
153  int length;
154  char type_item[] = {isc_info_sql_stmt_type};
155  char res_buffer[8];
156 
157  ISC_STATUS stat[stat_size];
158 
159  if (isc_dsql_sql_info(stat, &stmt, sizeof(type_item),
160  type_item, sizeof(res_buffer), res_buffer))
161  {
162  throw_iscerror(stat);
163  }
164 
165  if (res_buffer[0] == isc_info_sql_stmt_type)
166  {
167  length = isc_vax_integer(res_buffer+1, 2);
168  stype = isc_vax_integer(res_buffer+3, length);
169  }
170  else
171  {
172  throw soci_error("Can't determine statement type.");
173  }
174 
175  return stype;
176  }
177 }
178 
180  std::string const &query, std::vector<char> &buffer)
181 {
182  // buffer for temporary query
183  std::vector<char> tmpQuery;
184  std::vector<char>::iterator qItr;
185 
186  // buffer for query with named parameters changed to standard ones
187  std::vector<char> rewQuery(query.size() + 1);
188 
189  // take care of named parameters in original query
190  rewriteParameters(query, rewQuery);
191 
192  std::string const prefix("execute procedure ");
193  std::string const prefix2("select * from ");
194 
195  // for procedures, we are preparing statement to determine
196  // type of procedure.
197  if (procedure_)
198  {
199  tmpQuery.resize(prefix.size() + rewQuery.size());
200  qItr = tmpQuery.begin();
201  std::copy(prefix.begin(), prefix.end(), qItr);
202  qItr += prefix.size();
203  }
204  else
205  {
206  tmpQuery.resize(rewQuery.size());
207  qItr = tmpQuery.begin();
208  }
209 
210  // prepare temporary query
211  std::copy(rewQuery.begin(), rewQuery.end(), qItr);
212 
213  // preparing buffers for output parameters
214  if (sqldap_ == NULL)
215  {
217  }
218 
219  ISC_STATUS stat[stat_size];
220  isc_stmt_handle tmpStmtp = 0;
221 
222  // allocate temporary statement to determine its type
223  if (isc_dsql_allocate_statement(stat, &session_.dbhp_, &tmpStmtp))
224  {
225  throw_iscerror(stat);
226  }
227 
228  // prepare temporary statement
229  if (isc_dsql_prepare(stat, &(session_.trhp_), &tmpStmtp, 0,
230  &tmpQuery[0], SQL_DIALECT_V6, sqldap_))
231  {
232  throw_iscerror(stat);
233  }
234 
235  // get statement type
236  int stType = statementType(tmpStmtp);
237 
238  // free temporary prepared statement
239  if (isc_dsql_free_statement(stat, &tmpStmtp, DSQL_drop))
240  {
241  throw_iscerror(stat);
242  }
243 
244  // take care of special cases
245  if (procedure_)
246  {
247  // for procedures that return values, we need to use correct syntax
248  if (sqldap_->sqld != 0)
249  {
250  // this is "select" procedure, so we have to change syntax
251  buffer.resize(prefix2.size() + rewQuery.size());
252  qItr = buffer.begin();
253  std::copy(prefix2.begin(), prefix2.end(), qItr);
254  qItr += prefix2.size();
255  std::copy(rewQuery.begin(), rewQuery.end(), qItr);
256 
257  // that won't be needed anymore
258  procedure_ = false;
259 
260  return;
261  }
262  }
263  else
264  {
265  // this is not procedure, so syntax is ok except for named
266  // parameters in ddl
267  if (stType == isc_info_sql_stmt_ddl)
268  {
269  // this statement is a DDL - we can't rewrite named parameters
270  // so, we will use original query
271  buffer.resize(query.size() + 1);
272  std::copy(query.begin(), query.end(), buffer.begin());
273 
274  // that won't be needed anymore
275  procedure_ = false;
276 
277  return;
278  }
279  }
280 
281  // here we know, that temporary query is OK, so we leave it as is
282  buffer.resize(tmpQuery.size());
283  std::copy(tmpQuery.begin(), tmpQuery.end(), buffer.begin());
284 
285  // that won't be needed anymore
286  procedure_ = false;
287 }
288 
289 void firebird_statement_backend::prepare(std::string const & query,
290  statement_type /* eType */)
291 {
292  //std::cerr << "prepare: query=" << query << std::endl;
293  // clear named parametes
294  names_.clear();
295 
296  std::vector<char> queryBuffer;
297 
298  // modify query's syntax and prepare buffer for use with
299  // firebird's api
300  rewriteQuery(query, queryBuffer);
301 
302  ISC_STATUS stat[stat_size];
303 
304  // prepare real statement
305  if (isc_dsql_prepare(stat, &(session_.trhp_), &stmtp_, 0,
306  &queryBuffer[0], SQL_DIALECT_V6, sqldap_))
307  {
308  throw_iscerror(stat);
309  }
310 
311  if (sqldap_->sqln < sqldap_->sqld)
312  {
313  // sqlda is too small for all columns. it must be reallocated
314  prepareSQLDA(&sqldap_, sqldap_->sqld);
315 
316  if (isc_dsql_describe(stat, &stmtp_, SQL_DIALECT_V6, sqldap_))
317  {
318  throw_iscerror(stat);
319  }
320  }
321 
322  // preparing input parameters
323  if (sqlda2p_ == NULL)
324  {
326  }
327 
328  if (isc_dsql_describe_bind(stat, &stmtp_, SQL_DIALECT_V6, sqlda2p_))
329  {
330  throw_iscerror(stat);
331  }
332 
333  if (sqlda2p_->sqln < sqlda2p_->sqld)
334  {
335  // sqlda is too small for all columns. it must be reallocated
336  prepareSQLDA(&sqlda2p_, sqlda2p_->sqld);
337 
338  if (isc_dsql_describe_bind(stat, &stmtp_, SQL_DIALECT_V6, sqlda2p_))
339  {
340  throw_iscerror(stat);
341  }
342  }
343 
344  // prepare buffers for indicators
345  inds_.clear();
346  inds_.resize(sqldap_->sqld);
347 
348  // reset types of into buffers
350  intos_.resize(0);
351 
352  // reset types of use buffers
354  uses_.resize(0);
355 }
356 
357 
358 namespace
359 {
360  void checkSize(std::size_t actual, std::size_t expected,
361  std::string const & name)
362  {
363  if (actual != expected)
364  {
365  std::ostringstream msg;
366  msg << "Incorrect number of " << name << " variables. "
367  << "Expected " << expected << ", got " << actual;
368  throw soci_error(msg.str());
369  }
370  }
371 }
372 
375 {
376  ISC_STATUS stat[stat_size];
377  XSQLDA *t = NULL;
378 
379  std::size_t usize = uses_.size();
380 
381  // do we have enough into variables ?
382  checkSize(intos_.size(), sqldap_->sqld, "into");
383  // do we have enough use variables ?
384  checkSize(usize, sqlda2p_->sqld, "use");
385 
386  // do we have parameters ?
387  if (sqlda2p_->sqld)
388  {
389  t = sqlda2p_;
390 
391  if (useType_ == eStandard)
392  {
393  for (std::size_t col=0; col<usize; ++col)
394  {
396  }
397  }
398  }
399 
400  // make sure there is no active cursor
401  if (isc_dsql_free_statement(stat, &stmtp_, DSQL_close))
402  {
403  // ignore attempt to close already closed cursor
404  if (check_iscerror(stat, isc_dsql_cursor_close_err) == false)
405  {
406  throw_iscerror(stat);
407  }
408  }
409 
410  if (useType_ == eVector)
411  {
412  long long rowsAffectedBulkTemp = 0;
413 
414  // Here we have to explicitly loop to achieve the
415  // effect of inserting or updating with vector use elements.
416  std::size_t rows = static_cast<firebird_vector_use_type_backend*>(uses_[0])->size();
417  for (std::size_t row=0; row < rows; ++row)
418  {
419  // first we have to prepare input parameters
420  for (std::size_t col=0; col<usize; ++col)
421  {
423  }
424 
425  // then execute query
426  if (isc_dsql_execute(stat, &session_.trhp_, &stmtp_, SQL_DIALECT_V6, t))
427  {
428  // preserve the number of rows affected so far.
429  rowsAffectedBulk_ = rowsAffectedBulkTemp;
430  throw_iscerror(stat);
431  }
432  else
433  {
434  rowsAffectedBulkTemp += get_affected_rows();
435  }
436  // soci does not allow bulk insert/update and bulk select operations
437  // in same query. So here, we know that into elements are not
438  // vectors. So, there is no need to fetch data here.
439  }
440  rowsAffectedBulk_ = rowsAffectedBulkTemp;
441  }
442  else
443  {
444  // use elements aren't vectors
445  if (isc_dsql_execute(stat, &session_.trhp_, &stmtp_, SQL_DIALECT_V6, t))
446  {
447  throw_iscerror(stat);
448  }
449  }
450 
451  // Successfully re-executing the statement must reset the "end of rowset"
452  // flag, we might be able to fetch data again now.
453  endOfRowSet_ = false;
454 
455  if (sqldap_->sqld)
456  {
457  // query may return some data
458  if (number > 0)
459  {
460  // number contains size of input variables, so we may fetch() data here
461  return fetch(number);
462  }
463  else
464  {
465  // execute(0) was meant to only perform the query
466  return ef_success;
467  }
468  }
469  else
470  {
471  // query can't return any data
472  return ef_no_data;
473  }
474 }
475 
478 {
479  if (endOfRowSet_)
480  return ef_no_data;
481 
482  ISC_STATUS stat[stat_size];
483 
484  for (size_t i = 0; i<static_cast<unsigned int>(sqldap_->sqld); ++i)
485  {
486  inds_[i].resize(number > 0 ? number : 1);
487  }
488 
489  // Here we have to explicitly loop to achieve the effect of fetching
490  // vector into elements. After each fetch, we have to exchange data
491  // with into buffers.
492  rowsFetched_ = 0;
493  for (int i = 0; i < number; ++i)
494  {
495  long fetch_stat = isc_dsql_fetch(stat, &stmtp_, SQL_DIALECT_V6, sqldap_);
496 
497  // there is more data to read
498  if (fetch_stat == 0)
499  {
500  ++rowsFetched_;
501  exchangeData(true, i);
502  }
503  else if (fetch_stat == 100L)
504  {
505  endOfRowSet_ = true;
506  return ef_no_data;
507  }
508  else
509  {
510  // error
511  endOfRowSet_ = true;
512  throw_iscerror(stat);
513  return ef_no_data; // unreachable, for compiler only
514  }
515  } // for
516 
517  return ef_success;
518 }
519 
520 // here we put data fetched from database into user buffers
522 {
523  if (gotData)
524  {
525  for (size_t i = 0; i < static_cast<unsigned int>(sqldap_->sqld); ++i)
526  {
527  // first save indicators
528  if (((sqldap_->sqlvar+i)->sqltype & 1) == 0)
529  {
530  // there is no indicator for this column
531  inds_[i][row] = i_ok;
532  }
533  else if (*((sqldap_->sqlvar+i)->sqlind) == 0)
534  {
535  inds_[i][row] = i_ok;
536  }
537  else if (*((sqldap_->sqlvar+i)->sqlind) == -1)
538  {
539  inds_[i][row] = i_null;
540  }
541  else
542  {
543  throw soci_error("Unknown state in firebird_statement_backend::exchangeData()");
544  }
545 
546  // then deal with data
547  if (inds_[i][row] != i_null)
548  {
549  if (intoType_ == eVector)
550  {
551  static_cast<firebird_vector_into_type_backend*>(
552  intos_[i])->exchangeData(row);
553  }
554  else
555  {
557  intos_[i])->exchangeData();
558  }
559  }
560  }
561  }
562 }
563 
565 {
566  if (rowsAffectedBulk_ >= 0)
567  {
568  return rowsAffectedBulk_;
569  }
570 
571  ISC_STATUS_ARRAY stat;
572  char type_item[] = { isc_info_sql_records };
573  char res_buffer[256];
574 
575  if (isc_dsql_sql_info(stat, &stmtp_, sizeof(type_item), type_item,
576  sizeof(res_buffer), res_buffer))
577  {
578  throw_iscerror(stat);
579  }
580 
581  // We must get back a isc_info_sql_records block, that we parse below,
582  // followed by isc_info_end.
583  if (res_buffer[0] != isc_info_sql_records)
584  {
585  throw soci_error("Can't determine the number of affected rows");
586  }
587 
588  char* sql_rec_buf = res_buffer + 1;
589  const int length = isc_vax_integer(sql_rec_buf, 2);
590  sql_rec_buf += 2;
591 
592  if (sql_rec_buf[length] != isc_info_end)
593  {
594  throw soci_error("Unexpected isc_info_sql_records return format");
595  }
596 
597  // Examine the 4 sub-blocks each of which has a header indicating the block
598  // type, its value length in bytes and the value itself.
599  long long row_count = 0;
600 
601  for ( char* p = sql_rec_buf; !row_count && p < sql_rec_buf + length; )
602  {
603  switch (*p++)
604  {
605  case isc_info_req_select_count:
606  case isc_info_req_insert_count:
607  case isc_info_req_update_count:
608  case isc_info_req_delete_count:
609  {
610  int len = isc_vax_integer(p, 2);
611  p += 2;
612 
613  row_count += isc_vax_integer(p, len);
614  p += len;
615  }
616  break;
617 
618  case isc_info_end:
619  break;
620 
621  default:
622  throw soci_error("Unknown record counter");
623  }
624  }
625 
626  return row_count;
627 }
628 
630 {
631  return rowsFetched_;
632 }
633 
635  std::string const &query)
636 {
637  procedure_ = true;
638  return query;
639 }
640 
642 {
643  return static_cast<int>(sqldap_->sqld);
644 }
645 
647  data_type & type, std::string & columnName)
648 {
649  XSQLVAR * var = sqldap_->sqlvar+(colNum-1);
650 
651  columnName.assign(var->aliasname, var->aliasname_length);
652 
653  switch (var->sqltype & ~1)
654  {
655  case SQL_TEXT:
656  case SQL_VARYING:
657  type = dt_string;
658  break;
659  case SQL_TYPE_DATE:
660  case SQL_TYPE_TIME:
661  case SQL_TIMESTAMP:
662  type = dt_date;
663  break;
664  case SQL_FLOAT:
665  case SQL_DOUBLE:
666  type = dt_double;
667  break;
668  case SQL_SHORT:
669  case SQL_LONG:
670  if (var->sqlscale < 0)
671  {
673  type = dt_string;
674  else
675  type = dt_double;
676  }
677  else
678  {
679  type = dt_integer;
680  }
681  break;
682  case SQL_INT64:
683  if (var->sqlscale < 0)
684  {
686  type = dt_string;
687  else
688  type = dt_double;
689  }
690  else
691  {
692  type = dt_long_long;
693  }
694  break;
695  /* case SQL_BLOB:
696  case SQL_ARRAY:*/
697  default:
698  std::ostringstream msg;
699  msg << "Type of column ["<< colNum << "] \"" << columnName
700  << "\" is not supported for dynamic queries";
701  throw soci_error(msg.str());
702  break;
703  }
704 }
705 
707 {
708  return new firebird_standard_into_type_backend(*this);
709 }
710 
712 {
713  return new firebird_standard_use_type_backend(*this);
714 }
715 
717 {
718  return new firebird_vector_into_type_backend(*this);
719 }
720 
722 {
723  return new firebird_vector_use_type_backend(*this);
724 }
std::vector< void * > uses_
virtual void prepare(std::string const &query, details::statement_type eType)
virtual void exchangeData(bool gotData, int row)
virtual void describe_column(int colNum, data_type &dtype, std::string &columnName)
firebird_statement_backend(firebird_session_backend &session)
Definition: row.h:41
virtual firebird_vector_into_type_backend * make_vector_into_type_backend()
bool check_iscerror(ISC_STATUS const *status_vector, long errNum)
virtual exec_fetch_result execute(int number)
std::vector< void * > intos_
friend struct firebird_vector_use_type_backend
std::size_t const stat_size
Definition: soci-firebird.h:40
virtual std::string rewrite_for_procedure_call(std::string const &query)
virtual firebird_vector_use_type_backend * make_vector_use_type_backend()
virtual void prepareSQLDA(XSQLDA **sqldap, int size=10)
friend struct firebird_standard_into_type_backend
void throw_iscerror(ISC_STATUS *status_vector)
firebird_session_backend & session_
virtual void rewriteParameters(std::string const &src, std::vector< char > &dst)
std::map< std::string, int > names_
virtual void rewriteQuery(std::string const &query, std::vector< char > &buffer)
virtual firebird_standard_into_type_backend * make_into_type_backend()
friend struct firebird_vector_into_type_backend
virtual firebird_standard_use_type_backend * make_use_type_backend()
friend struct firebird_standard_use_type_backend
virtual exec_fetch_result fetch(int number)
std::vector< std::vector< indicator > > inds_


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