Kea  1.5.0
cql_exchange.cc
Go to the documentation of this file.
1 // Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
2 // Copyright (C) 2016-2017 Deutsche Telekom AG.
3 //
4 // Authors: Razvan Becheriu <razvan.becheriu@qualitance.com>
5 // Andrei Pavel <andrei.pavel@qualitance.com>
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 //
11 // http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 
19 #include <config.h>
20 
21 #include <cql/cql_connection.h>
22 #include <cql/cql_exchange.h>
23 #include <cql/sql_common.h>
24 #include <database/db_exceptions.h>
25 
26 #include <boost/multi_index/hashed_index.hpp>
27 #include <boost/multi_index/member.hpp>
28 #include <boost/multi_index/sequenced_index.hpp>
29 #include <boost/multi_index_container.hpp>
30 #include <boost/noncopyable.hpp>
31 #include <boost/shared_ptr.hpp>
32 
33 #include <fstream>
34 #include <iostream>
35 #include <map>
36 #include <string>
37 #include <utility>
38 #include <vector>
39 
40 namespace isc {
41 namespace db {
42 
44 #define KEA_CASS_CHECK(cass_error) \
45  { \
46  if (cass_error != CASS_OK) { \
47  return cass_error; \
48  } \
49  }
50 
54 public:
55  size_t operator()(const ExchangeDataType& key) const {
56  return std::hash<size_t>{}(static_cast<size_t>(key));
57  }
58 };
59 
61 typedef std::unordered_map<ExchangeDataType, CqlFunction, ExchangeDataTypeHash>
64 
72 std::size_t
73 hash_value(const CassValueType& key) {
74  return key;
75 }
76 
79 
81 typedef std::unordered_map<std::type_index, ExchangeDataType> AnyTypeMap;
82 
83 // Declare uint8_t as key here for compatibility with g++ version 5. Ideally,
84 // it would be CassValueType
85 typedef std::unordered_map<uint8_t, ExchangeDataType> CassTypeMap;
87 
89 static AnyTypeMap ANY_TYPE_MAP = {
90  {typeid(NULL), EXCHANGE_DATA_TYPE_NONE},
91  {typeid(cass_bool_t*), EXCHANGE_DATA_TYPE_BOOL},
92  {typeid(cass_int8_t*), EXCHANGE_DATA_TYPE_INT8},
93  {typeid(cass_int16_t*), EXCHANGE_DATA_TYPE_INT16},
94  {typeid(cass_int32_t*), EXCHANGE_DATA_TYPE_INT32},
95  {typeid(cass_int64_t*), EXCHANGE_DATA_TYPE_INT64},
96  {typeid(std::string*), EXCHANGE_DATA_TYPE_STRING},
98  {typeid(CassUuid*), EXCHANGE_DATA_TYPE_UUID},
99  {typeid(Udt*), EXCHANGE_DATA_TYPE_UDT}, // user data type
101 
103 static CassTypeMap CASS_TYPE_MAP = {
104  {CASS_VALUE_TYPE_CUSTOM, EXCHANGE_DATA_TYPE_UDT},
105  {CASS_VALUE_TYPE_ASCII, EXCHANGE_DATA_TYPE_STRING},
106  {CASS_VALUE_TYPE_BIGINT, EXCHANGE_DATA_TYPE_INT64},
107  {CASS_VALUE_TYPE_BLOB, EXCHANGE_DATA_TYPE_BYTES},
108  {CASS_VALUE_TYPE_BOOLEAN, EXCHANGE_DATA_TYPE_BOOL},
109  {CASS_VALUE_TYPE_COUNTER, EXCHANGE_DATA_TYPE_INT32},
110  {CASS_VALUE_TYPE_DECIMAL, EXCHANGE_DATA_TYPE_INT32},
111  {CASS_VALUE_TYPE_DOUBLE, EXCHANGE_DATA_TYPE_INT64},
112  {CASS_VALUE_TYPE_FLOAT, EXCHANGE_DATA_TYPE_INT32},
113  {CASS_VALUE_TYPE_INT, EXCHANGE_DATA_TYPE_INT32},
114  {CASS_VALUE_TYPE_TEXT, EXCHANGE_DATA_TYPE_STRING},
115  {CASS_VALUE_TYPE_TIMESTAMP, EXCHANGE_DATA_TYPE_INT64},
116  {CASS_VALUE_TYPE_UUID, EXCHANGE_DATA_TYPE_UUID},
117  {CASS_VALUE_TYPE_VARCHAR, EXCHANGE_DATA_TYPE_STRING},
118  {CASS_VALUE_TYPE_VARINT, EXCHANGE_DATA_TYPE_INT32},
119  {CASS_VALUE_TYPE_TIMEUUID, EXCHANGE_DATA_TYPE_INT64},
120  {CASS_VALUE_TYPE_INET, EXCHANGE_DATA_TYPE_NONE},
121  {CASS_VALUE_TYPE_DATE, EXCHANGE_DATA_TYPE_INT64},
122  {CASS_VALUE_TYPE_TIME, EXCHANGE_DATA_TYPE_INT64},
123  {CASS_VALUE_TYPE_SMALL_INT, EXCHANGE_DATA_TYPE_INT16},
124  {CASS_VALUE_TYPE_TINY_INT, EXCHANGE_DATA_TYPE_INT8},
125  {CASS_VALUE_TYPE_LIST, EXCHANGE_DATA_TYPE_COLLECTION},
126  {CASS_VALUE_TYPE_MAP, EXCHANGE_DATA_TYPE_COLLECTION},
127  {CASS_VALUE_TYPE_SET, EXCHANGE_DATA_TYPE_COLLECTION},
128  {CASS_VALUE_TYPE_UDT, EXCHANGE_DATA_TYPE_UDT},
129  {CASS_VALUE_TYPE_TUPLE, EXCHANGE_DATA_TYPE_UDT}};
130 
133 Udt::Udt(const CqlConnection& connection, const std::string& name)
134  : AnyArray(), connection_(connection), name_(name) {
135  // Create type.
136  cass_data_type_ = cass_keyspace_meta_user_type_by_name(
137  connection_.keyspace_meta_, name_.c_str());
138  if (!cass_data_type_) {
140  "Udt::Udt(): UDT " << name_ << " does not exist ");
141  }
142  // Create container.
143  cass_user_type_ = cass_user_type_new_from_data_type(cass_data_type_);
144  if (!cass_user_type_) {
146  "Udt::Udt(): Type " << name_
147  << " is not a UDT as expected. ");
148  }
149 }
150 
153  //
154  // Bug: it seems that if there is no call to
155  // cass_user_type_set_*(cass_user_type_), then
156  // cass_user_type_free(cass_user_type_) might SIGSEGV, so we never
157  // free. Udt objects should have application scope though.
158  // cass_user_type_free(cass_user_type_);
159 }
161 
164 void
165 AnyArray::add(const boost::any& value) {
166  push_back(value);
167 }
168 
169 void
170 AnyArray::remove(const size_t& index) {
171  if (size() <= index) {
173  "AnyArray::remove(): index "
174  << index << " out of bounds: [0, " << (size() - 1)
175  << "]");
176  }
177  erase(begin() + index);
178 }
180 
184 static CassError
185 CqlBindNone(const boost::any& /* value */,
186  const size_t& index,
187  CassStatement* statement) {
188  return cass_statement_bind_null(statement, index);
189 }
190 
191 static CassError
192 CqlBindBool(const boost::any& value,
193  const size_t& index,
194  CassStatement* statement) {
195  return cass_statement_bind_bool(statement, index,
196  *boost::any_cast<cass_bool_t*>(value));
197 }
198 
199 static CassError
200 CqlBindInt8(const boost::any& value,
201  const size_t& index,
202  CassStatement* statement) {
203  return cass_statement_bind_int8(statement, index,
204  *boost::any_cast<cass_int8_t*>(value));
205 }
206 
207 static CassError
208 CqlBindInt16(const boost::any& value,
209  const size_t& index,
210  CassStatement* statement) {
211  return cass_statement_bind_int16(statement, index,
212  *boost::any_cast<cass_int16_t*>(value));
213 }
214 
215 static CassError
216 CqlBindInt32(const boost::any& value,
217  const size_t& index,
218  CassStatement* statement) {
219  return cass_statement_bind_int32(statement, index,
220  *boost::any_cast<cass_int32_t*>(value));
221 }
222 
223 static CassError
224 CqlBindInt64(const boost::any& value,
225  const size_t& index,
226  CassStatement* statement) {
227  return cass_statement_bind_int64(statement, index,
228  *boost::any_cast<cass_int64_t*>(value));
229 }
230 
231 static CassError
232 CqlBindString(const boost::any& value,
233  const size_t& index,
234  CassStatement* statement) {
235  return cass_statement_bind_string(
236  statement, index, boost::any_cast<std::string*>(value)->c_str());
237 }
238 
239 static CassError
240 CqlBindBytes(const boost::any& value,
241  const size_t& index,
242  CassStatement* statement) {
243  CassBlob* blob_value = boost::any_cast<CassBlob*>(value);
244  return cass_statement_bind_bytes(statement, index, blob_value->data(),
245  blob_value->size());
246 }
247 
248 static CassError
249 CqlBindUuid(const boost::any& value,
250  const size_t& index,
251  CassStatement* statement) {
252  return cass_statement_bind_uuid(statement, index,
253  *boost::any_cast<CassUuid*>(value));
254 }
255 
256 static CassError
257 CqlBindUdt(const boost::any& value,
258  const size_t& index,
259  CassStatement* statement) {
260  Udt* udt = boost::any_cast<Udt*>(value);
261 
262  if (!udt) {
263  isc_throw(BadValue, "Invalid value specified, not an Udt object");
264  }
265 
266  size_t i = 0u;
267 
268  // Let's iterate over all elements in udt and check that we indeed
269  // can assign the set function for each specified type.
270  for (boost::any& element : *udt) {
271  try {
273  CQL_FUNCTIONS[exchangeType(element)].cqlUdtSetFunction_(
274  element, i, udt->cass_user_type_));
275  } catch (const boost::bad_any_cast& exception) {
276  isc_throw(DbOperationError,
277  "CqlCommon::udtSetData(): "
278  << exception.what() << " when binding parameter "
279  << " of type " << element.type().name()
280  << "in UDT with function CQL_FUNCTIONS["
281  << exchangeType(element) << "].cqlUdtSetFunction_");
282  }
283  ++i;
284  }
285 
286  return cass_statement_bind_user_type(statement, index,
287  udt->cass_user_type_);
288 }
289 
290 static CassError
291 CqlBindCollection(const boost::any& value,
292  const size_t& index,
293  CassStatement* statement) {
294  AnyCollection* elements = boost::any_cast<AnyCollection*>(value);
295 
296  CassCollection* collection =
297  cass_collection_new(CASS_COLLECTION_TYPE_SET, elements->size());
298 
299  // Iterate over all elements and assign appropriate append function
300  // for each.
301  for (boost::any& element : *elements) {
302  ExchangeDataType type = exchangeType(element);
303  KEA_CASS_CHECK(CQL_FUNCTIONS[type].cqlCollectionAppendFunction_(
304  element, collection));
305  }
306 
307  const CassError cass_error =
308  cass_statement_bind_collection(statement, index, collection);
309  cass_collection_free(collection);
310 
311  return cass_error;
312 }
314 
318 static CassError
319 CqlUdtSetNone(const boost::any& /* udt_member */,
320  const size_t& position,
321  CassUserType* cass_user_type) {
322  return cass_user_type_set_null(cass_user_type, position);
323 }
324 
325 static CassError
326 CqlUdtSetBool(const boost::any& udt_member,
327  const size_t& position,
328  CassUserType* cass_user_type) {
329  return cass_user_type_set_bool(cass_user_type, position,
330  *boost::any_cast<cass_bool_t*>(udt_member));
331 }
332 
333 static CassError
334 CqlUdtSetInt8(const boost::any& udt_member,
335  const size_t& position,
336  CassUserType* cass_user_type) {
337  return cass_user_type_set_int8(cass_user_type, position,
338  *boost::any_cast<cass_int8_t*>(udt_member));
339 }
340 
341 static CassError
342 CqlUdtSetInt16(const boost::any& udt_member,
343  const size_t& position,
344  CassUserType* cass_user_type) {
345  return cass_user_type_set_int16(
346  cass_user_type, position, *boost::any_cast<cass_int16_t*>(udt_member));
347 }
348 
349 static CassError
350 CqlUdtSetInt32(const boost::any& udt_member,
351  const size_t& position,
352  CassUserType* cass_user_type) {
353  return cass_user_type_set_int32(
354  cass_user_type, position, *boost::any_cast<cass_int32_t*>(udt_member));
355 }
356 
357 static CassError
358 CqlUdtSetInt64(const boost::any& udt_member,
359  const size_t& position,
360  CassUserType* cass_user_type) {
361  return cass_user_type_set_int64(
362  cass_user_type, position, *boost::any_cast<cass_int64_t*>(udt_member));
363 }
364 
365 static CassError
366 CqlUdtSetString(const boost::any& udt_member,
367  const size_t& position,
368  CassUserType* cass_user_type) {
369  return cass_user_type_set_string(
370  cass_user_type, position,
371  boost::any_cast<std::string*>(udt_member)->c_str());
372 }
373 
374 static CassError
375 CqlUdtSetBytes(const boost::any& udt_member,
376  const size_t& position,
377  CassUserType* cass_user_type) {
378  CassBlob* blob_value = boost::any_cast<CassBlob*>(udt_member);
379  return cass_user_type_set_bytes(cass_user_type, position,
380  blob_value->data(), blob_value->size());
381 }
382 
383 static CassError
384 CqlUdtSetUuid(const boost::any& udt_member,
385  const size_t& position,
386  CassUserType* cass_user_type) {
387  return cass_user_type_set_uuid(cass_user_type, position,
388  *boost::any_cast<CassUuid*>(udt_member));
389 }
390 
391 static CassError
392 CqlUdtSetUdt(const boost::any& udt_member,
393  const size_t& position,
394  CassUserType* cass_user_type) {
395  return cass_user_type_set_user_type(
396  cass_user_type, position,
397  boost::any_cast<Udt*>(udt_member)->cass_user_type_);
398 }
399 
400 static CassError
401 CqlUdtSetCollection(const boost::any& udt_member,
402  const size_t& position,
403  CassUserType* cass_user_type) {
404  return cass_user_type_set_collection(
405  cass_user_type, position, boost::any_cast<CassCollection*>(udt_member));
406 }
408 
412 static CassError
413 CqlCollectionAppendNone(const boost::any& /* value */,
414  CassCollection* /* collection */) {
415  return CASS_OK;
416 }
417 
418 static CassError
419 CqlCollectionAppendBool(const boost::any& value, CassCollection* collection) {
420  return cass_collection_append_bool(collection,
421  *boost::any_cast<cass_bool_t*>(value));
422 }
423 
424 static CassError
425 CqlCollectionAppendInt8(const boost::any& value, CassCollection* collection) {
426  return cass_collection_append_int8(collection,
427  *boost::any_cast<cass_int8_t*>(value));
428 }
429 
430 static CassError
431 CqlCollectionAppendInt16(const boost::any& value, CassCollection* collection) {
432  return cass_collection_append_int16(collection,
433  *boost::any_cast<cass_int16_t*>(value));
434 }
435 
436 static CassError
437 CqlCollectionAppendInt32(const boost::any& value, CassCollection* collection) {
438  return cass_collection_append_int32(collection,
439  *boost::any_cast<cass_int32_t*>(value));
440 }
441 
442 static CassError
443 CqlCollectionAppendInt64(const boost::any& value, CassCollection* collection) {
444  return cass_collection_append_int64(collection,
445  *boost::any_cast<cass_int64_t*>(value));
446 }
447 
448 static CassError
449 CqlCollectionAppendString(const boost::any& value, CassCollection* collection) {
450  return cass_collection_append_string(
451  collection, boost::any_cast<std::string*>(value)->c_str());
452 }
453 
454 static CassError
455 CqlCollectionAppendBytes(const boost::any& value, CassCollection* collection) {
456  CassBlob* blob_value = boost::any_cast<CassBlob*>(value);
457  return cass_collection_append_bytes(collection, blob_value->data(),
458  blob_value->size());
459 }
460 
461 static CassError
462 CqlCollectionAppendUuid(const boost::any& value, CassCollection* collection) {
463  return cass_collection_append_uuid(collection,
464  *boost::any_cast<CassUuid*>(value));
465 }
466 
467 static CassError
468 CqlCollectionAppendUdt(const boost::any& value, CassCollection* collection) {
469  Udt* udt = boost::any_cast<Udt*>(value);
470  size_t i = 0u;
471  for (boost::any& element : *udt) {
472  KEA_CASS_CHECK(CQL_FUNCTIONS[exchangeType(element)].cqlUdtSetFunction_(
473  element, i, udt->cass_user_type_));
474  ++i;
475  }
476  return cass_collection_append_user_type(collection, udt->cass_user_type_);
477 }
478 
479 static CassError
480 CqlCollectionAppendCollection(const boost::any& value,
481  CassCollection* collection) {
482  return cass_collection_append_collection(
483  collection, boost::any_cast<CassCollection*>(value));
484 }
485 // @}
486 
489 static CassError
490 CqlGetNone(const boost::any& /* data */, const CassValue* /* value */) {
491  return CASS_OK;
492 }
493 
494 static CassError
495 CqlGetBool(const boost::any& data, const CassValue* value) {
496  return cass_value_get_bool(value, boost::any_cast<cass_bool_t*>(data));
497 }
498 
499 static CassError
500 CqlGetInt8(const boost::any& data, const CassValue* value) {
501  return cass_value_get_int8(value, boost::any_cast<cass_int8_t*>(data));
502 }
503 
504 static CassError
505 CqlGetInt16(const boost::any& data, const CassValue* value) {
506  return cass_value_get_int16(value, boost::any_cast<cass_int16_t*>(data));
507 }
508 
509 static CassError
510 CqlGetInt32(const boost::any& data, const CassValue* value) {
511  return cass_value_get_int32(value, boost::any_cast<cass_int32_t*>(data));
512 }
513 
514 static CassError
515 CqlGetInt64(const boost::any& data, const CassValue* value) {
516  return cass_value_get_int64(value, boost::any_cast<cass_int64_t*>(data));
517 }
518 
519 static CassError
520 CqlGetString(const boost::any& data, const CassValue* value) {
521  char const* data_value;
522  size_t size_value;
523  CassError cass_error = cass_value_get_string(
524  value, static_cast<char const**>(&data_value), &size_value);
525  boost::any_cast<std::string*>(data)->assign(data_value,
526  data_value + size_value);
527  return cass_error;
528 }
529 
530 static CassError
531 CqlGetBytes(const boost::any& data, const CassValue* value) {
532  const cass_byte_t* data_value;
533  size_t size_value;
534  CassError cass_error = cass_value_get_bytes(
535  value, static_cast<const cass_byte_t**>(&data_value), &size_value);
536  boost::any_cast<CassBlob*>(data)->assign(data_value,
537  data_value + size_value);
538  return cass_error;
539 }
540 
541 static CassError
542 CqlGetUuid(const boost::any& data, const CassValue* value) {
543  return cass_value_get_uuid(value, boost::any_cast<CassUuid*>(data));
544 }
545 
546 static CassError
547 CqlGetUdt(const boost::any& data, const CassValue* value) {
548  Udt* udt = boost::any_cast<Udt*>(data);
549 
550  CassIterator* fields = cass_iterator_fields_from_user_type(value);
551  if (!fields) {
552  isc_throw(DbOperationError, "CqlGetUdt(): column is not a UDT");
553  }
554  Udt::const_iterator it = udt->begin();
555  while (cass_iterator_next(fields)) {
556  const CassValue* field_value =
557  cass_iterator_get_user_type_field_value(fields);
558  if (cass_value_is_null(field_value)) {
559  isc_throw(DbOperationError,
560  "CqlGetUdt(): null value returned in UDT");
561  }
562  const CassValueType& type = cass_value_type(field_value);
563  KEA_CASS_CHECK(CQL_FUNCTIONS[exchangeType(type)].cqlGetFunction_(
564  *it, field_value));
565  ++it;
566  // If cqlGetFunction_() returns != CASS_OK, don't
567  // cass_iterator_free(items_iterator) because we're returning from this
568  // function and throwing from the callee.
569  }
570  cass_iterator_free(fields);
571  return CASS_OK;
572 }
573 
574 static CassError
575 CqlGetCollection(const boost::any& data, const CassValue* value) {
576  AnyCollection* collection = boost::any_cast<AnyCollection*>(data);
577  if (!collection) {
578  isc_throw(DbOperationError, "CqlGetCollection(): column is not a collection");
579  }
580 
581  BOOST_ASSERT(collection->size() == 1);
582 
585  boost::any underlying_object = *collection->begin();
586 
587  collection->clear();
588 
589  CassIterator* items = cass_iterator_from_collection(value);
590  if (!items) {
591  isc_throw(DbOperationError,
592  "CqlGetCollection(): column is not a collection");
593  }
594  while (cass_iterator_next(items)) {
595  const CassValue* item_value = cass_iterator_get_value(items);
596  if (cass_value_is_null(item_value)) {
597  isc_throw(DbOperationError,
598  "CqlGetCollection(): null value returned in collection");
599  }
600  const CassValueType& type = cass_value_type(item_value);
601 
602  collection->push_back(underlying_object);
603  KEA_CASS_CHECK(CQL_FUNCTIONS[exchangeType(type)].cqlGetFunction_(
604  *collection->rbegin(), item_value));
605  // If cqlGetFunction_() returns != CASS_OK, don't call
606  // cass_iterator_free(items_iterator) because we're returning from this
607  // function and throwing from the callee.
608  }
609  cass_iterator_free(items);
610  return CASS_OK;
611 }
613 
617  {CqlBindNone, CqlUdtSetNone, CqlCollectionAppendNone, CqlGetNone}},
619  {CqlBindBool, CqlUdtSetBool, CqlCollectionAppendBool, CqlGetBool}},
621  {CqlBindInt8, CqlUdtSetInt8, CqlCollectionAppendInt8, CqlGetInt8}},
623  {CqlBindInt16, CqlUdtSetInt16, CqlCollectionAppendInt16, CqlGetInt16}},
625  {CqlBindInt32, CqlUdtSetInt32, CqlCollectionAppendInt32, CqlGetInt32}},
627  {CqlBindInt64, CqlUdtSetInt64, CqlCollectionAppendInt64, CqlGetInt64}},
629  {CqlBindString, CqlUdtSetString, CqlCollectionAppendString,
630  CqlGetString}},
632  {CqlBindBytes, CqlUdtSetBytes, CqlCollectionAppendBytes, CqlGetBytes}},
634  {CqlBindUuid, CqlUdtSetUuid, CqlCollectionAppendUuid, CqlGetUuid}},
636  {CqlBindUdt, CqlUdtSetUdt, CqlCollectionAppendUdt, CqlGetUdt}},
638  {CqlBindCollection, CqlUdtSetCollection, CqlCollectionAppendCollection,
639  CqlGetCollection}}};
640 
642 exchangeType(const boost::any& object) {
643  const std::type_index type = object.type();
644  AnyTypeMap::const_iterator exchange_type_it = ANY_TYPE_MAP.find(type);
645  if (exchange_type_it == ANY_TYPE_MAP.end()) {
647  "exchangeType(): boost::any type "
648  << type.name() << " does not map to any exchange type");
649  }
650  const ExchangeDataType exchange_type = exchange_type_it->second;
651  if (exchange_type >= CQL_FUNCTIONS.size()) {
653  "exchangeType(): index " << exchange_type << " out of bounds "
654  << 0 << " - "
655  << (CQL_FUNCTIONS.size() - 1));
656  }
657  return exchange_type;
658 }
659 
661 exchangeType(const CassValueType& type) {
662  CassTypeMap::const_iterator exchange_type_it = CASS_TYPE_MAP.find(type);
663  if (exchange_type_it == CASS_TYPE_MAP.end()) {
665  "exchangeType(): Cassandra value type "
666  << type << " does not map to any exchange type");
667  }
668  const ExchangeDataType exchange_type = exchange_type_it->second;
669  if (exchange_type >= CQL_FUNCTIONS.size()) {
671  "exchangeType(): index " << exchange_type << " out of bounds "
672  << 0 << " - "
673  << CQL_FUNCTIONS.size() - 1);
674  }
675  return exchange_type;
676 }
677 
678 void
679 CqlCommon::bindData(const AnyArray& data, CassStatement* statement) {
680  size_t i = 0u;
681  for (const boost::any& element : data) {
682  CassError cass_error;
683  try {
684  cass_error = CQL_FUNCTIONS[exchangeType(element)].cqlBindFunction_(
685  element, i, statement);
686  } catch (const boost::bad_any_cast& exception) {
688  "CqlCommon::bindData(): "
689  << exception.what() << " when binding parameter " << i
690  << " which is of type " << element.type().name()
691  << " with function CQL_FUNCTIONS["
692  << exchangeType(element) << "].cqlBindFunction_()");
693  }
694  if (cass_error != CASS_OK) {
696  "CqlCommon::bindData(): unable to bind parameter "
697  << i << " which is of type " << element.type().name()
698  << " with function CQL_FUNCTIONS["
699  << exchangeType(element)
700  << "].cqlBindFunction_(), Cassandra error code: "
701  << cass_error_desc(cass_error));
702  }
703  ++i;
704  }
705 }
706 
707 void
708 CqlCommon::getData(const CassRow* row, AnyArray& data) {
709  size_t i = 0u;
710  for (boost::any& element : data) {
711  const CassValue* value = cass_row_get_column(row, i);
712  CassError cass_error;
713  try {
714  cass_error = CQL_FUNCTIONS[exchangeType(element)].cqlGetFunction_(
715  element, value);
716  } catch (const boost::bad_any_cast& exception) {
718  "CqlCommon::getData(): "
719  << exception.what() << " when retrieving parameter "
720  << i << " which is of type " << element.type().name()
721  << " with function CQL_FUNCTIONS["
722  << exchangeType(element) << "].cqlGetFunction_()");
723  }
724  if (cass_error != CASS_OK) {
725  isc_throw(
727  "CqlCommon::getData(): Cassandra error when retrieving column "
728  << i << ", Cassandra error code: "
729  << cass_error_desc(cass_error));
730  }
731  ++i;
732  }
733 }
734 
736 }
737 
739 }
740 
741 void
743  const uint32_t& valid_lifetime,
744  cass_int64_t& expire) {
745  // Calculate expire time. Store it in the 64-bit value so as we can
746  // detect overflows.
747  cass_int64_t expire_time = static_cast<cass_int64_t>(cltt) +
748  static_cast<cass_int64_t>(valid_lifetime);
749 
750  if (expire_time > DatabaseConnection::MAX_DB_TIME) {
752  "CqlExchange(): convertToDatabaseTime(): time value "
753  << expire_time << " is too large");
754  }
755 
756  expire = expire_time;
757 }
758 
759 void
760 CqlExchange::convertFromDatabaseTime(const cass_int64_t& expire,
761  const cass_int64_t& valid_lifetime,
762  time_t& cltt) {
766 
767  // Convert to local time
768  cltt = static_cast<time_t>(expire - valid_lifetime);
769 }
770 
771 AnyArray
772 CqlExchange::executeSelect(const CqlConnection& connection, const AnyArray& data,
773  StatementTag statement_tag, const bool& single /* = false */) {
774  CassError rc;
775  CassStatement* statement = NULL;
776  CassFuture* future = NULL;
777  AnyArray local_data = data;
778 
779  // Find the query statement first.
780  StatementMap::const_iterator it = connection.statements_.find(statement_tag);
781  if (it == connection.statements_.end()) {
783  "CqlExchange::executeSelect(): Statement "
784  << statement_tag << "has not been prepared.");
785  }
786 
787  // Bind the data before the query is executed.
788  CqlTaggedStatement tagged_statement = it->second;
789  if (tagged_statement.is_raw_) {
790  // The entire query is the first element in data.
791  std::string* query = boost::any_cast<std::string*>(local_data.back());
792  local_data.pop_back();
793  statement = cass_statement_new(query->c_str(), local_data.size());
794  } else {
795  statement = cass_prepared_bind(tagged_statement.prepared_statement_);
796  if (!statement) {
798  "CqlExchange::executeSelect(): unable to bind statement "
799  << tagged_statement.name_);
800  }
801  }
802 
803  // Set specific level of consistency if we're told to do so.
804  if (connection.force_consistency_) {
805  rc = cass_statement_set_consistency(statement, connection.consistency_);
806  if (rc != CASS_OK) {
807  cass_statement_free(statement);
809  "CqlExchange::executeSelect(): unable to set statement "
810  "consistency for statement "
811  << tagged_statement.name_
812  << ", Cassandra error code: " << cass_error_desc(rc));
813  }
814  }
815 
816  CqlCommon::bindData(local_data, statement);
817 
818  // Everything's ready. Call the actual statement.
819  future = cass_session_execute(connection.session_, statement);
820  if (!future) {
821  cass_statement_free(statement);
823  "CqlExchange::executeSelect(): no CassFuture for statement "
824  << tagged_statement.name_);
825  }
826 
827  // Wait for the statement execution to complete.
828  cass_future_wait(future);
829  const std::string error = connection.checkFutureError(
830  "CqlExchange::executeSelect(): cass_session_execute() != CASS_OK",
831  future, statement_tag);
832  rc = cass_future_error_code(future);
833  if (rc != CASS_OK) {
834  cass_future_free(future);
835  cass_statement_free(statement);
836  isc_throw(DbOperationError, error);
837  }
838 
839  // Get column values.
840  const CassResult* result_collection = cass_future_get_result(future);
841  if (single && cass_result_row_count(result_collection) > 1) {
842  cass_result_free(result_collection);
843  cass_future_free(future);
844  cass_statement_free(statement);
845  isc_throw(
847  "CqlExchange::executeSelect(): multiple records were found in "
848  "the database where only one was expected for statement "
849  << tagged_statement.name_);
850  }
851 
852  // Get results.
853  AnyArray return_values;
854  AnyArray collection;
855  CassIterator* rows = cass_iterator_from_result(result_collection);
856  while (cass_iterator_next(rows)) {
857  const CassRow* row = cass_iterator_get_row(rows);
858  createBindForSelect(return_values, statement_tag);
859  CqlCommon::getData(row, return_values);
860  collection.add(retrieve());
861  }
862 
863  // Free resources.
864  cass_iterator_free(rows);
865  cass_result_free(result_collection);
866  cass_future_free(future);
867  cass_statement_free(statement);
868 
869  return collection;
870 }
871 
872 void
873 CqlExchange::executeMutation(const CqlConnection& connection, const AnyArray& data,
874  StatementTag statement_tag) {
875  CassError rc;
876  CassStatement* statement = NULL;
877  CassFuture* future = NULL;
878 
879  // Find the statement on a list of prepared statements.
880  StatementMap::const_iterator it =
881  connection.statements_.find(statement_tag);
882  if (it == connection.statements_.end()) {
883  isc_throw(DbOperationError, "CqlExchange::executeSelect(): Statement "
884  << statement_tag << "has not been prepared.");
885  }
886  // Bind the statement.
887  CqlTaggedStatement tagged_statement = it->second;
888  statement = cass_prepared_bind(tagged_statement.prepared_statement_);
889  if (!statement) {
891  "CqlExchange::executeMutation(): unable to bind statement "
892  << tagged_statement.name_);
893  }
894 
895  // Set specific level of consistency, if told to do so.
896  if (connection.force_consistency_) {
897  rc = cass_statement_set_consistency(statement, connection.consistency_);
898  if (rc != CASS_OK) {
899  cass_statement_free(statement);
900  isc_throw(DbOperationError, "CqlExchange::executeMutation(): unable to set"
901  " statement consistency for statement " << tagged_statement.name_
902  << ", Cassandra error code: " << cass_error_desc(rc));
903  }
904  }
905 
906  CqlCommon::bindData(data, statement);
907 
908  future = cass_session_execute(connection.session_, statement);
909  if (!future) {
910  cass_statement_free(statement);
912  "CqlExchange::executeMutation(): unable to execute statement "
913  << tagged_statement.name_);
914  }
915  cass_future_wait(future);
916  const std::string error = connection.checkFutureError("CqlExchange::executeMutation():"
917  "cass_session_execute() != CASS_OK", future, statement_tag);
918  rc = cass_future_error_code(future);
919  if (rc != CASS_OK) {
920  cass_future_free(future);
921  cass_statement_free(statement);
922  isc_throw(DbOperationError, error);
923  }
924 
925  // Check if statement has been applied.
926  bool applied = statementApplied(future);
927 
928  // Free resources.
929  cass_future_free(future);
930  cass_statement_free(statement);
931 
932  if (!applied) {
933  isc_throw(
935  "CqlExchange::executeMutation(): [applied] is false for statement "
936  << tagged_statement.name_);
937  }
938 }
939 
940 bool
941 CqlExchange::statementApplied(CassFuture* future,
942  size_t* row_count,
943  size_t* column_count) {
944  const CassResult* result_collection = cass_future_get_result(future);
945  if (!result_collection) {
946  isc_throw(DbOperationError, "CqlExchange::statementApplied(): unable to get"
947  " results collection");
948  }
949  if (row_count) {
950  *row_count = cass_result_row_count(result_collection);
951  }
952  if (column_count) {
953  *column_count = cass_result_column_count(result_collection);
954  }
955  CassIterator* rows = cass_iterator_from_result(result_collection);
956  AnyArray data;
957  cass_bool_t applied = cass_true;
958  while (cass_iterator_next(rows)) {
959  const CassRow* row = cass_iterator_get_row(rows);
960  // [applied]: bool
961  data.add(&applied);
962  CqlCommon::getData(row, data);
963  }
964  cass_iterator_free(rows);
965  cass_result_free(result_collection);
966  return applied == cass_true;
967 }
968 
970 
972  {GET_VERSION, {GET_VERSION, "SELECT version, minor FROM schema_version "}}
973 };
974 
976 }
977 
979 }
980 
981 void
983  data.clear(); // Start with a fresh array.
984  data.add(&version_); // first column is a major version
985  data.add(&minor_); // second column is a minor version
986 }
987 
988 boost::any
990  pair_ = VersionPair(version_, minor_);
991  return &pair_;
992 }
993 
996  // Run statement.
997  const AnyArray where_values;
998  AnyArray version_collection =
999  executeSelect(connection, where_values, GET_VERSION, true);
1000 
1001  if (!version_collection.empty()) {
1002  return *boost::any_cast<VersionPair*>(*version_collection.begin());
1003  }
1004 
1005  return VersionPair();
1006 }
1007 
1008 } // namespace db
1009 } // namespace isc
Database statement not applied.
Definition: db_exceptions.h:20
#define KEA_CASS_CHECK(cass_error)
Macro to return directly from caller function.
Definition: cql_exchange.cc:44
StatementMap statements_
Pointer to external array of tagged statements containing statement name, array of names of bind para...
void executeMutation(const CqlConnection &connection, const AnyArray &assigned_values, StatementTag statement_tag)
Executes INSERT, UPDATE or DELETE statements.
virtual void createBindForSelect(AnyArray &data, StatementTag statement_tag=NULL) override
Create BIND array to receive C++ data.
ExchangeDataType exchangeType(const boost::any &object)
Determine exchange type based on boost::any type.
const CassDataType * cass_data_type_
Internal Cassandra driver object representing a Cassandra data type.
Definition: cql_exchange.h:83
Structure used to bind C++ input values to dynamic CQL parameters.
Definition: cql_exchange.h:50
StatementTag name_
Short description of the query.
const CassPrepared * prepared_statement_
Internal Cassandra object representing the prepared statement.
std::unordered_map< std::type_index, ExchangeDataType > AnyTypeMap
Map types used to determine exchange type.
Definition: cql_exchange.cc:81
bool force_consistency_
CQL consistency enabled.
static constexpr StatementTag GET_VERSION
Statement tags definitions.
Definition: cql_exchange.h:276
virtual ~CqlVersionExchange()
Destructor.
static void getData(const CassRow *row, AnyArray &data)
Retrieves data returned by Cassandra.
const CqlConnection & connection_
Connection to the Cassandra database.
Definition: cql_exchange.h:76
std::unordered_map< ExchangeDataType, CqlFunction, ExchangeDataTypeHash > CqlFunctionMap
Defines a type for storing aux. Cassandra functions.
Definition: cql_exchange.cc:62
static const time_t MAX_DB_TIME
Defines maximum value for time that can be reliably stored.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
CqlFunctionMap CQL_FUNCTIONS
Functions used to interface with the Cassandra C++ driver.
char const *const StatementTag
Statement index representing the statement name.
const CassKeyspaceMeta * keyspace_meta_
Keyspace meta information, used for UDTs.
bool statementApplied(CassFuture *future, size_t *row_count=NULL, size_t *column_count=NULL)
Check if CQL statement has been applied.
Multiple lease records found where one expected.
Definition: db_exceptions.h:28
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
CassUserType * cass_user_type_
Internal Cassandra driver object representing a user defined type.
Definition: cql_exchange.h:86
Defines a single statement or query.
void add(const boost::any &value)
Add a value at the end of the vector.
static void convertToDatabaseTime(const time_t &cltt, const uint32_t &valid_lifetime, cass_int64_t &expire)
CassConsistency consistency_
CQL consistency.
virtual boost::any retrieve() override
Copy received data into the <version,minor> pair.
std::vector< cass_byte_t > CassBlob
Host identifier converted to Cassandra data type.
Definition: cql_exchange.h:37
CassSession * session_
CQL session handle.
size_t operator()(const ExchangeDataType &key) const
Definition: cql_exchange.cc:55
std::pair< uint32_t, uint32_t > VersionPair
Pair containing major and minor versions.
ExchangeDataType
Used to map server data types with internal backend storage data types.
Definition: sql_common.h:26
Udt(const CqlConnection &connection, const std::string &name)
Parameterized constructor.
const std::string name_
Name of the UDT in the schema: CREATE TYPE ___ { ... }.
Definition: cql_exchange.h:79
Collection (used in Cassandra)
Definition: sql_common.h:38
Defines the logger used by the top-level component of kea-dhcp-ddns.
static void convertFromDatabaseTime(const cass_int64_t &expire, const cass_int64_t &valid_lifetime, time_t &cltt)
Converts time from Cassandra format.
std::unordered_map< uint8_t, ExchangeDataType > CassTypeMap
Definition: cql_exchange.cc:85
const Name & name_
Definition: dns/message.cc:693
static void bindData(const AnyArray &data, CassStatement *statement)
Assigns values to every column of an INSERT or an UPDATE statement.
std::unordered_map< StatementTag, CqlTaggedStatement, StatementTagHash, StatementTagEqual > StatementMap
A container for all statements.
AnyArray executeSelect(const CqlConnection &connection, const AnyArray &where_values, StatementTag statement_tag, const bool &single=false)
Executes SELECT statements.
void remove(const size_t &index)
Remove the void pointer to the data value from a specified position inside the vector.
a helper structure with a function call operator that returns key value in a format expected by std::...
Definition: cql_exchange.cc:53
virtual boost::any retrieve()=0
Copy received data into the derived class' object.
virtual ~CqlExchange()
Destructor.
std::size_t hash_value(const CassValueType &key)
hash function for CassTypeMap
Definition: cql_exchange.cc:73
static StatementMap tagged_statements_
Cassandra statements.
Definition: cql_exchange.h:280
Common CQL connector pool.
static const std::string checkFutureError(const std::string &what, CassFuture *future, StatementTag statement_tag=NULL)
Check for errors.
~Udt()
Destructor.
User-Defined Type (used in Cassandra)
Definition: sql_common.h:37
virtual VersionPair retrieveVersion(const CqlConnection &connection)
Standalone method used to retrieve schema version.
Exception thrown on failure to execute a database function.
bool is_raw_
Should the statement be executed raw or with binds?
CqlExchange()
Constructor.
CqlVersionExchange()
Constructor.
AnyArray AnyCollection
Defines an array of arbitrary objects (used by Cassandra backend)
Definition: cql_exchange.h:90
virtual void createBindForSelect(AnyArray &data, StatementTag statement_tag=NULL)=0
Create BIND array to receive C++ data.