DataStax C/C++ Driver: 2.1 Beta released!

By Michael Penick -  July 7, 2015 | 0 Comments

We are pleased to announce the 2.1 beta release of the C/C++ driver for Apache Cassandra and DataStax Enterprise. This release includes a majority of client-side features required to take advantage of Apache Cassandra 2.1 and DataStax Enterprise 4.7. These features include support for user defined types, tuples, nested (frozen) collections and named parameters. The final 2.1 release will also include client-side timestamps, support for the full range of stream IDs available in CQL native protocol version 3 and retry polices.

What’s new

User defined types

User defined types (UDTs for short), introduced in Apache Cassandra 2.1, allow for creating composite data types with multiple fields in a single column. This can be useful for simplifying schema by grouping related fields into a single UDT instead of using multiple columns. More information about using UDTs can be found in this post.

A UDT constructed from a data type definition found in schema metadata

  int rc;

  const CassSchema* schema;
  const CassDataType* data_type1;

  CassStatement* statement;
  CassUserType* user_type1;
  CassFuture* future;

  const char* query = "INSERT INTO keyspace1.table1 (key, value) VALUES (?, ?)";

  /* Schema and type information is usually pretty static, therefore these
   * objects should be retreived sporadically and cached */
  schema = cass_session_get_schema(session);
  data_type1 = cass_schema_get_udt(schema, "keyspace1", "type1");

  statement = cass_statement_new(query, 2);

  /* The user type is created using the data type description found in the
   * Cassandra schema metadata */
  user_type1 = cass_user_type_new_from_data_type(data_type1);

  /* Values can be bound to a UDT by name (as well as by position) */
  cass_user_type_set_string_by_name(user_type1, "field1", "abc");
  cass_user_type_set_int32_by_name(user_type1, "field2", 123);

  /* Bind the parameters to the query */
  cass_statement_bind_string(statement, 0, "key1");
  cass_statement_bind_user_type(statement, 1, user_type1);

  future = cass_session_execute(session, statement);

  rc = cass_future_error_code(future);
  if (rc != CASS_OK) {
    /* Handle error */
  }

  /* Clean up */
  cass_schema_free(schema);
  cass_statement_free(statement);
  cass_user_type_free(user_type1);
  cass_future_free(future);

A UDT constructed from a manually created data type definition

  int rc;

  CassDataType* data_type1; /* Notice: not const */
  CassStatement* statement;
  CassUserType* user_type1;
  CassFuture* future;

  const char* query = "INSERT INTO keyspace1.table1 (key, value) VALUES (?, ?)";

  /* Manually create a data type that describes the UDT.
   * This should be created once and cached. */
  data_type1 = cass_data_type_new(CASS_VALUE_TYPE_UDT);
  cass_data_type_add_sub_value_type_by_name(data_type1, "field1", CASS_VALUE_TYPE_TEXT);
  cass_data_type_add_sub_value_type_by_name(data_type1, "field2", CASS_VALUE_TYPE_INT);

  statement = cass_statement_new(query, 2);

  /* The user type is created using the data type description built previously */
  user_type1 = cass_user_type_new_from_data_type(data_type1);

  /* Values can be bound to a UDT by name (as well as by position) */
  cass_user_type_set_string_by_name(user_type1, "field1", "def");
  cass_user_type_set_int32_by_name(user_type1, "field2", 456);

  /* Bind the parameters to the query */
  cass_statement_bind_string(statement, 0, "key2");
  cass_statement_bind_user_type(statement, 1, user_type1);

  future = cass_session_execute(session, statement);

  rc = cass_future_error_code(future);
  if (rc != CASS_OK) {
    /* Handle error */
  }

  /* Clean up */
  cass_data_type_free(data_type1);
  cass_statement_free(statement);
  cass_user_type_free(user_type1);
  cass_future_free(future);

