backends/odbc/statement.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
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_ODBC_SOURCE
9 #include "soci-odbc.h"
10 #include <cctype>
11 #include <sstream>
12 #include <cstring>
13 
14 #ifdef _MSC_VER
15 // disables the warning about converting int to void*. This is a 64 bit compatibility
16 // warning, but odbc requires the value to be converted on this line
17 // SQLSetStmtAttr(hstmt_, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)number, 0);
18 #pragma warning(disable:4312)
19 #endif
20 
21 using namespace soci;
22 using namespace soci::details;
23 
24 
26  : session_(session), hstmt_(0), numRowsFetched_(0),
27  hasVectorUseElements_(false), boundByName_(false), boundByPos_(false),
28  rowsAffected_(-1LL)
29 {
30 }
31 
33 {
34  SQLRETURN rc;
35 
36  // Allocate environment handle
37  rc = SQLAllocHandle(SQL_HANDLE_STMT, session_.hdbc_, &hstmt_);
38  if (is_odbc_error(rc))
39  {
40  throw odbc_soci_error(SQL_HANDLE_DBC, session_.hdbc_,
41  "Allocating statement");
42  }
43 }
44 
46 {
47  rowsAffected_ = -1LL;
48 
49  SQLFreeHandle(SQL_HANDLE_STMT, hstmt_);
50 }
51 
52 
53 void odbc_statement_backend::prepare(std::string const & query,
54  statement_type /* eType */)
55 {
56  // rewrite the query by transforming all named parameters into
57  // the ODBC numbers ones (:abc -> $1, etc.)
58 
59  enum { eNormal, eInQuotes, eInName, eInAccessDate } state = eNormal;
60 
61  std::string name;
62  query_.reserve(query.length());
63 
64  for (std::string::const_iterator it = query.begin(), end = query.end();
65  it != end; ++it)
66  {
67  switch (state)
68  {
69  case eNormal:
70  if (*it == '\'')
71  {
72  query_ += *it;
73  state = eInQuotes;
74  }
75  else if (*it == '#')
76  {
77  query_ += *it;
78  state = eInAccessDate;
79  }
80  else if (*it == ':')
81  {
82  state = eInName;
83  }
84  else // regular character, stay in the same state
85  {
86  query_ += *it;
87  }
88  break;
89  case eInQuotes:
90  if (*it == '\'')
91  {
92  query_ += *it;
93  state = eNormal;
94  }
95  else // regular quoted character
96  {
97  query_ += *it;
98  }
99  break;
100  case eInName:
101  if (std::isalnum(*it) || *it == '_')
102  {
103  name += *it;
104  }
105  else // end of name
106  {
107  names_.push_back(name);
108  name.clear();
109  query_ += "?";
110  query_ += *it;
111  state = eNormal;
112  }
113  break;
114  case eInAccessDate:
115  if (*it == '#')
116  {
117  query_ += *it;
118  state = eNormal;
119  }
120  else // regular quoted character
121  {
122  query_ += *it;
123  }
124  break;
125  }
126  }
127 
128  if (state == eInName)
129  {
130  names_.push_back(name);
131  query_ += "?";
132  }
133 
134  SQLRETURN rc = SQLPrepare(hstmt_, (SQLCHAR*)query_.c_str(), (SQLINTEGER)query_.size());
135  if (is_odbc_error(rc))
136  {
137  throw odbc_soci_error(SQL_HANDLE_STMT, hstmt_,
138  query_.c_str());
139  }
140 }
141 
144 {
145  // Store the number of rows processed by this call.
146  SQLULEN rows_processed = 0;
148  {
149  SQLSetStmtAttr(hstmt_, SQL_ATTR_PARAMS_PROCESSED_PTR, &rows_processed, 0);
150  }
151 
152  // if we are called twice for the same statement we need to close the open
153  // cursor or an "invalid cursor state" error will occur on execute
154  SQLCloseCursor(hstmt_);
155 
156  SQLRETURN rc = SQLExecute(hstmt_);
157  if (is_odbc_error(rc))
158  {
159  // If executing bulk operation a partial
160  // number of rows affected may be available.
162  {
163  rowsAffected_ = 0;
164 
165  do
166  {
167  SQLLEN res = 0;
168  // SQLRowCount will return error after a partially executed statement.
169  // SQL_DIAG_ROW_COUNT returns the same info but must be collected immediatelly after the execution.
170  rc = SQLGetDiagField(SQL_HANDLE_STMT, hstmt_, 0, SQL_DIAG_ROW_COUNT, &res, 0, NULL);
171  if (!is_odbc_error(rc) && res > 0) // 'res' will be -1 for the where the statement failed.
172  {
173  rowsAffected_ += res;
174  }
175  --rows_processed; // Avoid unnecessary calls to SQLGetDiagField
176  }
177  // Move forward to the next result while there are rows processed.
178  while (rows_processed > 0 && SQLMoreResults(hstmt_) == SQL_SUCCESS);
179  }
180  throw odbc_soci_error(SQL_HANDLE_STMT, hstmt_,
181  "Statement Execute");
182  }
183  // We should preserve the number of rows affected here
184  // where we know for sure that a bulk operation was executed.
185  else
186  {
187  rowsAffected_ = 0;
188 
189  do {
190  SQLLEN res = 0;
191  SQLRETURN rc = SQLRowCount(hstmt_, &res);
192  if (is_odbc_error(rc))
193  {
194  throw odbc_soci_error(SQL_HANDLE_STMT, hstmt_,
195  "Getting number of affected rows");
196  }
197  rowsAffected_ += res;
198  }
199  // Move forward to the next result if executing a bulk operation.
200  while (hasVectorUseElements_ && SQLMoreResults(hstmt_) == SQL_SUCCESS);
201  }
202  SQLSMALLINT colCount;
203  SQLNumResultCols(hstmt_, &colCount);
204 
205  if (number > 0 && colCount > 0)
206  {
207  return fetch(number);
208  }
209 
210  return ef_success;
211 }
212 
215 {
216  numRowsFetched_ = 0;
217  SQLULEN const row_array_size = static_cast<SQLULEN>(number);
218 
219  SQLSetStmtAttr(hstmt_, SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN, 0);
220  SQLSetStmtAttr(hstmt_, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)row_array_size, 0);
221  SQLSetStmtAttr(hstmt_, SQL_ATTR_ROWS_FETCHED_PTR, &numRowsFetched_, 0);
222 
223  SQLRETURN rc = SQLFetch(hstmt_);
224 
225  if (SQL_NO_DATA == rc)
226  {
227  return ef_no_data;
228  }
229 
230  if (is_odbc_error(rc))
231  {
232  throw odbc_soci_error(SQL_HANDLE_STMT, hstmt_,
233  "Statement Fetch");
234  }
235 
236  return ef_success;
237 }
238 
240 {
241  return rowsAffected_;
242 }
243 
245 {
246  return numRowsFetched_;
247 }
248 
250  std::string const &query)
251 {
252  return query;
253 }
254 
256 {
257  SQLSMALLINT numCols;
258  SQLNumResultCols(hstmt_, &numCols);
259  return numCols;
260 }
261 
263  std::string & columnName)
264 {
265  SQLCHAR colNameBuffer[2048];
266  SQLSMALLINT colNameBufferOverflow;
267  SQLSMALLINT dataType;
268  SQLULEN colSize;
269  SQLSMALLINT decDigits;
270  SQLSMALLINT isNullable;
271 
272  SQLRETURN rc = SQLDescribeCol(hstmt_, static_cast<SQLUSMALLINT>(colNum),
273  colNameBuffer, 2048,
274  &colNameBufferOverflow, &dataType,
275  &colSize, &decDigits, &isNullable);
276 
277  if (is_odbc_error(rc))
278  {
279  throw odbc_soci_error(SQL_HANDLE_STMT, hstmt_,
280  "describe Column");
281  }
282 
283  char const *name = reinterpret_cast<char const *>(colNameBuffer);
284  columnName.assign(name, std::strlen(name));
285 
286  switch (dataType)
287  {
288  case SQL_TYPE_DATE:
289  case SQL_TYPE_TIME:
290  case SQL_TYPE_TIMESTAMP:
291  type = dt_date;
292  break;
293  case SQL_DOUBLE:
294  case SQL_DECIMAL:
295  case SQL_REAL:
296  case SQL_FLOAT:
297  case SQL_NUMERIC:
298  type = dt_double;
299  break;
300  case SQL_TINYINT:
301  case SQL_SMALLINT:
302  case SQL_INTEGER:
303  type = dt_integer;
304  break;
305  case SQL_BIGINT:
306  type = dt_long_long;
307  break;
308  case SQL_CHAR:
309  case SQL_VARCHAR:
310  case SQL_LONGVARCHAR:
311  default:
312  type = dt_string;
313  break;
314  }
315 }
316 
317 std::size_t odbc_statement_backend::column_size(int colNum)
318 {
319  SQLCHAR colNameBuffer[2048];
320  SQLSMALLINT colNameBufferOverflow;
321  SQLSMALLINT dataType;
322  SQLULEN colSize;
323  SQLSMALLINT decDigits;
324  SQLSMALLINT isNullable;
325 
326  SQLRETURN rc = SQLDescribeCol(hstmt_, static_cast<SQLUSMALLINT>(colNum),
327  colNameBuffer, 2048,
328  &colNameBufferOverflow, &dataType,
329  &colSize, &decDigits, &isNullable);
330 
331  if (is_odbc_error(rc))
332  {
333  throw odbc_soci_error(SQL_HANDLE_STMT, hstmt_,
334  "column size");
335  }
336 
337  return colSize;
338 }
339 
341 {
342  return new odbc_standard_into_type_backend(*this);
343 }
344 
346 {
347  return new odbc_standard_use_type_backend(*this);
348 }
349 
352 {
353  return new odbc_vector_into_type_backend(*this);
354 }
355 
357 {
358  hasVectorUseElements_ = true;
359  return new odbc_vector_use_type_backend(*this);
360 }
virtual odbc_standard_into_type_backend * make_into_type_backend()
virtual exec_fetch_result fetch(int number)
std::size_t column_size(int position)
virtual odbc_standard_use_type_backend * make_use_type_backend()
virtual odbc_vector_use_type_backend * make_vector_use_type_backend()
bool is_odbc_error(SQLRETURN rc)
Definition: soci-odbc.h:399
odbc_session_backend & session_
Definition: soci-odbc.h:231
virtual void prepare(std::string const &query, details::statement_type eType)
std::vector< std::string > names_
Definition: soci-odbc.h:241
virtual void describe_column(int colNum, data_type &dtype, std::string &columnName)
virtual exec_fetch_result execute(int number)
virtual odbc_vector_into_type_backend * make_vector_into_type_backend()
virtual std::string rewrite_for_procedure_call(std::string const &query)
odbc_statement_backend(odbc_session_backend &session)


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