db_conn  v0.2.1-alpha
Database Connection API
sybase_driver.hpp
1 /*
2  * File: sybase_driver.hpp
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #ifndef SYBASE_DRIVER_HPP
20 #define SYBASE_DRIVER_HPP
21 
22 #include <ctpublic.h>
23 #include <cstring>
24 #include <vector>
25 #include <map>
26 #include <functional>
27 #include <algorithm>
28 #include <type_traits>
29 #include "driver.hpp"
30 
31 namespace vgi { namespace dbconn { namespace dbd { namespace sybase {
32 
33 static auto TRUE = CS_TRUE;
34 static auto FALSE = CS_FALSE;
35 
36 using Locale = CS_LOCALE;
37 using Context = CS_CONTEXT;
38 using Connection = CS_CONNECTION;
39 using ServerMessage = CS_SERVERMSG;
40 using ClientMessage = CS_CLIENTMSG;
41 
42 enum class cfg_type : char
43 {
44  CT_LIB,
45  CS_LIB
46 };
47 
48 enum class action : CS_INT
49 {
50  SET = CS_SET,
51  GET = CS_GET,
52  CLEAR = CS_CLEAR,
53  SUPPORTED = CS_SUPPORTED // only used for connection properties
54 };
55 
56 enum class debug_flag : CS_INT
57 {
58  ASYNC = CS_DBG_ASYNC,
59  ERROR = CS_DBG_ERROR,
60  MEM = CS_DBG_MEM,
61  API_STATES = CS_DBG_API_STATES,
62  NETWORK = CS_DBG_NETWORK,
63  API_LOGCALL = CS_DBG_API_LOGCALL,
64  ALL = CS_DBG_ALL,
65  PROTOCOL = CS_DBG_PROTOCOL,
66  PROTOCOL_STATES = CS_DBG_PROTOCOL_STATES,
67  DIAG = CS_DBG_DIAG
68 };
69 
70 constexpr debug_flag operator|(debug_flag l, debug_flag r) { return debug_flag(utils::base_type(l) | utils::base_type(r)); }
71 
72 
73 
74 // forward declaration
75 class driver;
76 class statement;
77 class connection;
78 
79 
80 
81 //=====================================================================================
82 
83 
93 {
94 private:
95  struct column_data
96  {
97  CS_INT length = 0;
98  CS_SMALLINT indicator = 0;
99  std::vector<CS_CHAR> data;
100 
101  void allocate(const size_t size)
102  {
103  data.resize(size, '\0');
104  }
105 
106  operator char*()
107  {
108  return data.data();
109  }
110  };
111 
112 public:
113  void clear()
114  {
115  columndata.clear();
116  name2index.clear();
117  row_cnt = 0;
118  affected_rows = 0;
119  more_res = false;
120  }
121 
122  virtual bool has_data()
123  {
124  return (columns.size() > 0);
125  }
126 
127  virtual bool more_results()
128  {
129  return more_res;
130  }
131 
132  virtual size_t row_count() const
133  {
134  return std::abs(row_cnt);
135  }
136 
137  virtual size_t rows_affected() const
138  {
139  return affected_rows;
140  }
141 
142  virtual size_t column_count() const
143  {
144  return columns.size();
145  }
146 
147  virtual std::string column_name(size_t col_idx)
148  {
149  if (col_idx >= columns.size())
150  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column index"));
151  return columns[col_idx].name;
152  }
153 
154  virtual int column_index(const std::string& col_name)
155  {
156  auto it = name2index.find(col_name);
157  if (it != name2index.end())
158  return it->second;
159  return -1;
160  }
161 
162  virtual bool prev()
163  {
164  return scroll_fetch(CS_PREV);
165  }
166 
167  virtual bool first()
168  {
169  return scroll_fetch(CS_FIRST);
170  }
171 
172  virtual bool last()
173  {
174  return scroll_fetch(CS_LAST);
175  }
176 
177  virtual bool next()
178  {
179  if (columndata.size() > 0)
180  {
181  if (scrollable)
182  {
183  return scroll_fetch(CS_NEXT);
184  }
185  else
186  {
187  if ((CS_SUCCEED == (retcode = ct_fetch(cscommand, CS_UNUSED, CS_UNUSED, CS_UNUSED, &result))) || CS_ROW_FAIL == retcode)
188  {
189  if (CS_ROW_FAIL == retcode)
190  throw std::runtime_error(std::string(__FUNCTION__).append(": Error fetching row ").append(std::to_string(result)));
191  row_cnt += result;
192  return true;
193  }
194  else
195  next_result();
196  }
197  }
198  return false;
199  }
200 
201  virtual bool is_null(size_t col_idx)
202  {
203  if (col_idx >= columns.size())
204  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column index"));
205  return (CS_NULLDATA == columndata[col_idx].indicator);
206  }
207 
208  virtual int16_t get_short(size_t col_idx)
209  {
210  if (CS_TINYINT_TYPE == columns[col_idx].datatype)
211  return get<CS_TINYINT>(col_idx);
212  if (CS_SMALLINT_TYPE == columns[col_idx].datatype)
213  return get<CS_SMALLINT>(col_idx);
214  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column data type (supported: tinyint, smallint)"));
215  }
216 
217  virtual uint16_t get_ushort(size_t col_idx)
218  {
219  switch (columns[col_idx].datatype)
220  {
221  case CS_TINYINT_TYPE:
222  return get<CS_TINYINT>(col_idx);
223  case CS_USHORT_TYPE:
224  return get<CS_USHORT>(col_idx);
225 #ifdef CS_USMALLINT_TYPE
226  case CS_USMALLINT_TYPE:
227  return get<CS_USMALLINT>(col_idx);
228 #endif
229  default:
230  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column data type (supported: tinyint, usmallint)"));
231  }
232  }
233 
234  virtual int32_t get_int(size_t col_idx)
235  {
236  switch (columns[col_idx].datatype)
237  {
238  case CS_TINYINT_TYPE:
239  return get<CS_TINYINT>(col_idx);
240  case CS_SMALLINT_TYPE:
241  return get<CS_SMALLINT>(col_idx);
242 #ifdef CS_USMALLINT_TYPE
243  case CS_USMALLINT_TYPE:
244  return get<CS_USMALLINT>(col_idx);
245 #endif
246  case CS_INT_TYPE:
247  return get<CS_INT>(col_idx);
248  default:
249  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column data type (supported: tinyint, smallint, usmallint, int)"));
250  }
251  }
252 
253  virtual uint32_t get_uint(size_t col_idx)
254  {
255  switch (columns[col_idx].datatype)
256  {
257  case CS_TINYINT_TYPE:
258  return get<CS_TINYINT>(col_idx);
259 #ifdef CS_USMALLINT_TYPE
260  case CS_USMALLINT_TYPE:
261  return get<CS_USMALLINT>(col_idx);
262 #endif
263  case CS_USHORT_TYPE:
264  return get<CS_USHORT>(col_idx);
265 #ifdef CS_UINT_TYPE
266  case CS_UINT_TYPE:
267  return get<CS_UINT>(col_idx);
268 #endif
269  default:
270  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column data type (supported: tinyint, usmallint, uint)"));
271  }
272  }
273 
274  virtual int64_t get_long(size_t col_idx)
275  {
276  switch (columns[col_idx].datatype)
277  {
278  case CS_TINYINT_TYPE:
279  return get<CS_TINYINT>(col_idx);
280  case CS_SMALLINT_TYPE:
281  return get<CS_SMALLINT>(col_idx);
282 #ifdef CS_USMALLINT_TYPE
283  if (CS_USMALLINT_TYPE == columns[col_idx].datatype)
284  return get<CS_USMALLINT>(col_idx);
285 #endif
286  case CS_INT_TYPE:
287  return get<CS_INT>(col_idx);
288 #ifdef CS_UINT_TYPE
289  case CS_UINT_TYPE:
290  return get<CS_UINT>(col_idx);
291 #endif
292  case CS_LONG_TYPE:
293  return get<CS_LONG>(col_idx);
294 #ifdef CS_BIGINT_TYPE
295  case CS_BIGINT_TYPE:
296  return get<CS_BIGINT>(col_idx);
297 #endif
298  case CS_DECIMAL_TYPE:
299  case CS_NUMERIC_TYPE:
300  return get<int64_t>(col_idx);
301  default:
302  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column data type (supported: tinyint, smallint, usmallint, int, uint, bigint)"));
303  }
304  }
305 
306  virtual uint64_t get_ulong(size_t col_idx)
307  {
308  switch (columns[col_idx].datatype)
309  {
310  case CS_TINYINT_TYPE:
311  return get<CS_TINYINT>(col_idx);
312 #ifdef CS_USMALLINT_TYPE
313  case CS_USMALLINT_TYPE:
314  return get<CS_USMALLINT>(col_idx);
315 #endif
316 #ifdef CS_UINT_TYPE
317  case CS_UINT_TYPE:
318  return get<CS_UINT>(col_idx);
319 #endif
320 #ifdef CS_UBIGINT_TYPE
321  case CS_UBIGINT_TYPE:
322  return get<CS_UBIGINT>(col_idx);
323 #endif
324  case CS_DECIMAL_TYPE:
325  case CS_NUMERIC_TYPE:
326  return get<uint64_t>(col_idx);
327  default:
328  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column data type (supported: tinyint, usmallint, uint, ubigint)"));
329  }
330  }
331 
332  virtual float get_float(size_t col_idx)
333  {
334  if (CS_REAL_TYPE == columns[col_idx].datatype)
335  return get<CS_REAL>(col_idx);
336  if (CS_FLOAT_TYPE == columns[col_idx].datatype && columndata[col_idx].length <= 4)
337  return get<CS_FLOAT>(col_idx);
338  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column data type (supported: real, float(p) if p < 16)"));
339  }
340 
341  virtual double get_double(size_t col_idx)
342  {
343  switch (columns[col_idx].datatype)
344  {
345  case CS_REAL_TYPE:
346  return get<CS_REAL>(col_idx);
347  case CS_FLOAT_TYPE:
348  return get<CS_FLOAT>(col_idx);
349  case CS_MONEY_TYPE:
350  case CS_MONEY4_TYPE:
351  case CS_DECIMAL_TYPE:
352  case CS_NUMERIC_TYPE:
353  return get<double>(col_idx);
354  default:
355  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column data type (supported: real, float, numeric/decimal)"));
356  }
357  }
358 
359  virtual bool get_bool(size_t col_idx)
360  {
361  if (CS_BIT_TYPE != columns[col_idx].datatype)
362  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column data type (supported: bit)"));
363  return get<bool>(col_idx);
364  }
365 
366  virtual char get_char(size_t col_idx)
367  {
368  switch (columns[col_idx].datatype)
369  {
370  case CS_CHAR_TYPE:
371  case CS_LONGCHAR_TYPE:
372  case CS_TEXT_TYPE:
373  case CS_VARCHAR_TYPE:
374  case CS_BOUNDARY_TYPE:
375  case CS_SENSITIVITY_TYPE:
376 #ifdef CS_XML_TYPE
377  case CS_XML_TYPE:
378 #endif
379  break;
380  default:
381  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column data type (supported: char, varchar, text)"));
382  }
383  return get<char>(col_idx);
384  }
385 
386  virtual std::string get_string(size_t col_idx)
387  {
388  switch (columns[col_idx].datatype)
389  {
390  case CS_CHAR_TYPE:
391  case CS_LONGCHAR_TYPE:
392  case CS_TEXT_TYPE:
393  case CS_VARCHAR_TYPE:
394  case CS_BOUNDARY_TYPE:
395  case CS_SENSITIVITY_TYPE:
396 #ifdef CS_XML_TYPE
397  case CS_XML_TYPE:
398 #endif
399  break;
400  default:
401  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column data type (supported: char, varchar, text)"));
402  }
403  return std::move(std::string(columndata[col_idx]));
404  }
405 
406  virtual int get_date(size_t col_idx)
407  {
408 #ifdef CS_TIME_TYPE
409  if (CS_TIME_TYPE == columns[col_idx].datatype)
410  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column data type (supported: date, datetime, smalldatetime, bigdatetime)"));
411 #endif
412 #ifdef CS_BIGTIME_TYPE
413  if (CS_BIGTIME_TYPE == columns[col_idx].datatype)
414  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column data type (supported: date, datetime, smalldatetime, bigdatetime)"));
415 #endif
416  getdt(col_idx);
417  return daterec.dateyear * 10000 + (daterec.datemonth + 1) * 100 + daterec.datedmonth;
418  }
419 
420  virtual double get_time(size_t col_idx)
421  {
422 #ifdef CS_DATE_TYPE
423  if (CS_DATE_TYPE == columns[col_idx].datatype)
424  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column data type (supported: time, datetime, smalldatetime, bigdatetime)"));
425 #endif
426  getdt(col_idx);
427  return (double)(daterec.datehour * 10000 + daterec.dateminute * 100 + daterec.datesecond) + (double)daterec.datemsecond / 1000.0;
428  }
429 
430  virtual time_t get_datetime(size_t col_idx)
431  {
432 #ifdef CS_TIME_TYPE
433  if (CS_TIME_TYPE == columns[col_idx].datatype)
434  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column data type (supported: date, datetime, smalldatetime, bigdatetime)"));
435 #endif
436  getdt(col_idx);
437  std::memset(&stm, 0, sizeof(stm));
438  stm.tm_sec = daterec.datesecond;
439  stm.tm_min = daterec.dateminute;
440  stm.tm_hour = daterec.datehour;
441  stm.tm_mon = daterec.datemonth;
442  stm.tm_mday = daterec.datedmonth;
443  stm.tm_year = daterec.dateyear - 1900;
444  stm.tm_isdst = -1;
445  return mktime(&stm);
446  }
447 
448  virtual char16_t get_u16char(size_t col_idx)
449  {
450  switch (columns[col_idx].datatype)
451  {
452  case CS_UNICHAR_TYPE:
453 #ifdef CS_UNITEXT_TYPE
454  case CS_UNITEXT_TYPE:
455 #endif
456  break;
457  default:
458  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column data type: ").append(std::to_string(columns[col_idx].datatype)));
459  }
460  return get<CS_UNICHAR>(col_idx);
461  }
462 
463  virtual std::u16string get_u16string(size_t col_idx)
464  {
465  switch (columns[col_idx].datatype)
466  {
467  case CS_UNICHAR_TYPE:
468 #ifdef CS_UNITEXT_TYPE
469  case CS_UNITEXT_TYPE:
470 #endif
471  break;
472  default:
473  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column data type: ").append(std::to_string(columns[col_idx].datatype)));
474  }
475  return std::u16string(reinterpret_cast<char16_t*>((char*)columndata[col_idx]));
476  }
477 
478  virtual std::vector<uint8_t> get_binary(size_t col_idx)
479  {
480  switch (columns[col_idx].datatype)
481  {
482  case CS_IMAGE_TYPE:
483  case CS_BINARY_TYPE:
484  case CS_VARBINARY_TYPE:
485  case CS_LONGBINARY_TYPE:
486 #ifdef CS_BLOB_TYPE
487  case CS_BLOB_TYPE:
488 #endif
489  break;
490  default:
491  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column data type: ").append(std::to_string(columns[col_idx].datatype)));
492  }
493  std::vector<uint8_t> t(columndata[col_idx].length);
494  std::memcpy(reinterpret_cast<void*>(t.data()), columndata[col_idx], columndata[col_idx].length);
495  return std::move(t);
496  }
497 
498  bool cancel()
499  {
500  if (CS_SUCCEED != ct_cancel(nullptr, cscommand, CS_CANCEL_ALL))
501  return false;
502  return true;
503  }
504 
505 private:
506  friend class statement;
507 
508  result_set() {}
509  result_set(const result_set&) = delete;
510  result_set& operator=(const result_set&) = delete;
511 
512  void set_scrollable(bool scroll)
513  {
514  scrollable = scroll;
515  }
516 
517  bool next_result()
518  {
519  CS_INT res;
520  auto done = false;
521  auto failed_cnt = 0;
522  clear();
523  do_cancel = true;
524  while (false == done)
525  {
526  retcode = ct_results(cscommand, &res);
527  switch (retcode)
528  {
529  case CS_SUCCEED:
530  done = process_ct_result(res);
531  if (true == done)
532  {
533  if (true == has_data())
534  more_res = true;
535  else
536  done = false;
537  }
538  else if (CS_CMD_FAIL == res)
539  failed_cnt += 1;
540  break;
541  case CS_END_RESULTS:
542  case CS_CANCELED:
543  done = true;
544  break;
545  case CS_FAIL:
546  cancel();
547  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to get results"));
548  default:
549  cancel();
550  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed ct_results returned unknown ret_code"));
551  }
552  }
553  if (failed_cnt > 0)
554  {
555  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to execute ").append(std::to_string(failed_cnt)).append(" command(s)"));
556  }
557  return more_res;
558  }
559 
560  bool process_ct_result(CS_INT res)
561  {
562  switch (res)
563  {
564  case CS_PARAM_RESULT:
565  case CS_STATUS_RESULT:
566  case CS_ROW_RESULT:
567  case CS_CURSOR_RESULT:
568  process_result(false, true);
569  return true;
570  case CS_COMPUTE_RESULT:
571  process_result(true, true);
572  return true;
573  case CS_DESCRIBE_RESULT:
574  process_result(false, false);
575  break;
576  case CS_CMD_DONE:
577  if (CS_SUCCEED != ct_res_info(cscommand, CS_ROW_COUNT, &res, CS_UNUSED, nullptr))
578  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to get result row count"));
579  affected_rows += res > 0 ? res : 0;
580  break;
581  case CS_ROWFMT_RESULT: // not supported. seen only when the CS_EXPOSE_FORMATS property is enabled
582  case CS_COMPUTEFMT_RESULT:// not supported. seen only when the CS_EXPOSE_FORMATS property is enabled
583  case CS_MSG_RESULT: // not supported
584  break;
585  case CS_CMD_SUCCEED:
586  break;
587  case CS_CMD_FAIL:
588  break;
589  default:
590  throw std::runtime_error(std::string(__FUNCTION__).append(": Unknown return from ct_results: ").append(std::to_string(res)));
591  }
592  return false;
593  }
594 
595  void process_result(bool compute, bool bind)
596  {
597  auto colcnt = 0;
598  if (CS_SUCCEED != ct_res_info(cscommand, CS_NUMDATA, &colcnt, CS_UNUSED, nullptr))
599  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to get number of columns"));
600  if (colcnt <= 0)
601  throw std::runtime_error(std::string(__FUNCTION__).append(": Returned zero columns"));
602  std::vector<std::string> colnames;
603  if (compute)
604  {
605  for (auto& dfmt : columns)
606  colnames.push_back(dfmt.name);
607  }
608  columns.resize(colcnt);
609  columndata.resize(colcnt);
610  auto agg_op = 0;
611  auto col_id = 0;
612  for (auto i = 0; i < colcnt; ++i)
613  {
614  std::memset(&columns[i], 0, sizeof(CS_DATAFMT));
615  if (CS_SUCCEED != ct_describe(cscommand, i + 1, &(columns[i])))
616  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to get column description: index ").append(std::to_string(i)));
617  if (compute)
618  {
619  if (CS_SUCCEED != ct_compute_info(cscommand, CS_COMP_OP, i + 1, &agg_op, CS_UNUSED, &(columns[i].namelen)))
620  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed compute info call for operation"));
621  if (CS_SUCCEED != ct_compute_info(cscommand, CS_COMP_COLID, i + 1, &col_id, CS_UNUSED, &(columns[i].namelen)))
622  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed compute info call for column id"));
623  col_id -= 1;
624  switch (agg_op)
625  {
626  case CS_OP_SUM: std::sprintf(columns[i].name, "sum(%s)", colnames[col_id].c_str()); break;
627  case CS_OP_AVG: std::sprintf(columns[i].name, "avg(%s)", colnames[col_id].c_str()); break;
628  case CS_OP_COUNT: std::sprintf(columns[i].name, "count(%s)", colnames[col_id].c_str()); break;
629  case CS_OP_MIN: std::sprintf(columns[i].name, "min(%s)", colnames[col_id].c_str()); break;
630  case CS_OP_MAX: std::sprintf(columns[i].name, "max(%s)", colnames[col_id].c_str()); break;
631  default: std::sprintf(columns[i].name, "unknown(%s)", colnames[col_id].c_str()); break;
632  }
633  }
634  else if (::strlen(columns[i].name) == 0)
635  std::sprintf(columns[i].name, "column%d", i + 1);
636  name2index[columns[i].name] = i;
637  columndata[i].allocate(columns[i].maxlength);
638  if (bind)
639  {
640  if (CS_SUCCEED != ct_bind(cscommand, i + 1, &(columns[i]), columndata[i], &(columndata[i].length), &(columndata[i].indicator)))
641  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to bind column ").append(std::to_string(i)));
642  }
643  }
644  }
645 
646  bool scroll_fetch(CS_INT type)
647  {
648  if (scrollable)
649  {
650  switch (ct_scroll_fetch(cscommand, type, CS_UNUSED, CS_TRUE, &result))
651  {
652  case CS_END_DATA:
653  case CS_CURSOR_BEFORE_FIRST:
654  row_cnt = 0;
655  return false;
656  case CS_CURSOR_AFTER_LAST:
657  row_cnt += 1;
658  return false;
659  }
660  switch (type)
661  {
662  case CS_FIRST: row_cnt = 1; break;
663  case CS_LAST: row_cnt = -1; break;
664  case CS_PREV: row_cnt -= 1; break;
665  case CS_NEXT: row_cnt += 1; break;
666  }
667  return true;
668  }
669  else
670  throw std::runtime_error(std::string(__FUNCTION__).append(": The function can only be called if scrollable cursor is used "));
671  }
672 
673  template<typename T>
674  T get(size_t col_idx)
675  {
676  if (is_null(col_idx))
677  throw std::runtime_error(std::string(__FUNCTION__).append(": Can't convert NULL data"));
678  switch (columns[col_idx].datatype)
679  {
680  case CS_NUMERIC_TYPE:
681  case CS_DECIMAL_TYPE:
682  case CS_MONEY_TYPE:
683  case CS_MONEY4_TYPE:
684  {
685  double num = 0.0;
686  std::memset(&destfmt, 0, sizeof(destfmt));
687  destfmt.maxlength = sizeof(num);
688  destfmt.datatype = CS_FLOAT_TYPE;
689  destfmt.format = CS_FMT_UNUSED;
690  destfmt.locale = nullptr;
691  if (CS_SUCCEED != cs_convert(cscontext, &columns[col_idx], static_cast<CS_VOID*>(columndata[col_idx]), &destfmt, &num, 0))
692  throw std::runtime_error(std::string(__FUNCTION__).append(": cs_convert failed"));
693  return num;
694  }
695  }
696  if (sizeof(T) < columndata[col_idx].data.size())
697  throw std::runtime_error(std::string(__FUNCTION__).append(": Sybase data type is larger than the primitive data type"));
698  return *(reinterpret_cast<T*>((char*)columndata[col_idx]));
699  }
700 
701  void getdt(size_t col_idx)
702  {
703  if (is_null(col_idx))
704  throw std::runtime_error(std::string(__FUNCTION__).append(": Can't convert NULL data"));
705  switch (columns[col_idx].datatype)
706  {
707  case CS_DATETIME_TYPE:
708  case CS_DATETIME4_TYPE:
709 #ifdef CS_DATE_TYPE
710  case CS_DATE_TYPE:
711 #endif
712 #ifdef CS_TIME_TYPE
713  case CS_TIME_TYPE:
714 #endif
715 #ifdef CS_BIGDATETIME_TYPE
716  case CS_BIGDATETIME_TYPE:
717 #endif
718 #ifdef CS_BIGTIME_TYPE
719  case CS_BIGTIME_TYPE:
720 #endif
721  break;
722  default:
723  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid column data type (supported: date, time, datetime, smalldatetime, bigdatetime)"));
724  }
725  std::memset(&daterec, 0, sizeof(daterec));
726  if (CS_SUCCEED != cs_dt_crack(cscontext, columns[col_idx].datatype, static_cast<CS_VOID*>(columndata[col_idx]), &daterec))
727  throw std::runtime_error(std::string(__FUNCTION__).append(": cs_dt_crack failed"));
728  }
729 
730 private:
731  bool scrollable = false;
732  bool do_cancel = false;
733  long row_cnt = 0;
734  size_t affected_rows = 0;
735  bool more_res = false;
736  Context* cscontext = nullptr;
737  CS_COMMAND* cscommand = nullptr;
738  CS_RETCODE retcode;
739  CS_INT result;
740  CS_DATAFMT destfmt;
741  CS_DATEREC daterec;
742  struct tm stm;
743  std::map<std::string, int> name2index;
744  std::vector<CS_DATAFMT> columns;
745  std::vector<column_data> columndata;
746 }; // result_set
747 
748 
749 
750 //=====================================================================================
751 
761 {
762 public:
763  virtual ~connection()
764  {
765  destroy();
766  }
767 
768  connection(connection&& conn)
769  : ase(conn.ase), is_autocommit(conn.is_autocommit),
770  cscontext(conn.cscontext), csconnection(conn.csconnection),
771  server(std::move(conn.server)), user(std::move(conn.user)),
772  passwd(std::move(conn.passwd))
773  {
774  conn.cscontext = nullptr;
775  conn.csconnection = nullptr;
776  }
777 
778  connection& operator=(connection&& conn)
779  {
780  if (this != &conn)
781  {
782  destroy();
783  ase = conn.ase;
784  is_autocommit = conn.is_autocommit;
785  cscontext = conn.cscontext;
786  csconnection = conn.csconnection;
787  conn.cscontext = nullptr;
788  conn.csconnection = nullptr;
789  server = std::move(conn.server);
790  user = std::move(conn.user);
791  passwd = std::move(conn.passwd);
792  }
793  return *this;
794  }
795 
796  virtual bool connect()
797  {
798  if (true == connected())
799  disconnect();
800  if (nullptr != csconnection && CS_SUCCEED == ct_connect(csconnection, (server.empty() ? nullptr : const_cast<CS_CHAR*>(server.c_str())), server.empty() ? 0 : CS_NULLTERM))
801  is_server_ase();
802  return alive();
803  }
804 
805  virtual void disconnect()
806  {
807  if (nullptr != csconnection)
808  {
809  CS_INT stat = 0;
810  ct_con_props(csconnection, CS_GET, CS_CON_STATUS, &stat, CS_UNUSED, nullptr);
811  if (CS_CONSTAT_DEAD == stat)
812  ct_close(csconnection, CS_FORCE_CLOSE);
813  else if (CS_CONSTAT_CONNECTED == stat)
814  {
815  ct_cancel(csconnection, nullptr, CS_CANCEL_ALL);
816  if (CS_SUCCEED != ct_close(csconnection, CS_UNUSED))
817  ct_close(csconnection, CS_FORCE_CLOSE);
818  }
819  }
820  };
821 
822  virtual bool connected() const
823  {
824  if (nullptr != csconnection)
825  {
826  CS_INT stat = 0;
827  ct_con_props(csconnection, CS_GET, CS_CON_STATUS, &stat, CS_UNUSED, nullptr);
828  return (CS_CONSTAT_CONNECTED == stat || CS_CONSTAT_DEAD == stat);
829  }
830  return false;
831  }
832 
833  virtual bool alive() const
834  {
835  if (nullptr != csconnection)
836  {
837  CS_INT stat = 0;
838  ct_con_props(csconnection, CS_GET, CS_CON_STATUS, &stat, CS_UNUSED, nullptr);
839  return (CS_CONSTAT_CONNECTED == stat);
840  }
841  return false;
842  }
843 
844  virtual void autocommit(bool ac)
845  {
846  if (nullptr != csconnection)
847  {
848  CS_BOOL val = (ac ? TRUE : FALSE);
849  if (val != is_autocommit)
850  {
851  is_autocommit = val;
852  std::unique_ptr<dbi::istatement> stmt(get_statement(*this));
853  if (val)
854  stmt->execute("rollback tran");
855  else
856  stmt->execute("begin tran");
857  }
858  }
859  }
860 
861  virtual void commit()
862  {
863  if (FALSE == is_autocommit)
864  {
865  std::unique_ptr<dbi::istatement> stmt(get_statement(*this));
866  stmt->execute("commit tran begin tran");
867  }
868  }
869 
870  virtual void rollback()
871  {
872  if (FALSE == is_autocommit)
873  {
874  std::unique_ptr<dbi::istatement> stmt(get_statement(*this));
875  stmt->execute("rollback tran begin tran");
876  }
877  }
878 
879  virtual dbi::istatement* get_statement(dbi::iconnection& iconn);
880 
881 
882  template<typename T>
883  connection& userdata(T& user_struct)
884  {
885  T* us = &user_struct;
886  if (nullptr == csconnection || CS_SUCCEED != ct_con_props(csconnection, CS_SET, CS_USERDATA, reinterpret_cast<CS_VOID*>(&us), sizeof(us), nullptr))
887  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set connection user data pointer"));
888  return *this;
889  }
890 
891  template<typename T>
892  connection& userdata(T*& user_struct)
893  {
894  auto len = 0;
895  if (nullptr == csconnection || CS_SUCCEED != ct_con_props(csconnection, CS_GET, CS_USERDATA, reinterpret_cast<CS_VOID*>(&user_struct), sizeof(user_struct), &len))
896  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to get connection user data pointer"));
897  return *this;
898  }
899 
900  connection& props(action actn, CS_INT property, CS_VOID* buffer, CS_INT buflen = CS_UNUSED, CS_INT* outlen = nullptr)
901  {
902  if (nullptr == csconnection || nullptr == buffer || CS_SUCCEED != ct_con_props(csconnection, utils::base_type(actn), property, buffer, buflen, outlen))
903  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set connection property"));
904  return *this;
905  }
906 
907  Context* native_context() const
908  {
909  return cscontext;
910  }
911 
912  Connection* native_connection() const
913  {
914  return csconnection;
915  }
916 
917  bool is_ase()
918  {
919  return ase;
920  }
921 
922 private:
923  friend class driver;
924  friend class statement;
925 
926  connection() = delete;
927  connection(const connection&) = delete;
928  connection& operator=(const connection&) = delete;
929 
930  connection(Context* context, CS_INT dbg_flag, const std::string& protofile, const std::string& server, const std::string& user, const std::string& passwd)
931  : cscontext(context), server(server), user(user), passwd(passwd)
932  {
933  if (CS_SUCCEED != ct_con_alloc(cscontext, &csconnection))
934  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to allocate connection struct"));
935  if (CS_SUCCEED != ct_con_props(csconnection, CS_SET, CS_USERNAME, const_cast<CS_CHAR*>(user.c_str()), CS_NULLTERM, nullptr))
936  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set connection user"));
937  if (CS_SUCCEED != ct_con_props(csconnection, CS_SET, CS_PASSWORD, const_cast<CS_CHAR*>(passwd.c_str()), CS_NULLTERM, nullptr))
938  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set connection password"));
939  if (false == protofile.empty() && CS_SUCCEED != ct_debug(nullptr, csconnection, CS_SET_PROTOCOL_FILE, CS_UNUSED, const_cast<CS_CHAR*>(protofile.c_str()), protofile.length()))
940  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set debug protocol file name"));
941  if (0 != dbg_flag && CS_SUCCEED != ct_debug(cscontext, csconnection, CS_SET_FLAG, dbg_flag, nullptr, CS_UNUSED))
942  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set debug flags"));
943  }
944 
945  void destroy()
946  {
947  disconnect();
948  if (nullptr != csconnection)
949  {
950  ct_con_drop(csconnection);
951  csconnection = nullptr;
952  }
953  cscontext = nullptr;
954  }
955 
956  void is_server_ase()
957  {
958  std::unique_ptr<dbi::istatement> stmt(get_statement(*this));
959  dbi::iresult_set* rs = stmt->execute("if object_id('dbo.sysobjects') is not null and object_id('dbo.syscolumns') is not null select 1 else select 0");
960  if (!rs->next())
961  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to get server type"));
962  ase = rs->get_int(0);
963  }
964 
965 private:
966  bool ase = false;
967  CS_BOOL is_autocommit = CS_TRUE;
968  Context* cscontext = nullptr;
969  Connection* csconnection = nullptr;
970  std::string server;
971  std::string user;
972  std::string passwd;
973 };
974 
975 
976 
977 //=====================================================================================
978 
979 
984 class driver : public idriver
985 {
986 public:
987  ~driver()
988  {
989  if (cslocale != nullptr && cscontext != nullptr)
990  cs_loc_drop(cscontext, cslocale);
991  destroy(cscontext);
992  }
993 
994  dbi::connection get_connection(const std::string& server, const std::string& user = "", const std::string& passwd = "")
995  {
996  return create_connection(new connection(cscontext, dbg_flag, protofile, server, user, passwd));
997  }
998 
999  driver& debug(debug_flag flag)
1000  {
1001  std::lock_guard<utils::spin_lock> lg(lock);
1002  dbg_flag = utils::base_type(flag);
1003  return *this;
1004  }
1005 
1006  driver& debug_file(const std::string& fname)
1007  {
1008  std::lock_guard<utils::spin_lock> lg(lock);
1009  if (nullptr == cscontext || CS_SUCCEED != ct_debug(cscontext, nullptr, CS_SET_DBG_FILE, CS_UNUSED, const_cast<CS_CHAR*>(fname.c_str()), fname.length()))
1010  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set debug file name"));
1011  return *this;
1012  }
1013 
1014  driver& debug_protocol_file(const std::string& fname)
1015  {
1016  std::lock_guard<utils::spin_lock> lg(lock);
1017  protofile = fname;
1018  return *this;
1019  }
1020 
1021  driver& app_name(const std::string& appname)
1022  {
1023  std::lock_guard<utils::spin_lock> lg(lock);
1024  if (nullptr == cscontext || CS_SUCCEED != cs_config(cscontext, CS_SET, CS_APPNAME, const_cast<CS_CHAR*>(appname.c_str()), CS_NULLTERM, nullptr))
1025  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set application name"));
1026  return *this;
1027  }
1028 
1029  driver& config_file(const std::string& fname)
1030  {
1031  std::lock_guard<utils::spin_lock> lg(lock);
1032  if (nullptr == cscontext ||
1033  CS_SUCCEED != cs_config(cscontext, CS_SET, CS_EXTERNAL_CONFIG, reinterpret_cast<CS_VOID*>(&TRUE), CS_UNUSED, nullptr) ||
1034  CS_SUCCEED != cs_config(cscontext, CS_SET, CS_CONFIG_FILE, const_cast<CS_CHAR*>(fname.c_str()), CS_NULLTERM, nullptr))
1035  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set config file"));
1036  return *this;
1037  }
1038 
1039  driver& max_connections(unsigned int conn_num)
1040  {
1041  std::lock_guard<utils::spin_lock> lg(lock);
1042  if (nullptr == cscontext || CS_SUCCEED != ct_config(cscontext, CS_SET, CS_MAX_CONNECT, reinterpret_cast<CS_VOID*>(&conn_num), CS_UNUSED, nullptr))
1043  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set maximum connections"));
1044  return *this;
1045  }
1046 
1047  driver& timeout(unsigned int tout)
1048  {
1049  std::lock_guard<utils::spin_lock> lg(lock);
1050  if (nullptr == cscontext || CS_SUCCEED != ct_config(cscontext, CS_SET, CS_TIMEOUT, reinterpret_cast<CS_VOID*>(&tout), CS_UNUSED, nullptr))
1051  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set connection timeout"));
1052  return *this;
1053  }
1054 
1055  driver& keepalive(bool keep_alive)
1056  {
1057  std::lock_guard<utils::spin_lock> lg(lock);
1058  if (nullptr == cscontext || CS_SUCCEED != ct_config(cscontext, CS_SET, CS_CON_KEEPALIVE, reinterpret_cast<CS_VOID*>(&(keep_alive ? TRUE : FALSE)), CS_UNUSED, nullptr))
1059  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set keepalive property"));
1060  return *this;
1061  }
1062 
1063  driver& tcp_nodelay(bool nodelay)
1064  {
1065  std::lock_guard<utils::spin_lock> lg(lock);
1066  if (nullptr == cscontext || CS_SUCCEED != ct_config(cscontext, CS_SET, CS_CON_TCP_NODELAY, reinterpret_cast<CS_VOID*>(&(nodelay ? TRUE : FALSE)), CS_UNUSED, nullptr))
1067  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set keepalive property"));
1068  return *this;
1069  }
1070 
1071  driver& locale(const std::string& locale_name)
1072  {
1073  std::lock_guard<utils::spin_lock> lg(lock);
1074  if (nullptr == cscontext || (nullptr == cslocale && CS_SUCCEED != cs_loc_alloc(cscontext, &cslocale)))
1075  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to allocate locale structure"));
1076  if (!locale_name.empty())
1077  {
1078  if (CS_SUCCEED != cs_locale(cscontext, CS_SET, cslocale, CS_LC_ALL, const_cast<CS_CHAR*>(locale_name.c_str()), CS_NULLTERM, nullptr) ||
1079  CS_SUCCEED != cs_config(cscontext, CS_SET, CS_LOC_PROP, reinterpret_cast<CS_VOID*>(cslocale), CS_UNUSED, nullptr))
1080  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set locale name"));
1081  }
1082  return *this;
1083  }
1084 
1085  template<typename T>
1086  driver& userdata(T& user_struct)
1087  {
1088  std::lock_guard<utils::spin_lock> lg(lock);
1089  T* us = &user_struct;
1090  if (nullptr == cscontext || CS_SUCCEED != cs_config(cscontext, CS_SET, CS_USERDATA, reinterpret_cast<CS_VOID*>(&us), sizeof(us), nullptr))
1091  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set context user data pointer"));
1092  return *this;
1093  }
1094 
1095  template<typename T>
1096  driver& userdata(T*& user_struct)
1097  {
1098  std::lock_guard<utils::spin_lock> lg(lock);
1099  if (nullptr == cscontext || CS_SUCCEED != cs_config(cscontext, CS_GET, CS_USERDATA, reinterpret_cast<CS_VOID*>(&user_struct), sizeof(user_struct), nullptr))
1100  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to get context user data pointer"));
1101  return *this;
1102  }
1103 
1104  driver& version(long& ver)
1105  {
1106  std::lock_guard<utils::spin_lock> lg(lock);
1107  if (nullptr == cscontext || CS_SUCCEED != ct_config(cscontext, CS_GET, CS_VERSION, reinterpret_cast<CS_VOID*>(&ver), CS_UNUSED, nullptr))
1108  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to get sybase library version"));
1109  return *this;
1110  }
1111 
1112  driver& version_string(std::string& ver)
1113  {
1114  std::array<char, 256> buf = {'\0'};
1115  std::lock_guard<utils::spin_lock> lg(lock);
1116  if (nullptr == cscontext || CS_SUCCEED != ct_config(cscontext, CS_GET, CS_VER_STRING, reinterpret_cast<CS_VOID*>(buf.data()), buf.size(), nullptr))
1117  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to get sybase library version string"));
1118  ver = buf.data();
1119  return *this;
1120  }
1121 
1122  driver& cs_msg_callback(CS_RETCODE (*func) (Context*, ClientMessage*))
1123  {
1124  std::lock_guard<utils::spin_lock> lg(lock);
1125  if (nullptr == cscontext || nullptr == func ||
1126  CS_SUCCEED != cs_config(cscontext, CS_SET, CS_MESSAGE_CB, reinterpret_cast<CS_VOID*>(func), CS_UNUSED, nullptr))
1127  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set cs library callback function"));
1128  return *this;
1129  }
1130 
1131  driver& ct_msg_callback(CS_RETCODE (*func) (Context*, Connection*, ClientMessage*))
1132  {
1133  std::lock_guard<utils::spin_lock> lg(lock);
1134  if (nullptr == cscontext || nullptr == func ||
1135  CS_SUCCEED != ct_callback(cscontext, nullptr, CS_SET, CS_CLIENTMSG_CB, reinterpret_cast<CS_VOID*>(func)))
1136  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set client message callback function"));
1137  return *this;
1138  }
1139 
1140  driver& srv_msg_callback(CS_RETCODE (*func) (Context*, Connection*, ServerMessage*))
1141  {
1142  std::lock_guard<utils::spin_lock> lg(lock);
1143  if (nullptr == cscontext || nullptr == func ||
1144  CS_SUCCEED != ct_callback(cscontext, nullptr, CS_SET, CS_SERVERMSG_CB, reinterpret_cast<CS_VOID*>(func)))
1145  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set server message callback function"));
1146  return *this;
1147  }
1148 
1149  driver& config(action actn, cfg_type type, CS_INT property, CS_VOID* buffer, CS_INT buflen = CS_UNUSED, CS_INT* outlen = nullptr)
1150  {
1151  auto ret = false;
1152  if (nullptr != cscontext && nullptr != buffer)
1153  {
1154  std::lock_guard<utils::spin_lock> lg(lock);
1155  if (cfg_type::CS_LIB == type)
1156  ret = (CS_SUCCEED == cs_config(cscontext, utils::base_type(actn), property, buffer, buflen, outlen));
1157  else
1158  ret = (CS_SUCCEED == ct_config(cscontext, utils::base_type(actn), property, buffer, buflen, outlen));
1159  }
1160  if (false == ret)
1161  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set/get config parameter"));
1162  return *this;
1163  }
1164 
1165  Context* cs_context() const
1166  {
1167  return cscontext;
1168  }
1169 
1170  static const char* decode_severity(CS_INT v)
1171  {
1172  if (v < 0 || severity.size() - 1 < (size_t)v)
1173  return "UNKNOWN";
1174  return severity[v];
1175  }
1176 
1177 protected:
1178  driver(const driver&) = delete;
1179  driver(driver&&) = delete;
1180  driver& operator=(const driver&) = delete;
1181  driver& operator=(driver&&) = delete;
1182  driver** operator&() = delete;
1183 
1184  driver()
1185  {
1186  allocate(cscontext, version());
1187  cs_msg_callback([](Context* context, ClientMessage* msg)
1188  {
1189  std::cout << __FUNCTION__ << ": CS Library message: Severity - " << CS_SEVERITY(msg->msgnumber) << " (" << decode_severity(CS_SEVERITY(msg->msgnumber)) <<
1190  "), layer - " << CS_LAYER(msg->msgnumber) << ", origin - " << CS_ORIGIN(msg->msgnumber) <<
1191  ", number -" << CS_NUMBER(msg->msgnumber) << ", message - " << msg->msgstring << std::endl;
1192  if (msg->osstringlen > 0)
1193  std::cout << __FUNCTION__ << ": Operating System Message: " << msg->osstring << std::endl;
1194  return (CS_SUCCEED);
1195  });
1196  ct_msg_callback([](Context* context, Connection* connection, ClientMessage* msg)
1197  {
1198  std::cout << __FUNCTION__ << ": Open Client Message: Severity - " << CS_SEVERITY(msg->msgnumber) << " (" << decode_severity(CS_SEVERITY(msg->msgnumber)) <<
1199  "), layer - " << CS_LAYER(msg->msgnumber) << ", origin - " << CS_ORIGIN(msg->msgnumber) <<
1200  ", number - " << CS_NUMBER(msg->msgnumber) << ", message - " << msg->msgstring << std::endl;
1201  if (msg->osstringlen > 0)
1202  std::cout << __FUNCTION__ << ": Operating System Message: " << msg->osstring << std::endl;
1203  return CS_SUCCEED;
1204  });
1205  srv_msg_callback([](Context* context, Connection* connection, ServerMessage* msg)
1206  {
1207  std::cout << __FUNCTION__ << ": Server message: " << (msg->svrnlen > 0 ? std::string("Server '").append(msg->svrname).append("': ") : "")
1208  << (msg->proclen > 0 ? std::string("Procedure '").append(msg->proc).append("': ") : "") << "Severity - " << msg->severity
1209  << ", state - " << msg->state << ", origin - " << msg->line << ", number - " << msg->msgnumber << ", message - " << msg->text << std::endl;
1210  return CS_SUCCEED;
1211  });
1212  }
1213 
1214  void allocate(Context*& cs_context, CS_INT version)
1215  {
1216  destroy(cs_context);
1217  if (CS_SUCCEED != cs_ctx_alloc(version, &cs_context))
1218  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to allocate context struct"));
1219  if (CS_SUCCEED != ct_init(cs_context, version))
1220  {
1221  destroy(cs_context);
1222  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to initialize context struct"));
1223  }
1224  }
1225 
1226  void destroy(Context*& cs_context)
1227  {
1228  if (cs_context != nullptr)
1229  {
1230  if (CS_SUCCEED != ct_exit(cs_context, CS_UNUSED))
1231  ct_exit(cs_context, CS_FORCE_EXIT);
1232  cs_ctx_drop(cs_context);
1233  cs_context = nullptr;
1234  }
1235  }
1236 
1237  CS_INT version()
1238  {
1239  auto version = CS_VERSION_100;
1240  Context* cs_context = nullptr;
1241  try
1242  {
1243  allocate(cs_context, CS_VERSION_100);
1244  std::array<char, 256> verbuf = {'\0'};
1245  if (CS_SUCCEED == ct_config(cs_context, CS_GET, CS_VER_STRING, verbuf.data(), verbuf.size(), nullptr))
1246  {
1247  std::string verstr = {verbuf.begin(), verbuf.end()};
1248  auto pos = verstr.find("BUILD");
1249  if (std::string::npos != pos && (pos + 8) < verstr.length())
1250  {
1251  auto ver = std::stoi(verstr.substr(pos + 5, 3));
1252 #ifdef CS_VERSION_160
1253  if (ver >= 160)
1254  version = CS_VERSION_160;
1255  else
1256 #endif
1257 #ifdef CS_VERSION_157
1258  if (ver >= 157)
1259  version = CS_VERSION_157;
1260  else
1261 #endif
1262 #ifdef CS_VERSION_155
1263  if (ver >= 155)
1264  version = CS_VERSION_155;
1265  else
1266 #endif
1267 #ifdef CS_VERSION_150
1268  if (ver >= 150)
1269  version = CS_VERSION_150;
1270  else
1271 #endif
1272 #ifdef CS_VERSION_125
1273  if (ver >= 125)
1274  version = CS_VERSION_125;
1275  else
1276 #endif
1277 #ifdef CS_VERSION_110
1278  if (ver >= 110)
1279  version = CS_VERSION_110;
1280 #endif
1281  }
1282  }
1283  }
1284  catch (...)
1285  { }
1286  destroy(cs_context);
1287  return version;
1288  }
1289 
1290 private:
1291  constexpr static std::array<const char*, 8> severity {{
1292  "CS_SV_INFORM",
1293  "CS_SV_CONFIG_FAIL",
1294  "CS_SV_RETRY_FAIL",
1295  "CS_SV_API_FAIL",
1296  "CS_SV_RESOURCE_FAIL",
1297  "CS_SV_COMM_FAIL",
1298  "CS_SV_INTERNAL_FAIL",
1299  "CS_SV_FATAL"
1300  }};
1301 
1302  Locale* cslocale = nullptr;
1303  Context* cscontext = nullptr;
1304  CS_INT dbg_flag = 0;
1305  std::string protofile;
1306  utils::spin_lock lock;
1307 }; // driver
1308 
1309 constexpr std::array<const char*, 8> driver::severity;
1310 
1311 
1312 
1313 
1314 
1315 //=====================================================================================
1316 
1317 
1318 
1328 {
1329 public:
1330  ~statement()
1331  {
1332  cancel();
1333  close_cursor();
1334  ct_cmd_drop(cscommand);
1335  }
1336 
1337  virtual bool cancel()
1338  {
1339  return rs.cancel();
1340  }
1341 
1342  virtual dbi::iresult_set* execute()
1343  {
1344  if (false == conn.alive())
1345  throw std::runtime_error(std::string(__FUNCTION__).append(": Database connection is dead"));
1346  if (CS_SUCCEED != ct_send(cscommand))
1347  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to send command: ").append(command));
1348  rs.next_result();
1349  return &rs;
1350  }
1351 
1352  virtual dbi::iresult_set* execute(const std::string& cmd, bool usecursor = false, bool scrollable = false)
1353  {
1354  if (cmd.empty())
1355  throw std::runtime_error(std::string(__FUNCTION__).append(": SQL command is not set"));
1356  set_command(cmd, CS_LANG_CMD);
1357  usecursor && init_cursor(scrollable) || init_command();
1358  return execute();
1359  }
1360 
1361  virtual void prepare(const std::string& cmd)
1362  {
1363  set_command(cmd, CS_LANG_CMD);
1364  std::string csid = genid("proc");
1365  if (false == conn.alive())
1366  throw std::runtime_error(std::string(__FUNCTION__).append(": Database connection is dead"));
1367  if (CS_SUCCEED != ct_dynamic(cscommand, CS_PREPARE, const_cast<CS_CHAR*>(csid.c_str()), CS_NULLTERM, const_cast<CS_CHAR*>(command.c_str()), CS_NULLTERM))
1368  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to prepare command"));
1369  execute();
1370  if (CS_SUCCEED != ct_dynamic(cscommand, CS_DESCRIBE_INPUT, const_cast<CS_CHAR*>(csid.c_str()), CS_NULLTERM, nullptr, CS_UNUSED))
1371  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to get input params decription"));
1372  execute();
1373  if (CS_SUCCEED != ct_dynamic(cscommand, CS_EXECUTE, const_cast<CS_CHAR*>(csid.c_str()), CS_NULLTERM, nullptr, CS_UNUSED))
1374  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set command for execution"));
1375  param_datafmt.resize(rs.columns.size());
1376  param_data.resize(rs.columns.size());
1377  for (auto i = 0U; i < rs.columns.size(); ++i)
1378  {
1379  param_datafmt[i] = rs.columns[i];
1380  param_data[i].allocate(rs.columns[i].maxlength);
1381  if (CS_SUCCEED != ct_setparam(cscommand, &(param_datafmt[i]), param_data[i], &(param_data[i].length), &(param_data[i].indicator)))
1382  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set param, index ").append(std::to_string(i)));
1383  }
1384  }
1385 
1386  virtual void call(const std::string& cmd)
1387  {
1388  get_proc_params(cmd);;
1389  set_command(cmd, CS_RPC_CMD);
1390  if (CS_SUCCEED != ct_command(cscommand, CS_RPC_CMD, const_cast<CS_CHAR*>(cmd.c_str()), CS_NULLTERM, CS_NO_RECOMPILE))
1391  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set stored proc command"));
1392  for (auto i = 0U; i < param_datafmt.size(); ++i)
1393  {
1394  if (CS_SUCCEED != ct_setparam(cscommand, &(param_datafmt[i]), param_data[i], &(param_data[i].length), &(param_data[i].indicator)))
1395  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set param, index ").append(std::to_string(i)));
1396  }
1397  }
1398 
1399  virtual int proc_retval()
1400  {
1401  if (CS_RPC_CMD == cmdtype && rs.more_results() && rs.next() && rs.column_count() == 1)
1402  {
1403  int ret = rs.get_int(0);
1404  rs.more_results() && rs.next();
1405  rs.more_results() && rs.next() && rs.cancel();
1406  return ret;
1407  }
1408  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid function call, it must be called after all result sets from stored procedure are processed"));
1409  }
1410 
1411  virtual void set_null(size_t param_idx)
1412  {
1413  if (param_idx >= param_data.size())
1414  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid index"));
1415  param_data[param_idx].length = 0;
1416  param_data[param_idx].indicator = -1;
1417  }
1418 
1419  virtual void set_short(size_t param_idx, int16_t val)
1420  {
1421  set(param_idx, val);
1422  }
1423 
1424  virtual void set_ushort(size_t param_idx, uint16_t val)
1425  {
1426  set(param_idx, val);
1427  }
1428 
1429  virtual void set_int(size_t param_idx, int32_t val)
1430  {
1431  set(param_idx, val);
1432  }
1433 
1434  virtual void set_uint(size_t param_idx, uint32_t val)
1435  {
1436  set(param_idx, val);
1437  }
1438 
1439  virtual void set_long(size_t param_idx, int64_t val)
1440  {
1441  set(param_idx, val);
1442  }
1443 
1444  virtual void set_ulong(size_t param_idx, uint64_t val)
1445  {
1446  set(param_idx, val);
1447  }
1448 
1449  virtual void set_float(size_t param_idx, float val)
1450  {
1451  set(param_idx, val);
1452  }
1453 
1454  virtual void set_double(size_t param_idx, double val)
1455  {
1456  set(param_idx, val);
1457  }
1458 
1459  virtual void set_bool(size_t param_idx, bool val)
1460  {
1461  set(param_idx, val);
1462  }
1463 
1464  virtual void set_char(size_t param_idx, char val)
1465  {
1466  set(param_idx, val);
1467  }
1468 
1469  virtual void set_string(size_t param_idx, const std::string& val)
1470  {
1471  if (param_idx >= param_data.size())
1472  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid index"));
1473  param_data[param_idx].length = val.length();
1474  if (CS_TEXT_TYPE == param_datafmt[param_idx].datatype)
1475  {
1476  param_datafmt[param_idx].maxlength = param_data[param_idx].length;
1477  param_data[param_idx].allocate(param_data[param_idx].length);
1478  }
1479  if (param_data[param_idx].length > param_datafmt[param_idx].maxlength)
1480  throw std::runtime_error(std::string(__FUNCTION__).append(": Data length is greater than maximum field size"));
1481  param_data[param_idx].indicator = 0;
1482  std::memcpy(param_data[param_idx], val.c_str(), val.length());
1483  }
1484 
1485  virtual void set_date(size_t param_idx, int val)
1486  {
1487  int yr = val / 10000;
1488  int mon = (val % 10000) / 100;
1489  int day = val % 100;
1490  std::vector<char> dt(22);
1491  std::sprintf(dt.data(), "%4d%02d%02d 00:00:00.000", yr, mon, day);
1492  setdt(param_idx, dt.data());
1493  }
1494 
1495  virtual void set_time(size_t param_idx, double val)
1496  {
1497  int t = static_cast<int>(val);
1498  int hr = t / 10000;
1499  int min = (t % 10000) / 100;
1500  int sec = t % 100;
1501  int ms = floor((val - t) * 1000 + 0.5);
1502  std::vector<char> dt(22);
1503  std::sprintf(dt.data(), "19000101 %02d:%02d:%02d.%03d", hr, min, sec, ms);
1504  setdt(param_idx, dt.data());
1505  }
1506 
1507  virtual void set_datetime(size_t param_idx, time_t val)
1508  {
1509 #if defined(_WIN32) || defined(_WIN64)
1510  ::localtime_s(&stm, &val);
1511 #else
1512  ::localtime_r(&val, &stm);
1513 #endif
1514  stm.tm_year += 1900;
1515  std::vector<char> dt(22);
1516  std::sprintf(dt.data(), "%04d%02d%02d %02d:%02d:%02d.000", stm.tm_year, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec);
1517  setdt(param_idx, dt.data());
1518  }
1519 
1520  virtual void set_u16char(size_t param_idx, char16_t val)
1521  {
1522  if (param_idx >= param_data.size())
1523  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid index"));
1524  param_data[param_idx].length = sizeof(char16_t);
1525  if (param_data[param_idx].length > param_datafmt[param_idx].maxlength)
1526  throw std::runtime_error(std::string(__FUNCTION__).append(": Data length is greater than maximum field size"));
1527  param_data[param_idx].indicator = 0;
1528  std::memcpy(param_data[param_idx], &val, param_data[param_idx].length);
1529  }
1530 
1531  virtual void set_u16string(size_t param_idx, const std::u16string& val)
1532  {
1533  if (param_idx >= param_data.size())
1534  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid index"));
1535  param_data[param_idx].length = sizeof(char16_t) * val.length();
1536 #ifdef CS_UNITEXT_TYPE
1537  if (CS_UNITEXT_TYPE == param_datafmt[param_idx].datatype)
1538  {
1539  param_datafmt[param_idx].maxlength = param_data[param_idx].length;
1540  param_data[param_idx].allocate(param_data[param_idx].length);
1541  }
1542 #endif
1543  if (param_data[param_idx].length > param_datafmt[param_idx].maxlength)
1544  throw std::runtime_error(std::string(__FUNCTION__).append(": Data length is greater than maximum field size"));
1545  param_data[param_idx].indicator = 0;
1546  std::memcpy(param_data[param_idx], &val[0], param_data[param_idx].length);
1547  }
1548 
1549  virtual void set_binary(size_t param_idx, const std::vector<uint8_t>& val)
1550  {
1551  if (param_idx >= param_data.size())
1552  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid index"));
1553  param_data[param_idx].length = val.size();
1554  if (CS_IMAGE_TYPE == param_datafmt[param_idx].datatype || CS_LONGBINARY_TYPE == param_datafmt[param_idx].datatype)
1555  {
1556  param_datafmt[param_idx].maxlength = param_data[param_idx].length;
1557  param_data[param_idx].allocate(param_data[param_idx].length);
1558  }
1559  if (param_data[param_idx].length > param_datafmt[param_idx].maxlength)
1560  throw std::runtime_error(std::string(__FUNCTION__).append(": Data length is greater than maximum field size"));
1561  param_data[param_idx].indicator = 0;
1562  std::memcpy(param_data[param_idx], &val[0], param_data[param_idx].length);
1563  }
1564 
1565 private:
1566  friend class connection;
1567  statement() = delete;
1568  statement(const statement&) = delete;
1569  statement& operator=(const statement&) = delete;
1570  statement(connection& conn) : conn(conn)
1571  {
1572  if (CS_SUCCEED != ct_cmd_alloc(conn.csconnection, &cscommand))
1573  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to allocate command struct"));
1574  rs.cscontext = conn.cscontext;
1575  rs.cscommand = cscommand;
1576  }
1577 
1578  std::string genid(const std::string& type)
1579  {
1580  static std::atomic_int cnt(1);
1581  std::string id = type;
1582  id.append(std::to_string(cnt++)).append(std::to_string(std::hash<std::string>()(command)));
1583  return id;
1584  }
1585 
1586  void set_command(const std::string& cmd, CS_INT type)
1587  {
1588  command = cmd;
1589  cmdtype = type;
1590  rs.cancel();
1591  rs.set_scrollable(false);
1592  close_cursor();
1593  }
1594 
1595  bool init_command()
1596  {
1597  if (CS_SUCCEED != ct_command(cscommand, CS_LANG_CMD, const_cast<CS_CHAR*>(command.c_str()), CS_NULLTERM, CS_UNUSED))
1598  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to set command"));
1599  return true;
1600  }
1601 
1602  bool init_cursor(bool scrollable)
1603  {
1604  cursor = true;
1605  rs.set_scrollable(scrollable);
1606  std::string csid = genid("cursor");
1607  if (CS_SUCCEED != ct_cursor(cscommand, CS_CURSOR_DECLARE, const_cast<CS_CHAR*>(csid.c_str()), CS_NULLTERM, const_cast<CS_CHAR*>(command.c_str()), CS_NULLTERM, (scrollable ? CS_SCROLL_CURSOR : CS_READ_ONLY)))
1608  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to declare cursor"));
1609  if (CS_SUCCEED != ct_cursor(cscommand, CS_CURSOR_OPEN, nullptr, CS_UNUSED, nullptr, CS_UNUSED, CS_UNUSED))
1610  throw std::runtime_error(std::string(__FUNCTION__).append(": Failed to open cursor"));
1611  return true;
1612  }
1613 
1614  void close_cursor()
1615  {
1616  if (cursor)
1617  {
1618  ct_cursor(cscommand, CS_CURSOR_CLOSE, nullptr, CS_UNUSED, nullptr, CS_UNUSED, CS_DEALLOC);
1619  cursor = false;
1620  execute();
1621  }
1622  }
1623 
1624  void get_proc_params(std::string procname)
1625  {
1626  param_datafmt.clear();
1627  param_data.clear();
1628  std::string sql;
1629  if (conn.is_ase())
1630  {
1631  std::string procnum = "1";
1632  std::string sysprefix = "dbo";
1633  if (2 == std::count(procname.begin(), procname.end(), '.'))
1634  sysprefix.insert(0, procname.substr(0, procname.find('.') + 1));
1635  std::size_t pos = procname.find(';');
1636  if (std::string::npos != pos)
1637  {
1638  procnum = procname.substr(pos + 1);
1639  procname.erase(pos);
1640  }
1641  sql = "select c.name, c.usertype, c.length, c.prec, c.scale, c.status2 from " +
1642  sysprefix + ".sysobjects o, " + sysprefix + ".syscolumns c where o.id = object_id('" +
1643  procname + "') and o.type = 'P' and o.id = c.id and c.number = " +
1644  procnum + " order by c.colid";
1645  }
1646  else
1647  {
1648  sql = "select m.parm_name, m.domain_id, m.width, m.width, m.scale, case m.parm_mode_out when 'Y' then 2 else 1 end from sysobjects o, sysprocedure p, sysprocparm m where o.id = object_id('" +
1649  procname + "') and o.type = 'P' and o.name = p.proc_name and o.uid = p.creator and m.proc_id = p.proc_id and m.parm_type = 0 order by m.parm_id";
1650  }
1651  execute(sql);
1652  while (rs.next())
1653  {
1654  param_datafmt.push_back(CS_DATAFMT());
1655  CS_DATAFMT& datafmt = *param_datafmt.rbegin();
1656  memset(&datafmt, 0, sizeof(datafmt));
1657  strcpy(datafmt.name, rs.get_string(0).c_str());
1658  datafmt.namelen = CS_NULLTERM;
1659  datafmt.datatype = ctlib_datatype(rs.get_int(1));
1660  datafmt.format = CS_FMT_UNUSED;
1661  datafmt.maxlength = rs.get_int(2);
1662  if (!rs.is_null(3))
1663  datafmt.precision = rs.get_int(3);
1664  if (!rs.is_null(4))
1665  datafmt.scale = rs.get_int(4);
1666  datafmt.status = (rs.get_int(5) == 1 ? CS_INPUTVALUE : CS_RETURN);
1667  datafmt.locale = NULL;
1668  param_data.push_back(result_set::column_data());
1669  result_set::column_data& coldata = *param_data.rbegin();
1670  coldata.allocate(datafmt.maxlength);
1671  coldata.length = datafmt.maxlength;
1672  }
1673  }
1674 
1675  template<typename T>
1676  void set(size_t param_idx, T val)
1677  {
1678  if (param_idx >= param_data.size())
1679  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid index"));
1680  switch (param_datafmt[param_idx].datatype)
1681  {
1682  case CS_NUMERIC_TYPE:
1683  case CS_DECIMAL_TYPE:
1684  case CS_MONEY_TYPE:
1685  case CS_MONEY4_TYPE:
1686  {
1687  double t = val;
1688  std::memset(&srcfmt, 0, sizeof(srcfmt));
1689  srcfmt.datatype = CS_FLOAT_TYPE;
1690  srcfmt.format = CS_FMT_UNUSED;
1691  srcfmt.locale = nullptr;
1692  srcfmt.maxlength = sizeof(t);
1693  if (CS_SUCCEED != cs_convert(conn.cscontext, &srcfmt, &t, &param_datafmt[param_idx], param_data[param_idx], 0))
1694  throw std::runtime_error(std::string(__FUNCTION__).append(": cs_convert failed"));
1695  }
1696  break;
1697  default:
1698  {
1699  if (sizeof(T) > param_data[param_idx].data.size() && CS_TINYINT_TYPE != param_datafmt[param_idx].datatype)
1700  throw std::runtime_error(std::string(__FUNCTION__).append(": Primitive data type is larger than the Sybase data type"));
1701  *(reinterpret_cast<T*>((char*)param_data[param_idx])) = val;
1702  }
1703  }
1704  param_data[param_idx].length = param_datafmt[param_idx].maxlength;
1705  param_data[param_idx].indicator = 0;
1706  }
1707 
1708  void setdt(size_t param_idx, const std::string& val)
1709  {
1710  if (param_idx >= param_data.size())
1711  throw std::runtime_error(std::string(__FUNCTION__).append(": Invalid index"));
1712  std::memset(&srcfmt, 0, sizeof(srcfmt));
1713  srcfmt.datatype = CS_CHAR_TYPE;
1714  srcfmt.format = CS_FMT_UNUSED;
1715  srcfmt.locale = nullptr;
1716  srcfmt.maxlength = val.length();
1717  if (CS_SUCCEED != cs_convert(conn.cscontext, &srcfmt, const_cast<char*>(val.c_str()), &param_datafmt[param_idx], param_data[param_idx], 0))
1718  throw std::runtime_error(std::string(__FUNCTION__).append(": cs_convert failed"));
1719  param_data[param_idx].length = param_datafmt[param_idx].maxlength;
1720  param_data[param_idx].indicator = 0;
1721  }
1722 
1723  CS_INT ctlib_datatype(int dbt)
1724  {
1725  if (conn.is_ase())
1726  {
1727  switch(dbt)
1728  {
1729  case 1: return CS_CHAR_TYPE; // char
1730  case 2: return CS_VARCHAR_TYPE; // varchar
1731  case 3: return CS_BINARY_TYPE; // binary
1732  case 4: return CS_VARBINARY_TYPE; // varbinary
1733  case 5: return CS_TINYINT_TYPE; // tinyint
1734  case 6: return CS_SMALLINT_TYPE; // smallint
1735  case 7: return CS_INT_TYPE; // int
1736  case 8: return CS_FLOAT_TYPE; // float
1737  case 10: return CS_NUMERIC_TYPE; // numeric
1738  case 11: return CS_MONEY_TYPE; // money
1739  case 12: return CS_DATETIME_TYPE; // datetime
1740  case 13: return CS_INT_TYPE; // intn
1741  case 14: return CS_FLOAT_TYPE; // floatn
1742  case 15: return CS_DATETIME_TYPE; // datetimn
1743  case 16: return CS_BIT_TYPE; // bit
1744  case 17: return CS_MONEY_TYPE; // moneyn
1745  case 19: return CS_TEXT_TYPE; // text
1746  case 20: return CS_IMAGE_TYPE; // image
1747  case 21: return CS_MONEY4_TYPE; // smallmoney
1748  case 22: return CS_DATETIME4_TYPE; // smalldatetime
1749  case 23: return CS_REAL_TYPE; // real
1750  case 24: return CS_CHAR_TYPE; // nchar
1751  case 25: return CS_VARCHAR_TYPE; // nvarchar
1752  case 26: return CS_DECIMAL_TYPE; // decimal
1753  case 27: return CS_DECIMAL_TYPE; // decimaln
1754  case 28: return CS_NUMERIC_TYPE; // numericn
1755  case 34: return CS_UNICHAR_TYPE; // unichar
1756  case 35: return CS_UNICHAR_TYPE; // univarchar
1757 #ifdef CS_UNITEXT_TYPE
1758  case 36: return CS_UNITEXT_TYPE; // unitext
1759 #endif
1760  case 37: return CS_DATE_TYPE; // date
1761  case 38: return CS_TIME_TYPE; // time
1762  case 39: return CS_DATE_TYPE; // daten
1763  case 40: return CS_TIME_TYPE; // timen
1764 #ifdef CS_BIGINT_TYPE
1765  case 43: return CS_BIGINT_TYPE; // bigint
1766 #endif
1767 #ifdef CS_USMALLINT_TYPE
1768  case 44: return CS_USMALLINT_TYPE; // usmallint
1769 #endif
1770 #ifdef CS_UINT_TYPE
1771  case 45: return CS_UINT_TYPE; // uint
1772 #endif
1773 #ifdef CS_UBIGINT_TYPE
1774  case 46: return CS_UBIGINT_TYPE; // ubigint
1775 #endif
1776 #ifdef CS_UINT_TYPE
1777  case 47: return CS_UINT_TYPE; // uintn
1778 #endif
1779  case 80: return CS_DATETIME_TYPE; // timestamp
1780  }
1781  }
1782  else
1783  {
1784  switch (dbt)
1785  {
1786  case 1: return CS_SMALLINT_TYPE; // smallint
1787  case 2: return CS_INT_TYPE; // int
1788  case 3: return CS_NUMERIC_TYPE; // numeric
1789  case 4: return CS_FLOAT_TYPE; // float
1790  case 5: return CS_REAL_TYPE; // real
1791  case 6: return CS_DATE_TYPE; // date
1792  case 7: return CS_CHAR_TYPE; // char
1793  case 8: return CS_CHAR_TYPE; // char
1794  case 9: return CS_VARCHAR_TYPE; // varchar
1795  case 10: return CS_LONGCHAR_TYPE; // long varchar
1796  case 11: return CS_BINARY_TYPE; // binary
1797  case 12: return CS_LONGBINARY_TYPE;// longbinary
1798  case 13: return CS_DATETIME_TYPE; // timestamp
1799  case 14: return CS_TIME_TYPE; // time
1800  case 19: return CS_TINYINT_TYPE; // tinyint
1801 #ifdef CS_BIGINT_TYPE
1802  case 20: return CS_BIGINT_TYPE; // bigint
1803 #endif
1804 #ifdef CS_UINT_TYPE
1805  case 21: return CS_UINT_TYPE; // uint
1806 #endif
1807 #ifdef CS_USMALLINT_TYPE
1808  case 22: return CS_USMALLINT_TYPE; // usmallint
1809 #endif
1810 #ifdef CS_UBIGINT_TYPE
1811  case 23: return CS_UBIGINT_TYPE; // ubigint
1812 #endif
1813  case 24: return CS_BIT_TYPE; // bit
1814  case 27: return CS_DECIMAL_TYPE; // decimal
1815  case 28: return CS_VARBINARY_TYPE; // varbinary
1816  }
1817  }
1818  throw std::runtime_error(std::string(__FUNCTION__).append(": Unknown data type: ").append(std::to_string(dbt)));
1819  }
1820 
1821 private:
1822  CS_COMMAND* cscommand = nullptr;
1823  connection& conn;
1824  bool cursor = false;
1825  CS_INT cmdtype = CS_RPC_CMD;
1826  std::string command;
1827  result_set rs;
1828  CS_DATAFMT srcfmt;
1829  struct tm stm;
1830  std::vector<CS_DATAFMT> param_datafmt;
1831  std::vector<result_set::column_data> param_data;
1832 }; // statement
1833 
1834 
1835 
1836 dbi::istatement* connection::get_statement(dbi::iconnection& iconn)
1837 {
1838  return new statement(dynamic_cast<connection&>(iconn));
1839 }
1840 
1841 
1842 
1843 
1844 
1845 
1846 } } } } // namespace vgi::dbconn::dbd::sybase
1847 
1848 #endif // SYBASE_DRIVER_HPP
1849 
sybase ASE driver class based on ctlib sybase C library.
iresult_set - is an interface that describes common functionality for all concrete native implementat...
Definition: result_set.hpp:32
result_set - is a class that implements dbi::iresult_set interface and represents results of SQL quer...
istatement - is an interface that describes common functionality for all concrete native implementati...
Definition: statement.hpp:32
iconnection - is an interface that describes common functionality for all concrete native implementat...
Definition: connection.hpp:38
connection - is a class that implements dbi::iconnection interface and represents native database con...
idriver - is an interface for driver classes which declares function for creating a connection object...
Definition: driver.hpp:33
statement - is a class that implements dbi::istatement interface and represents native database state...
connection - is a class that manages native driver connection handle.
Definition: connection.hpp:62