Selecting a UDT value and iterating over its fields

  CassStatement* statement;
  CassFuture* future;
  const CassResult* result;

  const char* query = "SELECT value FROM keyspace1.table1";

  statement = cass_statement_new(query, 0);
  future = cass_session_execute(session, statement);

  result = cass_future_get_result(future);
  if (result != NULL && cass_result_row_count(result) > 0) {
    const CassRow* row = cass_result_first_row(result);

    /* Create an iterator to iterate over the UDT's fields */
    CassIterator* fields_iterator =
      cass_iterator_from_user_type(cass_row_get_column_by_name(row, "value"));

    while (cass_iterator_next(fields_iterator)) {
      const char* field_name;
      size_t field_name_size;

      /* Get the field's name */
      cass_iterator_get_user_type_field_name(fields_iterator,
                                             &field_name, &field_name_size);

      /* Get the field's value */
      if (strncmp(field_name, "field1", field_name_size) == 0) {
        const char* field1_value;
        size_t field1_value_size;
        cass_value_get_string(cass_iterator_get_user_type_field_value(fields_iterator),
                              &field1_value, &field1_value_size);
        /* Use field1's value */
        printf("%.*s %.*s\n", (int)field_name_size, field_name,
                              (int)field1_value_size, field1_value);
      } else if (strncmp(field_name, "field2", field_name_size) == 0) {
        cass_int32_t field2_value;
        cass_value_get_int32(cass_iterator_get_user_type_field_value(fields_iterator),
                             &field2_value);
        /* Use field2's value */
        printf("%.*s %d\n", (int)field_name_size, field_name,
                            field2_value);
      }
    }

    cass_iterator_free(fields_iterator);
  } else {
    /* Handle error */
  }

  cass_statement_free(statement);
  cass_future_free(future);

UDT schema

CREATE KEYSPACE keyspace1 
WITH replication = { 'class': 'SimpleStrategy', 'replication_factor': '3' };

CREATE TYPE keyspace1.type1 (field1 text, field2 int);

CREATE TABLE keyspace1.table1 (key text PRIMARY KEY, value frozen<type1>)

Tuples

Tuples, also introduced in Apache Cassandra 2.1, are useful for creating positional, fixed length sets of multiple types. They’re similar to UDTs in that they are arbitrary composite types, however; tuple fields are unnamed, therefore its fields can only be referenced by position. This also means that it is not possible to add new fields to a tuple.

Inserting a tuple value

  int rc;

  CassStatement* statement;
  CassTuple* tuple;
  CassFuture* future;

  const char* query = "INSERT INTO keyspace1.table2 (key, value) VALUES (?, ?)";

  statement = cass_statement_new(query, 2);

  tuple = cass_tuple_new(2);

  /* Values are bound to a tuple by position */
  cass_tuple_set_string(tuple, 0, "abc");
  cass_tuple_set_int32(tuple, 1, 123);

  /* Bind the parameters to the query */
  cass_statement_bind_string(statement, 0, "key1");
  cass_statement_bind_tuple(statement, 1, tuple);

  future = cass_session_execute(session, statement);

  rc = cass_future_error_code(future);
  if (rc != CASS_OK) {
    /* Handle error */
  }

  /* Clean up */
  cass_statement_free(statement);
  cass_tuple_free(tuple);
  cass_future_free(future);

Selecting a tuple value and iterating over its fields

  CassStatement* statement;
  CassFuture* future;
  const CassResult* result;

  const char* query = "SELECT value FROM keyspace1.table2";

  statement = cass_statement_new(query, 0);
  future = cass_session_execute(session, statement);

  result = cass_future_get_result(future);
  if (result != NULL && cass_result_row_count(result) > 0) {
    const CassRow* row = cass_result_first_row(result);

    /* Create an iterator to iterate over the tuple's fields */
    CassIterator* tuple_iterator =
      cass_iterator_from_tuple(cass_row_get_column_by_name(row, "value"));

    while (cass_iterator_next(tuple_iterator)) {
      const CassValue* value = cass_iterator_get_value(tuple_iterator);

      /* Get the tuple field's value */
      if (cass_value_type(value) == CASS_VALUE_TYPE_TEXT) {
        const char* text_value;
        size_t text_value_size;
        cass_value_get_string(value,
                              &text_value, &text_value_size);
        /* Use value */
        printf("%.*s\n", (int)text_value_size, text_value);
      } else if (cass_value_type(value) == CASS_VALUE_TYPE_INT) {
        cass_int32_t int_value;
        cass_value_get_int32(value, &int_value);
        /* Use value */
        printf("%d\n", int_value);
      }
    }

    cass_iterator_free(tuple_iterator);
  } else {
    /* Handle error */
  }

  cass_statement_free(statement);
  cass_future_free(future);

Tuple schema

CREATE TABLE keyspace1.table2 (key text PRIMARY KEY, value frozen<tuple<text, int>>);

Nested collections

With the release of Apache Cassandra 2.1 it is now possible to nest immutable (known as “frozen”) collections within other collections. To support this feature the driver added functions and internal serialization logic to allow appending collections inside other collections.

A new method has been added for nesting collections

  /* A nested collection can be appended to another collection */
  cass_collection_append_collection(collection, nested_collection);

Collection values can now be recursively iterated

  /* A nested collection can be retreived from another collection */
  const CassValue* nested_collection = cass_iterator_get_value(collection);
  CassIterator* nested_collection_iterator = cass_iterator_from_collection(nested_collection);

  /* Iterate over nested collection */

Named parameters

It is possible to name parameters inside a query string. In previous releases only positional parameters were supported for non-prepared queries, that is, parameters denoted with “?” needed to be bound to a query in the same order as they appeared in the query string. This version of the driver allows parameters to be named using the “:<name>” syntax. Named parameters can also be used in conjunction with prepared queries, but are most useful for non-prepared queries where metadata for the parameters’ names are not available.

  int rc;

  CassStatement* statement;
  CassTuple* tuple;
  CassFuture* future;

  /* The query string uses the form ":&lt;name&gt;" of parameters instead of "?" */
  const char* query = "INSERT INTO keyspace1.table2 (key, value) VALUES (:k, :v)";

  statement = cass_statement_new(query, 2);

  tuple = cass_tuple_new(2);

  /* Values are bound to a tuple by position */
  cass_tuple_set_string(tuple, 0, "def");
  cass_tuple_set_int32(tuple, 1, 456);

  /* Bind the parameters to the query using names */
  cass_statement_bind_string_by_name(statement, "k", "key2");
  cass_statement_bind_tuple_by_name(statement, "v", tuple);

  future = cass_session_execute(session, statement);

  rc = cass_future_error_code(future);
  if (rc != CASS_OK) {
    /* Handle error */
  }

  /* Clean up */
  cass_statement_free(statement);
  cass_tuple_free(tuple);
  cass_future_free(future);

Internal improvements

This release includes a couple internal improvements:

  • The driver now supports version 3 of the CQL native protocol allowing the driver to support new client-side features as well as supporting larger range of stream IDs. The larger range of stream IDs will allow the driver to achieve higher query throughput with less connections. The next driver release will fully capitalize on this potential increase in performance.
  • Read buffers are now cached and reused inside a connection. This is a big performance win on Windows and has the potential to improve performance on other platforms too. More information can be found in the JIRA issue.

What’s next

In the next release we plan to finish Apache Cassandra 2.1 support and address any feedback or issues introduced by this beta release. Let us know what you think of the new features and API. To provide feedback use the following:



Comments

Your email address will not be published. Required fields are marked *




Subscribe for newsletter:

Tel. +1 (408) 933-3120 sales@datastax.com Offices France Germany

DataStax Enterprise is powered by the best distribution of Apache Cassandra™.

© 2017 DataStax, All Rights Reserved. DataStax, Titan, and TitanDB are registered trademark of DataStax, Inc. and its subsidiaries in the United States and/or other countries.
Apache Cassandra, Apache, Tomcat, Lucene, Solr, Hadoop, Spark, TinkerPop, and Cassandra are trademarks of the Apache Software Foundation or its subsidiaries in Canada, the United States and/or other countries.