c/c++ driver 2.2 for apache cassandra · 2016-10-16 · new features in 2.1 • tuples and...

60
C/C++ Driver 2.2 for Apache Cassandra Documentation February 9, 2016 © 2016 DataStax, Inc. All rights reserved.

Upload: others

Post on 31-May-2020

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

C/C++ Driver 2.2 for Apache CassandraDocumentation

February 9, 2016

© 2016 DataStax, Inc. All rights reserved.

Page 2: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Contents

2

Contents

About the C/C++ driver................................................................................................... 4

Architecture...................................................................................................................... 5The driver and its dependencies........................................................................................................ 5Architecture..........................................................................................................................................8

Writing your first client................................................................................................... 9Connecting to a Cassandra cluster.....................................................................................................9Executing CQL statements................................................................................................................14

Reference........................................................................................................................ 26Basics................................................................................................................................................ 26

BATCH statements................................................................................................................. 26Futures.................................................................................................................................... 27Handling results...................................................................................................................... 28Keyspaces...............................................................................................................................31Prepared statements.............................................................................................................. 31Schema metadata API............................................................................................................34User-defined function's and user-defined aggregate's metadata........................................... 35

Client configuration............................................................................................................................36Client-side timestamps...................................................................................................................... 37Connection heartbeats...................................................................................................................... 37CQL data types to C/C++ types....................................................................................................... 38Enabling and disabling schema metadata........................................................................................ 44Latency-aware routing....................................................................................................................... 44Logging.............................................................................................................................................. 45Named parameters............................................................................................................................45Nested collections............................................................................................................................. 46Performance metrics......................................................................................................................... 47Retry policies..................................................................................................................................... 47Security.............................................................................................................................................. 49

SSL......................................................................................................................................... 50Tuples................................................................................................................................................ 54User-defined types............................................................................................................................ 55Whitelist load-balancing policy.......................................................................................................... 58

FAQ..................................................................................................................................59Which versions of Cassandra does the driver support?................................................................... 59Which versions of CQL does the driver support?.............................................................................59How do I generate a uuid or a timebased uuid?..............................................................................59Should I create one client instance per module in my application?..................................................59Should I shut down the pool after executing a query?..................................................................... 59

API reference..................................................................................................................59

Page 3: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Contents

3

Using the docs...............................................................................................................59

Page 4: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

About the C/C++ driver

4

About the C/C++ driverUse this driver in production applications to pass CQL statements from the client to a cluster and retrieve, manipulate, or remove data.

The C/C++ driver is a modern, feature-rich and highly tunable client library for Apache Cassandra (1.2+)and DataStax Enterprise (3.1+) using exclusively Cassandra's binary protocol and Cassandra QueryLanguage version 3.

Use this driver in production applications to pass CQL statements from the client to a cluster andretrieve, manipulate, or remove data. Cassandra Query Language (CQL) is the primary language forcommunicating with the Cassandra database. Documentation for CQL is available in CQL for Cassandra2.x. DataStax also provides DataStax DevCenter, which is a free graphical tool for creating and runningCQL statements against Apache Cassandra and DataStax Enterprise. Other administrative tasks can beaccomplished using OpsCenter.

What's new in 2.2?• New datatypes introduced

• date

• smallint

• time

• tinyint

• User-defined functions and user-defined aggregates (UDF and UDA)• Whitelist load-balancing policy• Schema metadata API• The default consistency for CQL statement is now LOCAL_QUORUM instead of ONE• Improved the performance of to and from string conversion in UUID functions• Server-side warnings that are logged at the CASS_LOG_WARN level• Custom payloads

New features in 2.1• Tuples and user-defined types (UDT)• Nested collections• Retry policies• Client-side timestamps• Datatypes• Idle connection heartbeats• Enabling or disabling schema metadata• Named parameters when using (non-prepared) simple queries• Protocol version 3 (Cassandra 2.1)

New features in 2.0Here are the new and noteworthy features of the 2.2 driver.

• Latency-aware routing• Performance metrics• Removal of CassString, CassBytes, and CassDecimal types• Removal of Boost library dependency on page 8

Page 5: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Architecture

5

ArchitectureAn overview of the C/C++ architecture.

The C/C++ Driver 2.2 for Apache Cassandra works exclusively with the Cassandra Query Language (CQL)version 3 and Cassandra's binary protocol which was introduced in Cassandra version 1.2.

Architectural overviewThe driver architecture is a layered one. At the bottom lies the driver core. This core handles everythingrelated to the connections to a Cassandra cluster (for example, connection pool, discovering new nodes,etc.) and exposes a simple, relatively low-level API on top of which a higher level layer can be built.

The driver has the following features:

• Asynchronous: the driver uses the new CQL binary protocol asynchronous capabilities. Only a relativelylow number of connections per nodes needs to be maintained open to achieve good performance.

• Cassandra trace handling: tracing can be set on a per-query basis and the driver provides a convenientAPI to retrieve the trace.

• Configurable load balancing: the driver allows for custom routing and load balancing of queries toCassandra nodes. Out of the box, round robin is provided with optional data-center awareness (onlynodes from the local data-center are queried (and have connections maintained to)) and optional tokenawareness (that is, the ability to prefer a replica for the query as coordinator).

• Configurable retry policy: a retry policy can be set to define a precise behavior to adopt on queryexecution exceptions (for example, timeouts, unavailability). This avoids polluting client code with retry-related code.

• Convenient schema access: the driver exposes a Cassandra schema in a usable way.• Node discovery: the driver automatically discovers and uses all nodes of the Cassandra cluster,

including newly bootstrapped ones.• Paging: both automatic and manual.• SSL support• Transparent failover: if Cassandra nodes fail or become unreachable, the driver automatically and

transparently tries other nodes and schedules reconnection to the dead nodes in the background.• Tunability: the default behavior of the driver can be changed or fine tuned by using tuning policies and

connection options.

The driver and its dependenciesThe C/C++ driver only supports the Cassandra Binary Protocol and CQL3

Cassandra binary protocolThe driver uses the binary protocol that was introduced in Cassandra 1.2. It only works with a version ofCassandra greater than or equal to 1.2. Furthermore, the binary protocol server is not started with thedefault configuration file in Cassandra 1.2. You must edit the cassandra.yaml file for each node:

start_native_transport: true

Then restart the node.

Cassandra compatibilityThe driver is compatible with any Cassandra version from 1.2. The driver uses native protocol 1 (forCassandra 1.2) and 2 (Cassandra 2.0+).

Page 6: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Architecture

6

Building the driverSupported operating systems

• Mac OS X 10.8 (Mavericks) and 10.9 (Yosemite)• Windows 7 SP1• CentOS/RHEL 5, 6, and 7• Ubuntu 12.04 and14.04 LTS

Build dependencies

• Libraries

• libuv 1.x or 0.10.x• OpenSSL (optional)

• Tools

• CMake• GCC 4.1.2+, Clang 3.4+, or MSVC 2010, 2012, or 2013

Note: Utilizing the default package manager configuration to install dependencies on Unix-basedoperating systems may result in older versions of dependencies being installed.

Testing dependencies

• Boost 1.55+• libssh2

Build dependencies under LinuxThe driver has been built using both Clang (Ubuntu 12.04 or 14.04 LTS and Mac OS X) and GCC 4.1.2+(Linux).

1. (Optional CentOS or RHEL 5.

CentOS and RHEL 5 do not contain git in its repositories; however RepoForge (formerly RPMforge)has a RPM for this dependency.

a. Download the appropriate RepoForge release package:

• 32-bit• 64-bit

b. Install key and RPM package:

$ sudo rpm --import http://apt.sw.be/RPM-GPG-KEY.dag.txt$ sudo rpm -i rpmforge-release-0.5.3-1.el5.rf.*.rpm

2. Install dependencies and libuv library:

$ sudo yum install automake cmake gcc-c++ git libtool openssl-devel wget$ pushd /tmp$ wget http://libuv.org/dist/v1.4.2/libuv-v1.4.2.tar.gz$ tar xzf libuv-v1.4.2.tar.gz$ pushd libuv-v1.4.2$ sh autogen.sh$ ./configure$ sudo make install$ popd$ popd

Additional dependencies under Ubuntu 12.04

Ubuntu 12.04 does not contain libuv in its repositories; however the LinuxJedi PPA has a backport fromUbuntu 14.04.

Page 7: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Architecture

7

$ sudo apt-add-repository ppa:linuxjedi/ppa$ sudo apt-get update

GCC

$ sudo apt-get install g++ make cmake libuv-dev libssl-dev

Clang

$ sudo apt-get install clang make cmake libuv-dev libssl-dev

Build dependencies under Mac OS XThe driver has been built and tested using the Clang compiler provided by XCode 5.1+. The dependencieswere obtained using Homebrew.

$ brew install libuv cmake

Note: The driver utilizes the OpenSSL library included with XCode.

Building the driver under Linux or Mac OS X

$ git clone https://github.com/datastax/cpp-driver.git$ mkdir cpp-driver/build$ cd cpp-driver/build$ cmake ..$ make

Building the driver under WindowsThe driver has been built and tested using Microsoft Visual Studio 2010, 2012 and 2013 (using both theExpress and Professional versions) and Windows SDK 7.1, 8.0, and 8.1 on Windows 7 SP1. The librarydependencies are automatically downloaded and built, however the following build dependencies need tobe installed.

1. Download and install CMake.

Make sure to select the option Add CMake to the system PATH for all users or Add CMake to thesystem PATH for current user.

2. Download and install git.

Make sure to select the option Use Git from Windows Command Prompt or manually add the gitexecutable to the system PATH.

3. Download and install ActiveState Perl.

Make sure to select the option Add Perl to PATH environment variable.

Note: This build dependency is required if building with OpenSSL support.4. Download and install Python 2.7.x.

Make sure to select and install the feature Add python.exe to Path.5. A batch script has been created to detect installed versions of Visual Studio (and/or Windows SDK

installations) to simplify the build process on Windows. If you have more than one version of VisualStudio (and/or Windows SDK) installed you will be prompted to select which version to use whencompiling the driver.

First you will need to open a Command Prompt (or Windows SDK Command Prompt) to execute thevc_build.bat batch script.

Usage: VC_BUILD.BAT [OPTION...]

--DEBUG Enable debug build

Page 8: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Architecture

8

--RELEASE Enable release build (default) --DISABLE-CLEAN Disable clean build --TARGET-COMPILER [version] 120, 110, 100, or WINSDK --DISABLE-OPENSSL Disable OpenSSL support --ENABLE-EXAMPLES Enable example builds --ENABLE-PACKAGES [version] Enable package generation (*) --ENABLE-TESTS [boost-root-dir] Enable test builds --ENABLE-ZLIB Enable zlib --GENERATE-SOLUTION Generate Visual Studio solution (**) --INSTALL-DIR [install-dir] Override installation directory --SHARED Build shared library (default) --STATIC Build static library --X86 Target 32-bit build (***) --X64 Target 64-bit build (***) --USE-BOOST-ATOMIC Use Boost atomic

--HELP Display this message

* Packages are only generated using detected installations of Visual Studio** Dependencies are built before generation of Visual Studio solution*** Default target architecture is determined based on system architecture

To use the vc_build.bat script for easy inclusion into a project:

dos VC_BUILD.BAT --TARGET-COMPILER 120 --INSTALL-DIR C:\myproject\dependencies\libs\cpp-driver

Removal of Boost library dependencyBoost was an internal dependency in previous versions of the driver. This likely wasn’t even known tomost users of the driver because the required subset of the Boost source code was included in the driveritself. However, for some users this caused serious build issues because their applications also had adependency on an older, but incompatible version of Boost. Instead of making Boost a required externaldependency we’ve instead removed our dependency on Boost. There is still one external, but optionalBoost dependency.

Before version 2.0, the driver depended on Boost’s implementation for lock-free and atomic operations.Moving forwarded the driver will allow for three choices for the implementation of atomic operations:std::atomic, a built-in intrinsics implementation and Boost Atomic. The built-in implementation workson all supported platforms, but can be slower than using the optimized versions found in std::atomic orBoost Atomic. On compilers that support the C++11 memory model the std::atomic implementation isautomatically enabled, however; on older compilers and systems, to achieve maximum performance, it isrecommended that the Boost Atomic implementation be used.

The std::atomic implementation can also be explicitly enabled:

cmake -DCASS_USE_STD_ATOMIC ...

When Boost is available (on older per-C++11 compilers) its Atomics library can be used:

cmake -DCASS_USE_BOOST_ATOMIC ...

Note: Both of these implementations are entirely optional and the driver will work and perform well withoutthem, but to achieve maximum performance they’re recommended.

ArchitectureAn overview of the C/C++ architecture.

Page 9: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Writing your first client

9

ClusterThe CassCluster object represents your cluster’s configuration. The default cluster object is good formost clusters and only a list of contact points needs to be configured. Once a session is connected usinga cluster object its configuration is constant. Modify the cluster object configuration after connecting asession doesn't change the session's configuration.

SessionA session object is used to execute queries. Internally, it also manages a pool of client connections to thecluster and uses a load-balancing policy to distribute requests across those connections. Your applicationshould only create a single session object per keyspace because a session object is designed to becreated once, reused, and shared by multiple application threads. The throughput of a session can bescaled by increasing the number of I/O threads. An I/O thread is used to handle reading and writing queryrequest data to and from Cassandra. The number of I/O threads defaults to one per CPU core, but it canbe configured using cass_cluster_set_num_threads_io(). It’s generally better to create a singlesession with more I/O threads than multiple sessions with a smaller number of I/O threads.

Thread safetyCassSession is designed to be used concurrently from multiple threads. It is best practice to create asingle session per keyspace. CassFuture is also thread safe. Other than these exceptions, in general,functions that modify an object's state are not thread safe. Objects that are immutable (marked const)can be read safely by multiple threads. None of the free functions can be called concurrently on the sameinstance of an object.

Memory handlingValues such as CassString and CassBytes point to memory held by the result object. The lifetimesof those values are valid as long as the result object is not freed. These values need to be copied intoapplication memory if they need to live longer than the result object’s lifetime. Primitive types suchas cass_int32_t are copied by the driver because it can be done cheaply without incurring extraallocations.

Moving an iterator to the next value invalidates the value it previously returned.

Writing your first clientThis section walks you through creating a client application that uses the C/C++ driver to connect to a Cassandra cluster, create a schema, load some data, and execute some queries.

Connecting to a Cassandra clusterThe C/C++ driver provides functions for connecting to a Cassandra cluster.

PrerequisitesThis tutorial uses the following software:

• An installed and running Cassandra or DSE cluster• DataStax C/C++ driver• A C/C++ compiler (e.g., gcc)• A build tool (e.g., make)

In this tutorial, you create a client application project consisting of four files:

• SimpleClient.hpp (the header file for the SimpleClient class)

Page 10: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Writing your first client

10

• SimpleClient.cpp (the implementation for the SimpleClient class)• simple.cpp (a C++ program that has a main function)• Makefile (the make file that contains the directives to build the simple application)

Procedure1. Create a directory to contain your project.

$ mkdir simple$ cd simple

2. Using a text editor, create the SimpleClient.hpp file.

a) Add include directives for the driver and standard string header files.

#include "cassandra.h"#include <string>

b) After the include directives, declare a SimpleClient class within an examples namespace:

namespace example {

class SimpleClient {

} // end class} // end namespace

c) Add two private member fields to the class to store references to a cluster and a session object.

class SimpleClient {private: CassCluster* cluster; CassSession* session;} // end class

d) Now add three public member functions: an inline getter for the session object, a connect(), and aclose() function.

class SimpleClient {private: CassCluster* cluster; CassSession* session;public: inline CassSession* getSession() { return session; } CassError connect(const std::string nodes); void close();} // end class

3. Using a text editor, create the SimpleClient.cpp file.

4. Implement the SimpleClient::connect() function in the SimpleClient.cpp file.

a) Add include directives for the iostream and the SimpleClient header files.

Page 11: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Writing your first client

11

#include <iostream>#include "SimpleClient"

b) Declare the examples namespace.

namespace example {using namespace std;

} // end namespace

c) Declare the connect() function.

namespace example{

CassError SimpleClient::connect(const string nodes) {}} // end namespace

d) Add code to return the CassError code to the calling function and log some helpful information:

CassError SimpleClient::connect(const string nodes) { CassError rc = CASS_OK; cout << "Connecting to " << nodes << "\n";

return rc;}

e) Create new cluster and session objects and store references to them in the class' appropriateprivate member fields.

CassError SimpleClient::connect(const string nodes) { CassError rc = CASS_OK; cout << "Connecting to " << nodes << "\n"; cluster = cass_cluster_new(); session = cass_session_new();

return rc;}

You only need one cluster and one session object per client application. The driver library takes careof managing connections and providing load balancing.

f) Set the contact points (of one or more of the nodes in your cluster) and connect to the cluster.

CassError SimpleClient::connect(const string nodes) { // earlier code elided CassFuture* connect_future = NULL; cass_cluster_set_contact_points(cluster, "127.0.0.1"); connect_future = cass_session_connect(session, cluster); cass_future_wait(connect_future);

return rc;}

Page 12: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Writing your first client

12

} // end namespace

The cass_session_connect() function returns a future object. In this example, we merely waitfor the asynchronous call to return, but in your code, you could do other tasks while waiting for acallback from the future.

g) Retrieve the error code and log information appropriate to its value.

CassError SimpleClient::connect(const string nodes) { // earlier code elided rc = cass_future_error_code(connect_future); if ( rc == CASS_OK ) { cout << "Connected." << "\n"; } else { cout << cass_error_desc(rc) << "\n"; return rc; } return rc;}

} // end namespace

h) Free up the future object and return the resulting error code.

CassError SimpleClient::connect(const string nodes) { // earlier code elided cass_future_free(connect_future);

return rc;}

} // end namespace

5. Implement the SimpleClient::close() function in the SimpleClient.cpp file.

void SimpleClient::close() { cout << "Closing down cluster connection." << "\n"; cass_session_close(session); cass_cluster_free(cluster);}

Before exiting your client application, close down the session you created and free up the memoryallocated for the cluster object.

Code listingSimpleClient.hpp

#ifndef __SIMPLE_CLIENT_H__#define __SIMPLE_CLIENT_H__

#include "cassandra.h"

Page 13: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Writing your first client

13

#include <string>

namespace example {

class SimpleClient {private: CassCluster* cluster; CassSession* session;public: inline CassSession* getSession() { return session; } CassError connect(const std::string nodes); void close();} // end class

} // end namespace

#endif

Code listingSimpleClient.cpp

#include <iostream>#include "SimpleClient.hpp"

namespace example {using namespace std;

CassError SimpleClient::connect(const string nodes) { CassError rc = CASS_OK; cout << "Connecting to " << nodes << "\n";

cluster = cass_cluster_new(); session = cass_session_new();

CassFuture* connect_future = NULL; cass_cluster_set_contact_points(cluster, "127.0.0.1"); connect_future = cass_session_connect(session, cluster); cass_future_wait(connect_future);

rc = cass_future_error_code(connect_future); if ( rc == CASS_OK ) { cout << "Connected." << "\n"; } else { cout << cass_error_desc(rc) << "\n"; return rc; } cass_future_free(connect_future); return rc;}

void SimpleClient::close() { cout << "Closing down cluster connection." << "\n"; cass_session_close(session);

Page 14: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Writing your first client

14

cass_cluster_free(cluster);}

} // end namespace

Code listingsimple.cpp

#include <iostream>

#include "SimpleClient.hpp"

int main(int argc, char**){ using example::SimpleClient; SimpleClient client; client.connect("127.0.0.1"); return 0;}

Code listingMakefile

OBJS = simple.o bound.o SimpleClient.o

CC = g++DRIVER_DIR = path-to/cpp-driver-library

DEBUG = -gCPPFLAGS = -Wall -c $(DEBUG) -I$(DRIVER_DIR)/includeLFLAGS = -Wall $(DEBUG)

LIBS = -L$(DRIVER_DIR) -luv -lssh2 -lcassandra

all: simple

simple: SimpleClient.o simple.o $(CC) $(LFLAGS) -o simple SimpleClient.o simple.o $(LIBS)

clean: rm $(OBJS) simple

Executing CQL statementsOnce you have connected to a Cassandra cluster using a Session object, you execute CQL statements to read and write data.

PrerequisitesThis tutorial uses a CQL schema which is described in a post on the DataStax developer blog. Readingthat post, could help with some of the CQL concepts used here.

Page 15: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Writing your first client

15

After connecting to a cluster and creating a Session object, you can execute CQL statements. In thistutorial, you will add code to your client for:

• printing out errors• executing statements

• creating a keyspace• creating tables• inserting data into those tables• querying the tables• printing the results

Procedure1. Declare and implement an inline function, printError():

inline CassError printError(CassError error) { cout << cass_error_desc(error) << "\n"; return error;}

2. Declare and implement an inline function, executeStatement():

a) Declare the function in the class declaration in the header file (SimpleClient.hpp):

class SimpleClient {

private: CassSession* session; CassCluster* cluster; inline CassError executeStatement(const char* cqlStatement, const CassResult** results /* = NULL */);

b) Declare the function in your implementation file (SimpleClient.cpp) and add code to hold theresulting error code and return it to the calling function and log some information to the user in theconsole:

inline CassError executeStatement(const char* cqlStatement, const CassResult** results /* = NULL */){ CassError rc = CASS_OK; CassFuture* result_future = NULL; cout << "Executing " << cqlStatement << "\n";

return rc;}

c) After the logging statement, add code to create a statement from the specified query string andexecute it on the session object:

inline CassError executeStatement(string cqlStatement, const CassResult** results /* = NULL */){ // Some code elided

Page 16: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Writing your first client

16

cout << "Executing " << cqlStatement << "\n";

CassStatement* statement = cass_statement_new(cqlStatement.c_str(), 0); result_future = cass_session_execute(session, statement);

return rc;}

The driver is designed so that no function forces your application to block. Functions that wouldnormally cause your application to block, such as connecting to a cluster or running a query, insteadreturn a CassFuture object that can be waited on, polled, or used to register a callback. The driverAPI can also be used synchronously by immediately attempting to get the result from a future.

d) Add code that waits on the CassFuture object to complete:

inline CassError executeStatement(const char* cqlStatement, const CassResult** results /* = NULL */){ // Some code elided

result_future = cass_session_execute(session, statement); cass_future_wait(result_future);

return rc;}

e) Add code to retrieve the resulting error code and deal with it (by getting the result if CASS_OK orprinting out the error message if not):

inline CassError executeStatement(const char* cqlStatement, const CassResult** results /* = NULL */){ // Some code elided

cass_future_wait(result_future);

rc = cass_future_error_code(result_future); if (rc == CASS_OK) { cout << "Statement " << cqlStatement << " executed successully." << "\n"; if ( results != NULL ) { *results = cass_future_get_result(result_future); } } else { return printError(rc); }

return rc;}

f) Add code to free up the statement and future objects:

inline CassError executeStatement(const char* cqlStatement, const CassResult** results /* = NULL */){

Page 17: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Writing your first client

17

// Some code elided

cass_statement_free(statement); cass_future_free(result_future);

return rc;}

Write code to create a schema on your cluster, load data into it, and query the data.

3. Declare and implement a function, createSchema(), that creates a keyspace with two tables in it:

a) Add the createSchema() function signature in the header file (SimpleClient.hpp):

class SimpleClient { // Some code elided.

public: CassError createSchema();};

b) Declare the function in your implementation file (SimpleClient.cpp) and add code to hold theresulting error code and return it to the calling function and log some information to the user in theconsole:

CassError SimpleClient::createSchema() { CassError rc = CASS_OK; cout << "Creating simplex keyspace." << endl; return rc;}

c) Add code to create a keyspace and two tables:

CassError SimpleClient::createSchema() { // Some code elided rc = executeStatement( string("CREATE KEYSPACE IF NOT EXISTS simplex ") + "WITH replication = {'class':'SimpleStrategy', 'replication_factor':1};"); rc = executeStatement( string("CREATE TABLE simplex.songs (") + "id uuid PRIMARY KEY," + "title text," + "album text," + "artist text," + "tags set<text>," + "data blob" + ");"); rc = executeStatement( string("CREATE TABLE simplex.playlists (") + "id uuid," + "title text," + "album text, " + "artist text," + "song_id uuid," + "PRIMARY KEY (id, title, album, artist)" +

Page 18: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Writing your first client

18

");"); return rc;}

4. Declare and implement a function, loadData(), that populates the schema with some data:

a) Add the loadData() function signature in the header file (SimpleClient.hpp):

class SimpleClient { // Some code elided.

public: CassError loadData();};

b) Declare the function in your implementation file (SimpleClient.cpp) and add code to hold theresulting error code and return it to the calling function and log some information to the user in theconsole:

CassError SimpleClient::loadData() { CassError rc = CASS_OK; cout << "Loading data into simplex keyspace." << endl; return rc;}

c) Add code to insert some data into the newly created schema:

CassError SimpleClient::loadData() { // Some code elided. rc = executeStatement( string("INSERT INTO simplex.songs (id, title, album, artist, tags) ") + "VALUES (" + "756716f7-2e54-4715-9f00-91dcbea6cf50," + "'La Petite Tonkinoise'," + "'Bye Bye Blackbird'," + "'Joséphine Baker'," + "{'jazz', '2013'})" + ";"); rc = executeStatement( string("INSERT INTO simplex.songs (id, title, album, artist, tags) ") + "VALUES (" + "f6071e72-48ec-4fcb-bf3e-379c8a696488," + "'Die Mösch'," + "'In Gold'," + "'Willi Ostermann'," + "{'kölsch', '1996', 'birds'}" + ");"); rc = executeStatement( string("INSERT INTO simplex.songs (id, title, album, artist, tags) ") + "VALUES (" + "fbdf82ed-0063-4796-9c7c-a3d4f47b4b25," +

Page 19: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Writing your first client

19

"'Memo From Turner'," + "'Performance'," + "'Mick Jager'," + "{'soundtrack', '1991'}" + ");"); rc = executeStatement( string("INSERT INTO simplex.playlists (id, song_id, title, album, artist) ") + "VALUES (" + "2cc9ccb7-6221-4ccb-8387-f22b6a1b354d," + "756716f7-2e54-4715-9f00-91dcbea6cf50," + "'La Petite Tonkinoise'," + "'Bye Bye Blackbird'," + "'Joséphine Baker'" + ");"); rc = executeStatement( string("INSERT INTO simplex.playlists (id, song_id, title, album, artist) ") + "VALUES (" + "2cc9ccb7-6221-4ccb-8387-f22b6a1b354d," + "f6071e72-48ec-4fcb-bf3e-379c8a696488," + "'Die Mösch'," + "'In Gold'," + "'Willi Ostermann'" + ");"); rc = executeStatement( string("INSERT INTO simplex.playlists (id, song_id, title, album, artist) ") + "VALUES (" + "3fd2bedf-a8c8-455a-a462-0cd3a4353c54," + "fbdf82ed-0063-4796-9c7c-a3d4f47b4b25," + "'Memo From Turner'," + "'Performance'," + "'Mick Jager'" + ");");

return rc;}

5. Declare and implement a function, querySchema():

a) Add the querySchema() function signature in the header file (SimpleClient.hpp):

class SimpleClient { // Some code elided.

public: CassError querySchema();};

b) Declare the function in your implementation file (SimpleClient.cpp) and add code to hold theresulting error code and return it to the calling function and log some information to the user in theconsole:

CassError SimpleClient::querySchema(){ CassError rc = CASS_OK; cout << "Querying the simplex.playlists table." << endl;

Page 20: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Writing your first client

20

return rc;}

c) Add code to execute a simple query on one of the tables:

CassError SimpleClient::querySchema(){ // Some code elided.

const CassResult* results; rc = executeStatement( string("SELECT title, artist, album FROM simplex.playlists ") + "WHERE id = 2cc9ccb7-6221-4ccb-8387-f22b6a1b354d;", &results); return rc;}

This time, you pass in a CassResults object to the executeStatement() function that willcontain the results of the query.

d) Add code to get a CassIterator object from the results object and check that the returned errorcode is CASS_OK:

CassError SimpleClient::querySchema(){ // Some code elided.

CassIterator* rows = cass_iterator_from_result(results); if ( rc == CASS_OK ) { } return rc;}

e) Add code to retrieve the data from the result and print it out to the console:

CassError SimpleClient::querySchema(){ // Some code elided.

CassIterator* rows = cass_iterator_from_result(results); if ( rc == CASS_OK ) { while ( cass_iterator_next(rows) ) { const CassRow* row = cass_iterator_get_row(rows); const char* title; const char* artist; const char* album; size_t key_length; cass_value_get_string(cass_row_get_column(row, 0), &title, &key_length); cass_value_get_string(cass_row_get_column(row, 1), &artist, &key_length); cass_value_get_string(cass_row_get_column(row, 2), &album, &key_length);

Page 21: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Writing your first client

21

cout << "title: " << title << ", artist: " << artist << ", album: " << album << "\n"; } } return rc;}

f) Finish the implementation of the function by freeing the result and iterator objects:

CassError SimpleClient::querySchema(){ // Some code elided.

CassIterator* rows = cass_iterator_from_result(results); if ( rc == CASS_OK ) { // Some code elided. cass_result_free(results); cass_iterator_free(rows); } return rc;}

ExampleThe complete code listing illustrates:

• write two inline auxiliary functions• creating a keypsace with two tables• loading data into your new schema• retrieving a row from one of the tables

Code listingSimpleClient.hpp

#ifndef __SIMPLE_CLIENT_H__#define __SIMPLE_CLIENT_H__

#include "cassandra.h"

#include <string>

namespace example {

class SimpleClient {

private: CassSession* session; CassCluster* cluster;

Page 22: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Writing your first client

22

inline CassError executeStatement(std::string cqlStatement, const CassResult** results = NULL);public: inline CassSession* getSession() { return session; } CassError connect(const std::string nodes); CassError createSchema(); virtual CassError loadData(); CassError querySchema(); void close(); SimpleClient() { } ~SimpleClient() { } }; } // end namespace example

#endif

Code listingSimpleClient.cpp

#include <iostream>#include "SimpleClient.hpp"

namespace example {

using namespace std;

const std::string TAGS_COLUMN("tags");const std::string TITLE_COLUMN("title");const std::string ARTIST_COLUMN("artist");const std::string ALBUM_COLUMN("album");

// auxiliary functions

inline CassError printError(CassError error) { cout << cass_error_desc(error) << "\n"; return error;}

inline CassError SimpleClient::executeStatement(string cqlStatement, const CassResult** results /* = NULL */){ CassError rc = CASS_OK; CassFuture* result_future = NULL; cout << "Executing " << cqlStatement << "\n"; CassStatement* statement = cass_statement_new(cqlStatement.c_str(), 0); result_future = cass_session_execute(session, statement); cass_future_wait(result_future); rc = cass_future_error_code(result_future); if (rc == CASS_OK) {

Page 23: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Writing your first client

23

cout << "Statement " << cqlStatement << " executed successully." << "\n"; if ( results != NULL ) { *results = cass_future_get_result(result_future); } } else { return printError(rc); } cass_statement_free(statement); cass_future_free(result_future); return rc;}

CassError SimpleClient::connect(const string nodes) { CassError rc = CASS_OK; cout << "Connecting to " << nodes << "\n"; cluster = cass_cluster_new(); session = cass_session_new(); CassFuture* connect_future = NULL; cass_cluster_set_contact_points(cluster, "127.0.0.1");

connect_future = cass_session_connect(session, cluster); cass_future_wait(connect_future); rc = cass_future_error_code(connect_future);

if ( rc == CASS_OK ) { cout << "Connected." << "\n"; } else { return printError(rc); } cass_future_free(connect_future); return rc;}

CassError SimpleClient::createSchema() { CassError rc = CASS_OK; cout << "Creating simplex keyspace." << endl; rc = executeStatement(string("CREATE KEYSPACE IF NOT EXISTS simplex ") + "WITH replication = {'class':'SimpleStrategy', 'replication_factor':1};"); rc = executeStatement( string("CREATE TABLE simplex.songs (") + "id uuid PRIMARY KEY," + "title text," + "album text," + "artist text," + "tags set<text>," + "data blob" + ");"); rc = executeStatement( string("CREATE TABLE simplex.playlists (") + "id uuid," +

Page 24: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Writing your first client

24

"title text," + "album text, " + "artist text," + "song_id uuid," + "PRIMARY KEY (id, title, album, artist)" + ");"); return rc;}

CassError SimpleClient::loadData() { CassError rc = CASS_OK; cout << "Loading data into simplex keyspace." << endl; rc = executeStatement( string("INSERT INTO simplex.songs (id, title, album, artist, tags) ") + "VALUES (" + "756716f7-2e54-4715-9f00-91dcbea6cf50," + "'La Petite Tonkinoise'," + "'Bye Bye Blackbird'," + "'Joséphine Baker'," + "{'jazz', '2013'})" + ";"); rc = executeStatement( string("INSERT INTO simplex.songs (id, title, album, artist, tags) ") + "VALUES (" + "f6071e72-48ec-4fcb-bf3e-379c8a696488," + "'Die Mösch'," + "'In Gold'," + "'Willi Ostermann'," + "{'kölsch', '1996', 'birds'}" + ");"); rc = executeStatement( string("INSERT INTO simplex.songs (id, title, album, artist, tags) ") + "VALUES (" + "fbdf82ed-0063-4796-9c7c-a3d4f47b4b25," + "'Memo From Turner'," + "'Performance'," + "'Mick Jager'," + "{'soundtrack', '1991'}" + ");"); rc = executeStatement( string("INSERT INTO simplex.playlists (id, song_id, title, album, artist) ") + "VALUES (" + "2cc9ccb7-6221-4ccb-8387-f22b6a1b354d," + "756716f7-2e54-4715-9f00-91dcbea6cf50," + "'La Petite Tonkinoise'," + "'Bye Bye Blackbird'," + "'Joséphine Baker'" + ");"); rc = executeStatement( string("INSERT INTO simplex.playlists (id, song_id, title, album, artist) ") + "VALUES (" + "2cc9ccb7-6221-4ccb-8387-f22b6a1b354d," + "f6071e72-48ec-4fcb-bf3e-379c8a696488," + "'Die Mösch'," +

Page 25: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Writing your first client

25

"'In Gold'," + "'Willi Ostermann'" + ");"); rc = executeStatement( string("INSERT INTO simplex.playlists (id, song_id, title, album, artist) ") + "VALUES (" + "3fd2bedf-a8c8-455a-a462-0cd3a4353c54," + "fbdf82ed-0063-4796-9c7c-a3d4f47b4b25," + "'Memo From Turner'," + "'Performance'," + "'Mick Jager'" + ");"); return rc;}

CassError SimpleClient::querySchema(){ CassError rc = CASS_OK; const CassResult* results; cout << "Querying the simplex.playlists table." << endl; rc = executeStatement( string("SELECT title, artist, album FROM simplex.playlists ") + "WHERE id = 2cc9ccb7-6221-4ccb-8387-f22b6a1b354d;", &results); CassIterator* rows = cass_iterator_from_result(results); if ( rc == CASS_OK ) { while ( cass_iterator_next(rows) ) { const CassRow* row = cass_iterator_get_row(rows); const char* title; const char* artist; const char* album; size_t key_length; cass_value_get_string(cass_row_get_column(row, 0), &title, &key_length); cass_value_get_string(cass_row_get_column(row, 1), &artist, &key_length); cass_value_get_string(cass_row_get_column(row, 2), &album, &key_length); cout << "title: " << title << ", artist: " << artist << ", album: " << album << "\n"; } cass_result_free(results); cass_iterator_free(rows); } return rc;}

void SimpleClient::close() { cout << "Closing down cluster connection." << "\n"; cass_session_close(session); cass_cluster_free(cluster);}

Page 26: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

26

} // end namespace example

ReferenceReference for the C/C++ driver.

BasicsStatements, results sets, futures, and schemas.

BATCH statementsGroup together two or more statements and execute them as though they were a single statement.

Batches are used to group multiple mutations (UPDATE, INSERT, and DELETE) together into a singlestatement. There are three different types of batches supported by Cassandra.

• CASS_BATCH_TYPE_LOGGED is used to make sure that multiple mutations across multiple partitionshappen atomically, that is, all the included mutations will eventually succeed. However, there is aperformance penalty imposed by atomicity guarantee.

• CASS_BATCH_TYPE_UNLOGGED is generally used to group mutations for a single partition and do notsuffer from the performance penalty imposed by logged batches, but there is no atomicity guarantee formulti-partition updates.

• CASS_BATCH_TYPE_COUNTER is used to group counters updates.

Note: Be careful when using batches as a performance optimization.

/* This logged batch will makes sure that all the mutations eventually succeed */CassBatch* batch = cass_batch_new(CASS_BATCH_TYPE_LOGGED);

/* Statements can be immediately freed after being added to the batch */

{ CassStatement* statement = cass_statement_new(cass_string_init("INSERT INTO example1(key, value) VALUES ('a', '1')"), 0); cass_batch_add_statement(batch, statement); cass_statement_free(statement);}

{ CassStatement* statement = cass_statement_new(cass_string_init("UPDATE example2 set value = '2' WHERE key = 'b'"), 0); cass_batch_add_statement(batch, statement); cass_statement_free(statement);}

{ CassStatement* statement = cass_statement_new(cass_string_init("DELETE FROM example3 WHERE key = 'c'"), 0); cass_batch_add_statement(batch, statement); cass_statement_free(statement);}

CassFuture* batch_future = cass_session_execute_batch(session, batch);

Page 27: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

27

/* Batch objects can be freed immediately after being executed */cass_batch_free(batch);

/* This will block until the query has finished */CassError rc = cass_future_error_code(batch_future);

printf("Batch result: %s\n", cass_error_desc(rc));

cass_future_free(batch_future);

FuturesA future is a kind of proxy for a result that is initially unknown, because the computation of its value is yet incomplete.

Futures are returned from any driver call that would have required your application to block. This allowsyour application to continue processing and/or also submitting several queries at once. Although the driverhas an asynchronous design it can be used synchronously by immediately attempting to get result orexplicitly waiting for the future.

Waiting for resultsYou can wait indefinitely for futures results by either calling a wait function or by attempting to get theresult. You can also wait for results for an specified amount of time or you can poll without waiting.

Waiting synchronously

CassFuture* future = /* Some operation */

/* Block until a result or error is set */cass_future_wait(future);

cass_future_free(future);

Waiting synchronously for the result

CassFuture* future = cass_session_execute(session, statement);

/* Blocks and gets a result */const CassResult* result = cass_future_get_result(future);

/* If there was an error then the result won't be available */if (result == NULL) { /* The error code and message will be set instead */ CassError error_code = cass_future_error_code(future); CassString error_message = cass_future_error_message(future);

/* Handle error */}

cass_future_free(future);

Timed waiting

CassFuture* future = /* Some operation */

cass_duration_t microseconds = 30 * 1000000; /* 30 seconds */

Page 28: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

28

/* Block for a fixed amount of time */if (cass_future_wait_timed(future, microseconds) { /* A result or error was set during the wait call */} else { /* The operation hasn't completed yet */}

cass_future_free(future);

Polling

CassFuture* future = /* Some operation */

/* Poll to see if the future is ready */while (!cass_future_ready(future)) { /* Run other application logic or wait */}

/* A result or error was set */

cass_future_free(future);

Using callbacksYou can set a callback on a future, and it notifies your application when a request has completed. Using afuture callback is the lowest latency way of notification when waiting for several asynchronous operations.

Note: The driver may run the callback on thread that’s different from the application’s calling thread. Anydata accessed in the callback must be immutable or synchronized with a mutex, semaphore, etc.

Example

void on_result(CassFuture* future, void* data) { /* This result will now return immediately */ CassError rc = cass_future_error_code(future); printf("%s\n", cass_error_desc(rc));}

/* Code elided ... */

CassFuture* future = /* Some operation */;

/* Set a callback instead of waiting for the result to be returned */cass_future_set_callback(on_result, NULL);

/* The application's reference to the future can be freed immediately */cass_future_free(future);

/* Run other application logic */

Handling resultsA result object contains the results of a SELECT statement.

The CassResult object is generally only returned for SELECT statements. For mutations(INSERT, UPDATE, and DELETE) only a status code is present and is accessed usingcass_future_error_code(). However, when using lightweight transactions a result object is availableto check the status of the transaction. The result object is obtained from the query's future object.

Page 29: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

29

Note: Rows, column values, collections, decimals, strings, and bytes objects are all invalidated when theresult object is freed. All of these objects point to memory held by the result. This allows the driver to avoidunnecessarily copying data.

const CassResult* result = cass_future_get_result(future);

/* Process result */

cass_result_free(result);

Note:

The result object is immutable and may be accessed by multiple threads concurrently.

Rows and columns valuesThe result object represents a collection of rows. The first row, if present, can be obtained usingcass_result_first_row(). Multiple rows are accessed using a CassIterator object. Once a row isretrieved the column values are retrieved from a row by either index or by name. A CassIterator objectis used for enumerated column values.

Column value by index example

const CassRow* row = cass_result_first_row(result);

/* Get the first column value using the index */const CassValue* column1 = cass_row_get_column(row, 0);

Column value by name example

const CassRow* row = cass_result_first_row(result);

/* Get the value of the column named "column1" */const CassValue* column1 = cass_row_get_column_by_name(row, "column1");

Retrieving a column valueOnce you have the column values you retrieve the actual value of the data in the column.

cass_int32_t int_value;cass_value_get_int32(column1, &int_value);

cass_int64_t timestamp_value;cass_value_get_int32(column2, &timestamp_value);

CassString string_value;cass_value_get_string(column3, &string_value);

IteratorsIterators are used to iterate over the rows in a result, the columns in a row, or the values in a collection.

Note: cass_iterator_next() invalidates values retrieved by the previous iteration.

Page 30: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

30

const CassResult* result = cass_future_get_result(future);

CassIterator* iterator = cass_iterator_from_result(result);

while (cass_iterator_next(iterator)) { const CassRow* row = cass_iterator_get_row(iterator); /* Retrieve and use values from the row */}

cass_iterator_free(iterator);

All the iterators use the same pattern, but are instantiated differently and have different retrieval functions.Iterating over a map collection is slightly different because it has two values per entry, but it uses the samebasic pattern.

/* Execute SELECT query where a map collection is returned */

const CassResult* result = cass_future_get_result(future);

const CassRow* row = cass_result_first_row(result);

const CassValue* map = cass_row_get_column(row, 0);

CassIterator* iterator = cass_iterator_from_map(map);

while (cass_iterator_next(iterator)) { /* A separate call is used to get the key and the value */ const CassValue* key = cass_iterator_get_map_key(iterator); const CassValue* value = cass_iterator_get_map_value(iterator);

/* Use key/value pair */}

cass_iterator_free(iterator);

PagingLarge result sets can be divided into multiple pages automatically using paging. The result object keepstrack of the pagination state for the sequence of paging queries. When paging through a result set theresult object is checked to see if more pages exist and then attached to the statement before re-executingthe query to get the next page.

CassString query = cass_string_init("SELECT * FROM table1");CassStatement* statement = cass_statement_new(query, 0);

/* Return a 100 rows every time this statement is executed */cass_statement_set_paging_size(statement, 100);

cass_bool_t has_more_pages = cass_true;

while (has_more_pages) { CassFuture* query_future = cass_session_execute(session, statement);

const CassResult* result = cass_future_get_result(future);

if (result == NULL) { /* Handle error */ cass_future_free(query_future); break; }

Page 31: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

31

/* Get values from result... */

/* Check to see if there are more pages remaining for this result */ has_more_pages = cass_result_has_more_pages(result);

if (has_more_pages) { /* If there are more pages we need to set the position for the next execute */ cass_statement_set_paging_state(statement, result); }

cass_result_free(result); }

KeyspacesThere are several strategies for using keyspaces with a session.

Setting keyspaces at connection timeIt’s best to only create a single session per keyspace. Specify the keyspace when initially connecting asession.

CassFuture* connect_future = cass_session_connect_keyspace(session, cluster, "keyspace1");

Changing the keyspace with a USE statementTo change the keyspace of an already connected session, execute a USE statement.

Using a single session with multiple keyspacesTo use multiple keyspaces from a single session, fully qualify the table names in your queries, (forexample, keyspace_name.table_name.

SELECT * FROM keyspace_name.table_name WHERE ...;INSERT INTO keyspace_name.table_name (...) VALUES (...);

Prepared statementsPrepare a statement once (on the server) and pass values to bind to its variables before executing it.

Prepared statements can be used to improve the performance of frequently executed queries. Preparingthe query caches it on the Cassandra cluster and only needs to be performed once. Once created,prepared statements should be reused with different bind variables. Prepared queries use the ? marked todenote bind variables in the query string.

/* Prepare the statement on the Cassandra cluster */CassFuture* prepare_future = cass_session_prepare(session, "INSERT INTO example (key, value) VALUES (?, ?)");

/* Wait for the statement to prepare and get the result */CassError rc = cass_future_error_code(prepare_future);

printf("Prepare result: %s\n", cass_error_desc(rc));

if (rc != CASS_OK) {

Page 32: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

32

/* Handle error */ cass_future_free(prepare_future); return -1;}

/* Get the prepared object from the future */const CassPrepared* prepared = cass_future_get_prepared(prepared_future);

/* The future can be freed immediately after getting the prepared object */cass_future_free(prepare_future);

/* The prepared object can now be used to create statements that can be executed */CassStatement* statement = cass_prepared_bind(prepared);

/* Bind variables by name this time (this can only be done with prepared statements)*/cass_statement_bind_string_by_name(statement, "key", cass_string_init("abc"));cass_statement_bind_int32_by_name(statement, "value", 123);

/* Execute statement (same a the non-prepared code) */

/* The prepared object must be freed */cass_prepared_free(prepared);

Bound parametersThe driver supports two kinds of bound parameters: by marker and by name.

Binding parametersThe ? marker is used to denote the bind variables in a query string. This is used for both regularand prepared parameterized queries. In addition to adding the bind marker to your query string,your application must also provide the number of bind variables to cass_statement_new() whenconstructing a new statement. If a query doesn’t require any bind variables then 0 can be used. Thecass_statement_bind_*() functions are then used to bind values to the statement’s variables.

Bind variables can be bound by the marker index or by name.

Bind by marker index example

CassString query = cass_string_init("SELECT * FROM table1 WHERE column1 = ?");

/* Create a statement with a single parameter */CassStatement* statement = cass_statement_new(query, 1);

cass_statement_bind_string(statement, 0, cass_string_init("abc"));

/* Execute statement */

cass_statement_free(statement);

Bind by marker name exampleVariables can only be bound by name for prepared statements. This limitation exists because querymetadata provided by Cassandra is required to map the variable name to the variable’s marker index.

/* Prepare statement */

Page 33: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

33

/* The prepared query allocates the correct number of parameters automatically */CassStatement* statement = cass_prepared_bind(prepared);

/* The parameter can now be bound by name */cass_statement_bind_string_by_name(statement, "column1", cass_string_init("abc"));

/* Execute statement */

cass_statement_free(statement);

Binding large valuesThe data of values bound to statements are copied into the statement. That means that values boundto statements can be freed immediately after being bound. However, this might be problematic for largevalues so the driver provides cass_statement_bind_custom() which allocates a buffer where thelarge value is directly stored avoiding an extra allocation and copy.

CassString query = cass_string_init("INSERT INTO table1 (column1, column2) VALUES (?, ?)");

/* Create a statement with two parameters */CassStatement* statement = cass_statement_new(query, 2);

cass_statement_bind_string(statement, 0, cass_string_init("abc"));

cass_byte_t* bytes;cass_statement_bind_custom(statement, 1, 8 * 1024 * 1024, &bytes);

/* 'bytes' then can be used in the application to store a large value */

/* Execute statement */

Constructing connectionsCollections are supported using CassCollection objects, which supports the list, map, and set CQLtypes.

Note: Values appended to the collection can be freed immediately afterward because the values arecopied.

List exampleThis shows how to construct a list collection. A set can be constructed in a very similar way. Thedifference is the type CASS_COLLECTION_TYPE_SET is used to create the collection instead ofCASS_COLLECTION_TYPE_LIST.

CassStatement* statement = cass_statement_new(query, 1);

CassCollection* list = cass_collection_new(CASS_COLLECTION_TYPE_LIST, 3);

cass_collection_append_string(list, cass_string_init("123"));cass_collection_append_string(list, cass_string_init("456"));cass_collection_append_string(list, cass_string_init("789"));

cass_statement_bind_collection(statement, 0, list);

Page 34: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

34

/* The collection can be freed after binding */cass_collection_free(list);

Map exampleMaps are built similarly, but the key and value need to be interleaved as they are appended to thecollection.

CassStatement* statement = cass_statement_new(query, 1);

CassCollection* map = cass_collection_new(CASS_COLLECTION_TYPE_MAP, 2);

/* map["abc"] = 123 */cass_collection_append_string(map, cass_string_init("abc"));cass_collection_append_int32(map, 123);

/* map["def"] = 456 */cass_collection_append_string(map, cass_string_init("def"));cass_collection_append_int32(map, 456);

cass_statement_bind_collection(statement, 0, map);

/* The collection can be freed after binding */cass_collection_free(map);

Schema metadata APIAs of the 2.2 release, the driver improves the schema metadata API by adding concrete types for eachof the different metadata types (that is CassKeyspaceMeta, CassTableMeta, and CassColumnMeta)instead of the single CassSchemaMeta type used in the previous version of the API. This lets specificfunctions handle each of the metadata types and better represents the metadata hierarchy (that is, usertypes, functions, and aggregates metadata now live under the keyspace metadata). Applications that usedthe previous schema metadata API will require some small modifications to use the new API.

/* Obtain a snapshot of the schema from the session */const CassSchemaMeta* schema_meta = cass_session_get_schema_meta(session); /* There is no need to free metadata types derived from the snapshot. Their lifetime's are bound * the snapshot. */const CassKeyspaceMeta* keyspace_meta = cass_schema_meta_keyspace_by_name(schema_meta, "some_keyspace"); if (keyspace_meta != NULL) { const CassDataType* some_user_type = cass_keyspace_meta_user_type_by_name(keyspace_meta, "some_user_type"); /* Use user type */ } else { /* Handle error */ } /* Only the snapshot needs to be freed */cass_schema_meta_free(schema_meta);

A more complete example of using this API is available in the driver repository.

Page 35: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

35

User-defined function's and user-defined aggregate's metadataCassandra 2.2 added user-defined function (UDF) and user-defined aggregates (UDA). The 2.2 release ofthe driver adds support to inspect the metadata of these new datatypes.

UDF metadataThe example uses the following schema:

USE keyspace1; CREATE FUNCTION multiplyBy (x int, n int)RETURNS NULL ON NULL INPUTRETURNS int LANGUAGE javascript AS 'x * n';

Retrieving and printing UDF metadata

/* Obtain a snapshot of the schema metadata */const CassSchemaMeta* schema_meta = cass_session_get_schema_meta(session); /* Search for the function's keyspace by name */const CassKeyspaceMeta* keyspace_meta = cass_schema_meta_keyspace_by_name(schema_meta, "keyspace1"); /* Search for the function by name and argument types (overloads are possible) */const CassFunctionMeta* function_meta = cass_keyspace_meta_function_by_name(keyspace_meta, "multiplyBy", "int, int"); /* Inspect the function's metadata */ const char* full_name;size_t full_name_length;cass_function_meta_full_name(function_meta, &full_name, &full_name_length); const CassDataType* arg1_type = cass_function_meta_type_by_name(function_meta, "x"); const CassDataType* arg2_type = cass_function_meta_type_by_name(function_meta, "m"); const CassDataType* return_type = cass_function_meta_return_type(function_meta); /* ... */ /* Only the snapshot needs to be freed */cass_schema_meta_free(schema_meta);

UDA metadataThe example uses the following schema:

USE keyspace1;

Page 36: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

36

CREATE OR REPLACE FUNCTION avgState ( state tuple<int,bigint>, val int ) CALLED ON NULL INPUT RETURNS tuple<int,bigint> LANGUAGE java AS 'if (val !=null) { state.setInt(0, state.getInt(0)+1); state.setLong(1, state.getLong(1)+val.intValue()); } return state;'; CREATE OR REPLACE FUNCTION avgFinal ( state tuple<int,bigint> ) CALLED ON NULL INPUT RETURNS double LANGUAGE java AS 'double r = 0; if (state.getInt(0) == 0) return null; r = state.getLong(1); r/= state.getInt(0); return Double.valueOf(r);'; CREATE AGGREGATE IF NOT EXISTS average ( int ) SFUNC avgState STYPE tuple<int,bigint> FINALFUNC avgFinal INITCOND (0,0);

Retrieving and printing UDA metadata

/* Obtain a snapshot of the schema metadata */const CassSchemaMeta* schema_meta = cass_session_get_schema_meta(session); /* Search for the aggregate's keyspace by name */const CassKeyspaceMeta* keyspace_meta = cass_schema_meta_keyspace_by_name(schema_meta, "keyspace1"); /* Search for the aggregate by name and argument types (overloads are possible) */const CassAggregateMeta* aggregate_meta = cass_keyspace_meta_aggregate_by_name(keyspace_meta, "multiplyBy", "int, int"); /* Inspect the aggregate's metadata */ const char* full_name;size_t full_name_length;cass_aggregate_meta_full_name(aggregate, &full_name, &full_name_length); const CassFunctionMeta* avg_state_func = cass_aggregate_meta_state_func(aggregate_meta); const CassFunctionMeta* avg_final_func = cass_aggregate_meta_final_func(aggregate_meta); const CassDataType* state_type = cass_aggregate_meta_state_type(aggregate_meta); const CassDataType* return_type = cass_aggregate_meta_return_type(aggregate_meta); const CassValue* init_cond = cass_aggregate_meta_init_cond(aggregate_meta); /* ... */ /* Only the snapshot needs to be freed */cass_schema_meta_free(schema_meta);

Client configurationYou can modify the tuning policies and connection options for a client as you build it.

You can modify the tuning policies and connection options for a client as you build it.

Page 37: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

37

The configuration of a client cannot be changed after it has been built. There are some miscellaneousproperties (such as whether metrics are enabled, contact points, and which authentication informationprovider to use when connecting to a Cassandra cluster).

Client-side timestamps

Cassandra uses timestamps to serialize write operations. That is, values with a more current timestampare considered to be the most up-to-date version of that information. By default, timestamps are assignedby Cassandra on the server-side. This behavior can be overridden by configuring the driver to use atimestamp generator or assigning a timestamp directly to a CassStatement or CassBatch.

Monotonically increasing timestamp generatorThe monotonic timestamp generator guarantees that all writes that share this generator are givenmonotonically increasing timestamps. This generator produces microsecond timestamps with the sub-millisecond part generated using an atomic counter. That guarantees that no more than 1000 timestampsare generated for a given millisecond clock tick even when shared by multiple sessions.

Note: If the rate of 1000 timestamps per millisecond is exceeded this generator produces duplicatetimestamps.

CassTimestampGen* timestamp_gen = cass_timestamp_gen_monotonic_new();

cass_cluster_set_timestamp_gen(cluster, timestamp_gen);

/* ... */

/* Connect sessions */

/* Timestamp generators must be freed */cass_timestamp_gen_free(timestamp_gen);

All sessions that connect using this cluster object share this same timestamp generator.

Per-statement and per-batch timestampsTimestamps can also be assigned to individuals CassStatement or CassBatch requests.

CassStatement* statement = cass_statement_new("INSERT INTO * ...", 2);

/* Add a timestamp to the statement */cass_statement_set_timestamp(statement, 123456789);

CassBatch* batch = cass_batch_new(CASS_BATCH_TYPE_LOGGED);

/* Add a timestamp to the batch */cass_batch_set_timestamp(batch, 123456789);

/* Add statments to batch */

Connection heartbeats

Page 38: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

38

To prevent intermediate network devices (routers, switches, etc.) from disconnecting pooled connectionsthe driver sends a lightweight heartbeat request (using an OPTIONS protocol request) periodically. Bydefault the driver sends a heartbeat every 30 seconds. This can be changed or disabled (0 second interval)using the following:

/* Change the heartbeat interval to 1 minute */cass_cluster_set_connection_heartbeat_interval(cluster, 60);

/* Disable heartbeat requests */cass_cluster_set_connection_heartbeat_interval(cluster, 0);

Heartbeats are also used to detect unresponsive connections. An idle timeout setting controls theamount of time a connection is allowed to be without a successful heartbeat before being terminated andscheduled for reconnection. This interval can be changed from the default of 60 seconds:

/* Change the idle timeout to 2 minute */cass_cluster_set_connection_idle_timeout(cluster, 120);

It can be disabled by setting the value to a very long timeout or by disabling heartbeats.

CQL data types to C/C++ typesA summary of the mapping between CQL data types and C/C++ data types is provided.

DescriptionWhen retrieving the value of a column from a Row object, the value is typed according to the followingtable.

Table: C/C++ types to CQL data types

CQL data type C/C++ type

ascii const char*

bigint cass_int64_t

blob const cass_byte_t*

boolean cass_bool_t

counter cass_int64_t

date cass_uint_32_t

decimal const cass_byte_t* (varint) and a cass_int32_t(scale)

double cass_double_t

float cass_float_t

inet CassInet

int cass_int32_t

list CassCollection

map CassCollection

set CassCollection

Page 39: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

39

CQL data type C/C++ type

smallint cass_int16_t

text const char*

time cass_int64_t

timestamp cass_int64_t

timeuuid CassUuid

tinyint cass_int8_t

uuid CassUuid

varchar const char*

varint const cass_byte_t*

DatatypesCassDataType objects are useful for describing the different values that can be stored in Cassandra, fromprimitive types to more complex composite types such as UDTs, tuples and collections. Data types can beretrieved from existing metadata found in schema, results, values or prepared statements or they can beconstructed programmatically.

The following code snippets use the following type schema:

CREATE TYPE person (name text, // Street address, zip code, state/province, and country address frozen<tuple<text, int, text, text>>, // Type and number phone_numbers frozen<map<text, int>>);

Retrieving an existing datatypeUDT data types can be retrieved using a CassSchema object. The resulting data type object can be usedto construct a new CassUserType object using the cass_user_type_new_from_data_type()function.

Note: Any const [CassDataType]* object doesn’t need to be freed. Its lifetime is bound to the object itcame from.

/* Get schema object (this should be cached) */const CassSchema* schema = cass_session_get_schema(session);

/* This data type object doesn't need to be freed */const DataType* person_data_type = cass_schema_get_udt(schema, "keyspace", "person");

/* ... */

/* Schema object must be freed */cass_schema_free(schema);

Data types can also be retrieved from CassResult, CassPrepared, and CassValue objects.

• cass_result_column_data_type() is used to get the data type of a column for a CassResult

Page 40: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

40

• cass_prepared_parameter_data_type() is used to get the data type of the parameters fora CassPrepared object. There are also functions to get the data type of a prepared parameter byname

• cass_value_data_type() is used to get the data type represented by a CassValue object

Programmatically building a datatypeDatatypes can also be programmatically constructed. This is useful for application that may have disabledschema metadata.

CassDataType* person_data_type = cass_data_type_new_udt(3);CassDataType* address_data_type = cass_data_type_new_type(4);CassDataType* phone_numbers_data_type = cass_data_type_new(CASS_VALUE_TYPE_MAP);

/* Street address, zip code, state/province, and country */cass_data_type_add_sub_value_type(address_data_type, CASS_VALUE_TYPE_TEXT);cass_data_type_add_sub_value_type(address_data_type, CASS_VALUE_TYPE_INT);cass_data_type_add_sub_value_type(address_data_type, CASS_VALUE_TYPE_TEXT);cass_data_type_add_sub_value_type(address_data_type, CASS_VALUE_TYPE_TEXT);

/* Phone type and number*/cass_data_type_add_sub_value_type(phone_numbers_data_type, CASS_VALUE_TYPE_TEXT);cass_data_type_add_sub_value_type(phone_numbers_data_type, CASS_VALUE_TYPE_INT);

/* Add fields to the person data type */cass_data_type_add_sub_value_type_by_name(data_type, "name", CASS_VALUE_TYPE_TEXT);cass_data_type_add_sub_data_type_by_name(data_type, "address", address_data_type);cass_data_type_add_sub_value_type_by_name(data_type, "phone_numbers", phone_numbers_data_type);

/* ... */

/* Data types must be freed */cass_data_type_free(person_data_type);cass_data_type_free(address_data_type);cass_data_type_free(phone_numbers_data_type);

Creating UDTs, tuples, and collections from datatypesOnce the user type object has be retrieved or created manually it can be used to construct compositedatatypes. The sub-types of a datatype can be used to construct other nested types.

/* Construct a new UDT from a data type */CassUserType* person = cass_user_type_new_from_data_type(person_data_type);

/* ... */

/* Construct a new tuple from a nested data type */CassTuple* address = cass_tuple_type_new_from_data_type( cass_data_type_sub_data_type_by_name(person_data_type, "address"));

/* ... */

Page 41: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

41

/* Construct a new map collection from a nested data type */CassCollection* phone_numbers = cass_collection_new_from_data_type cass_data_type_sub_data_type_by_name(person_data_type, "phone_numbers"));

/* ... */

/* Add fields to the UDT */cass_user_type_set_string_byte_by_name(person, "name", "Bob");cass_user_type_set_user_type_by_name(person, "address", address);cass_user_type_set_collection_by_name(person, "phone_numbers", phone_numbers);

/* ... */

/* UDT, tuple, and collection objects must be freed */cass_user_type_free(person);cass_tuple_free(address);cass_collection_free(phone_numbers);

Removal of CassString, CassBytes, and CassDecimal typesThe previous version of the driver used wrapper types to represent strings, bytes and decimal types. Thisprovided a convenient way to encapsulate an array of data with its length. However, its use throughoutthe API was inconsistent, especially when it came to strings. Some functions would accept strings as null-terminated char arrays and others used CassString. These extra types also obfuscated the lifetime’s ofthe memory pointed to by these objects. This release improves the API by removing these wrapper typesand consistently uses two parameters in their place, a pointer to the data and the size of that data. Forfunctions that take strings there are always two versions of that function, one that accepts a null-terminatedchar array, and one that makes the lengths explicit (these functions have the suffix _n).

Functions that accepted CassStrings before look like this:

CassString query = cass_string_init("SELECT keyspace_name " "FROM system.schema_keyspaces");CassStatement* statement = cass_statement_new(query, 0); /* Use statement */

cass_statement_free(statement);

Now, instead functions accept the string literal directly:

CassStatement* statement = cass_statement_new("SELECT keyspace_name " "FROM system.schema_keyspaces", 0); /* Use statement */ cass_statement_free(statement);

They can also be used with an explicit lengths:

const char* query = "SELECT keyspace_name " "FROM system.schema_keyspaces";CassStatement* statement = cass_statement_new_n(query, strlen(query), 0);

Page 42: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

42

/* Use statement */ cass_statement_free(statement);

date and time datatypesThe date datatype is an unsigned 32-bit integer (cass_uint32_t). This number represents the numberof days with Epoch equal to January 1, 1970, centered at 2^31. Because it is centered at this Epoch, it isused to represent days before Epoch.

The time datatype is a signed 64-bit integer (cass_int64_t). This number represent the number ofnanoseconds since midnight and valid values are in the range 0 to 86399999999999.

The following examples both use the following schema:

CREATE TABLE date_time (key text PRIMARY KEY, year_month_day date, time_of_day time);

Using date and time in an INSERT statement

#include <time.h> /* ... */ CassStatement* statement = cass_statement_new("INSERT INTO date_time (key, year_month_day, time_of_day) " "VALUES (?, ?, ?)"); time_t now = time(NULL); /* Time in seconds from Epoch */ /* Converts the time since the Epoch in seconds to the 'date' type */cass_uint_32_t year_month_day = cass_date_from_epoch(now); /* Converts the time since the Epoch in seconds to the 'time' type */cass_int64_t time_of_day = cass_time_from_epoch(now); cass_statement_bind_string(statement, 0, "xyz"); /* 'date' uses an unsigned 32-bit integer */cass_statement_bind_uint32(statement, 1, year_month_day); /* 'time' uses a signed 64-bit integer */cass_statement_bind_int64(statement, 2, time_of_day) CassFuture* future = cass_session_execute(session, statement); /* Handle future result */ /* CassStatement and CassFuture both need to be freed */cass_statement_free(statement);cass_future_free(future);

Using date and time in a SELECT statement

#include <time.h>

Page 43: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

43

/* ... */ CassStatement* statement = cass_statement_new("SELECT * FROM examples.date_time WHERE key = ?"); CassFuture* future = cass_session_execute(session, statement); const CassResult* result = cass_future_get_result(future);/* Make sure there's a valid result */if (result != NULL && cass_result_row_count(resut) > 0) { const CassRow* row = cass_result_first_row(result); /* Get the value of the "year_month_day" column */ cass_uint32_t year_month_day; cass_value_get_uint32(cass_row_get_column(row, 1), &year_month_day); /* Get the value of the "time_of_day" column */ cass_int64_t time_of_day; cass_value_get_int64(cass_row_get_column(row, 2), &time_of_day); /* Convert 'date' and 'time' to Epoch time */ time_t time = (time_t)cass_date_time_to_epoch(year_month_day, time_of_day); printf("Date and time: %s", asctime(localtime(&time)))} else { /* Handle error */} /* CassStatement and CassFuture both need to be freed */cass_statement_free(statement);cass_future_free(future);

smallint and tinyint datatypesThe smallint and tinyint datatypes are used in cases where a smaller range would be a better fitthan an int or bigint. The smallint datatype is a signed 16-bit int (range -32768 to 32767) and thetinyint is a signed 8-bit int (range -128 to 127).

The example uses the following schema:

CREATE TABLE integers (key text PRIMARY KEY, tiny tinyint, small smallint);

Using smallint and tinyint in an INSERT statement

CassStatement* statement = cass_statement_new("INSERT INTO integers (key, tiny, small) " "VALUES (?, ?, ?)");cass_statement_bind_string(statement, "abc"); /* 'tinyint' is a signed 8-bit integer. It can represent values between -128 and 127 */cass_statement_bind_int8(statement, 127); /* 'smallint' is a signed 16-bit integer. It can represent values between -32768 and 32767 */cass_statement_bind_int16(statement, 32767);

Page 44: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

44

CassFuture* future = cass_session_execute(session, statement); /* Handle future result */ /* CassStatement and CassFuture both need to be freed */cass_statement_free(statement);cass_future_free(future);

Enabling and disabling schema metadata

Retrieving and updating schema metadata can be enabled or disabled. It is enabled by default. However,some application might wish to reduce this overhead. This is useful to improve the startup performance ofshort-lived sessions or an environment where up-to-date schema metadata is unnecessary.

Note: This also disables token-aware routing, because it depends on schema metadata.

/* Disable schema metdata */cass_cluster_set_use_schema(cluster, cass_false);

Latency-aware routingReroute queries from poorly-performing nodes to better-performing ones.

Latency-aware routing tracks the latency of queries to avoid sending new queries to poorly performingCassandra nodes. It can be used in conjunction with other load-balancing and routing policies.

/* Disable latency-aware routing (this is the default setting) */cass_cluster_set_latency_aware_routing(cluster, cass_false);

/* Enable latency-aware routing */cass_cluster_set_latency_aware_routing(cluster, cass_true);

/* * Configure latency-aware routing settings */

/* Up to 2 times the best performing latency is okay */cass_double_t exclusion_threshold = 2.0;

/* Use the default scale */cass_uint64_t scale_ms = 100;

/* Retry a node after 10 seconds even if it was performing poorly before */cass_uint64_t retry_period_ms = 10000;

/* Find the best performing latency every 100 milliseconds */cass_uint64_t update_rate_ms = 100;

/* Only consider the average latency of a node after it's been queried 50 times */cass_uint64_t min_measured = 50;

cass_cluster_set_latency_aware_routing_settings(cluster, exclusion_threshold, scale_ms, retry_period_ms, update_rate_ms,

Page 45: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

45

min_measured);

LoggingUse the driver's logging mechanism to record different kinds of information about your client.

The driver’s logging system uses stderr by default and the log level CASS_LOG_WARN. Both of thesesettings are changed using the driver’s cass_log_*() configuration functions.

Note: Logging configuration must be done before calling any other driver function.

Log levelTo update the log level use cass_log_set_level().

cass_log_set_level(CASS_LOG_INFO);/* Create cluster and connect session */

Custom logging callbackThe use of a logging callback allows an application to log messages to a file, syslog, or any other loggingmechanism. This callback must be thread-safe, because it is possible for it to be called from multiplethreads concurrently. The data parameter allows custom resources to be passed to the logging callback.

void on_log(const CassLogMessage* message, void* data) { /* Handle logging */}

// Code elided ...

void* log_data = custom-log-resource;cass_log_set_callback(on_log, log_data);cass_log_set_level(CASS_LOG_INFO);

/* Create cluster and connect session */

Logging cleanupResources passed to a custom logging callback should be cleaned up by calling cass_log_cleanup().This shuts down the logging system and ensures that the custom callback will no longer be called.

/* Close any sessions */

cass_log_cleanup();

/* Free custom logging resources */

Named parameters

You can name parameters inside a query string. Previous to release 2.1, only positional parameters weresupported for non-prepared queries, that is, parameters denoted with ? needed to be bound to a queryin the same order as they appeared in the query string. Version 2.1 of the driver allows parameters to benamed using the :<name> syntax. Named parameters can also be used in conjunction with prepared

Page 46: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

46

queries, but are most useful for non-prepared queries where metadata for the parameters names are notavailable.

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);

Nested collections

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

A new cass_collection_append_collection function has been added.

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

Collection values can then be recursively iterated over:

/* 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);

Page 47: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

47

/* Iterate over nested collection */

Performance metricsRetrieve performance metrics and other diagnostic information from the driver.

Performance metrics and diagnostic information can be obtained from the driver usingcass_session_get_metrics(). The resulting CassMetrics object contains several useful metrics foraccessing request performance or debugging issues.

CassMetrics metrics;

/* Get a snapshot of the driver's metrics */cass_session_get_metrics(session, &metrics);

RequestsThe requests field contains information about request latency and throughput. All latency times are inmicroseconds and throughput numbers are in requests per seconds.

StatisticsThe stats field contains information about connections and backpressure markers. If the numberof available_connections is less than the number of total_connections this could meanthe number of I/O threads or number of connections may need to be increased. The same is true forexceeded_pending_requests_water_mark and exceeded_write_bytes_water_mark metrics. Itcould also mean the Cassandra cluster is unable to handle the current request load.

ErrorsThe errors field contains information about the occurrence of requests and connection timeouts.Request timeouts occur when a request fails to get a timely response (default: 12 seconds). Pendingrequest timeouts occur whens a request waits too long to be serviced by an assigned host. This can occurwhen too many requests are in-flight for a single host. Connection timeouts occur when the process ofestablishing new connections is unresponsive (default: 5 seconds).

Retry policies

Retry polices allow the driver to automatically handle server-side failures when Cassandra is unable tofulfill the consistency requirement of a request.

Note: Retry policies do not handle client-side failures such as client-side timeouts or client-side connectionissues. In these cases application code must handle the failure and retry the request. The driver willautomatically recover requests that haven’t been written, but once a request is written the driver willreturn an error for in-flight requests and will not try to automatically recover. This is done because not alloperations are idempotent and the driver is unable to distinguish which requests can automatically retriedwithout side effect. It’s up to application code to make this distinction.

Setting retry policyBy default, the driver uses the default retry policy for all requests unless it is overridden. Theretry policy can be set globally using the cass_cluster_set_retry_policy() function, orit can be set per statement or batch using the cass_statement_set_retry_policy() orcass_batch_set_retry_policy() functions, respectively.

Page 48: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

48

Default retry policyThe default retry policy will only retry a request when it is safe to do so while preserving the consistencylevel of the request and it is likely to succeed. In all other cases, this policy will return an error.

Failure type Action

Read Timeout Retry if the number of received responses isgreater than or equal to the number of requiredresponses, but the data was not received. Returnsand error in all other cases.

Write Timeout Retry only if the request is a logged batch requestand the request failed to write the batch log.Returns an error in all other cases.

Unavailable Retry the request using the next host in the queryplan.

CassRetryPolicy* default_policy = cass_retry_policy_default_new();

/* ... */

/* Retry policies must be freed */cass_retry_policy_free(default_policy);

Downgrading consistency retry policyThis policy retries in all the same scenarios as the default policy, and it also retries in cases where there isa chance to save the request at the cost of lower the consistency. The goal of this policy is to be robust inthe face of transient failures. Read requests succeed as long as there’s a single copy available and writessucceed if there’s at least a single copy persisted.

Note: This policy may attempt to retry request with a lower consistency level. Using this policy can breakconsistency guarantees and should not be used in an application that required strong consistency.

Failure type Action

Read Timeout Retry with a lower consistency if some at leastsome replicas responded.

Write Timeout Retry unlogged batches at a lower consistencylevel if at least one replica responded. For singlequeries and other batch types if any replicasresponded then consider the request successfuland ignore the error.

Unavailable Retry with a lower consistency if some at leastsome replicas responded.

CassRetryPolicy* downgrading_policy =cass_retry_policy_downgrading_consistency_new();

/* ... */

/* Retry policies must be freed */cass_retry_policy_free(downgrading_policy);

Page 49: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

49

Fall-through retry policyThis policy never retries or ignores a server-side failures. Errors are always returned. This policy is usefulfor application that want to handle retries directly.

Failure type Action

Read Timeout Return error.

Write Timeout Return error.

Unavailable Return error.

CassRetryPolicy* fallthrough_policy =cass_retry_policy_fallthrough_new();

/* ... */

/* Retry policies must be freed */cass_retry_policy_free(fallthrough_policy);

LoggingThis policy can be added as a parent policy to all the other polices. It logs the retry decision of its childpolicy. The log messages created by this policy are done using the CASS_LOG_INFO level.

CassRetryPolicy* default_policy = cass_retry_policy_default_new();CassRetryPolicy* logging_policy = cass_retry_policy_logging_new(default_policy)

cass_cluster_set_retry_policy(cluster, logging_policy);

/* ... */

/* Retry policies must be freed */cass_retry_policy_free(default_policy);cass_retry_policy_free(logging_policy);

SecurityThe driver currently supports plain text authentication and SSL (via OpenSSL).

The driver currently supports plain text authentication and SSL (via OpenSSL).

AuthenticationAuthentication allows your application to supply credentials that are used to control access to Cassandraresources. The driver currently only supports plain text authentication, so it is best used in conjunction withSSL. A future release may relax this constraint with the use of SASL authentication.

Credentials are passed using the following:

CassCluster* cluster = cass_cluster_new();

const char* username = "username1";const char* password = "password1";

cass_cluster_set_credentials(cluster, username, password);

Page 50: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

50

/* Connect session object */

cass_cluster_free(cluster);

SSLThis example uses a self-signed certificate, but most steps are similar for certificates generated by acertificate authority (CA). First, generate a public and private key pair for your Cassandra nodes andconfigure them to use the generated certificate.

Keystore and truststore may be used interchangeably. These can and oftentimes are the same file. Thisexample uses the same file for both (that is, keystore.jks). The difference is keystores generally holdprivate keys and truststores hold public keys/certificate chains. Variable fields in example need to bereplaced with values specific to your environment.

SSL can be rather cumbersome to setup. If you run into issues or have trouble configuring SSL please usethe mailing list or IRC.

Generating the keysThe most secure method of setting up SSL is to verify that domain name or the IP address used to connectto the server matches identity information found in the SSL certificate. This helps to prevent man-in-the-middle attacks. Cassandra uses IP addresses internally so that’s the only supported information for identityverification. That means that the IP address of the Cassandra server where the certificate is installedneeds to be present in either the certificate’s common name (CN) or one of its subject alternative names(SANs). It’s possible to create the certificate without either, but then it will not be possible to verify theserver’s identity. Although this is not as secure, it eases the deployment of SSL by allowing the samecertificate to be deployed across the entire Cassandra cluster.

To generate a public-private key pair with the IP address in the CN field use the following:

keytool -genkeypair -noprompt -keyalg RSA -validity 36500 \ -alias node \ -keystore keystore.jks \ -storepass keystore-password \ -keypass key-password \ -dname "CN=IP-address-goes-here, OU=Drivers and Tools, O=DataStax Inc., L=Santa Clara, ST=California, C=US"

If you would prefer to use a SAN use this command:

keytool -genkeypair -noprompt -keyalg RSA -validity 36500 \ -alias node \ -keystore keystore.jks \ -storepass keystore-password \ -keypass key-password \ -ext SAN="IP-address-goes-here" \ -dname "CN=node1.datastax.com, OU=Drivers and Tools, O=DataStax Inc., L=Santa Clara, ST=California, C=US"

Enabling client-to-node encryptionCopy the generated keystore from the previous step to your Cassandra node(s), and add the following toyour cassandra.yaml file.

Page 51: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

51

client_encryption_options: enabled: true keystore: path-to-keystore/keystore.jks keystore_password: keystore-password ## The password you used when generating the keystore. truststore: path-to-keystore/keystore.jks truststore_password: keystore-password require_client_auth: true-or-false

Note: This example uses the same file for both the keystore and truststore.

Setting up SSLCreate an SSL object and configure it:

#include <cassandra.h>

void setup_ssl(CassCluster* cluster) { CassSsl* ssl = cass_ssl_new();

// Configure SSL object...

// To enable SSL attach it to the cluster object cass_cluster_set_ssl(cluster, ssl);

// You can detach your reference to this object once it's // added to the cluster object cass_ssl_free(ssl);

Exporting and loading the public keyThe default setting of the driver is to verify the certificate sent during the SSL handshake. For the driver toproperly verify the Cassandra certificate the driver needs either the public key from the self-signed publickey or the CA certificate chain used to sign the public key. To have this work, extract the public key fromthe Cassandra keystore generated in the previous steps. This exports a PEM formatted certificate which isrequired by the driver.

keytool -exportcert -rfc -noprompt \ -alias node \ -keystore keystore.jks \ -storepass keystore-password \ -file cassandra.pem

The trusted certificate is loaded using the following code:

c int load_trusted_cert_file(const char* file, CassSsl* ssl) { CassError rc; char* cert; long cert_size;

FILE *in = fopen(file, “rb”); if (in == NULL) { fprintf(stderr, “Error loading certificate file ’%s’\n”, file); return 0; }

fseek(in, 0, SEEK_END); cert_size = ftell(in); rewind(in);

Page 52: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

52

cert = (char*)malloc(cert_size); fread(cert, sizeof(char), cert_size, in); fclose(in);

// Add the trusted certificate (or chain) to the driver rc = cass_ssl_add_trusted_cert(ssl, cass_string_init2(cert, cert_size)); if (rc != CASS_OK) { fprintf(stderr, “Error loading SSL certificate: %s\n”, cass_error_desc(rc)); free(cert); return 0; }

free(cert); return 1;}

It is possible to load multiple self-signed certificates or CA certificate chains. In the case where you’re usingself-signed certificates with unique IP addresses this is required. It is possible to disable the certificateverification process, but it is not recommended.

// Disable certificate verificationcass_ssl_set_verify_flags(ssl,CASS_SSL_VERIFY_NONE);

Enabling identity verificationIf you’ve generated a unique certificate for each Cassandra node with the IP address in the CN or SANfields you’ll need to also enable identity verification. This is disabled by default.

// Add identity verification flag: CASS_SSL_VERIFY_PEER_IDENTITYcass_ssl_set_verify_flags(ssl, CASS_SSL_VERIFY_PEER_CERT | CASS_SSL_VERIFY_PEER_IDENTITY);

Using with client-side certificatesClient-side certificates allow Cassandra to authenticate the client using public key cryptography and chainsof trust. This is same process as above but in reverse. The client has a public and private key and theCassandra node has a copy of the private key or the CA chain used to generate the pair.

Generating and loading the client-side certificateA new public-private key pair needs to be generated for client authentication.

keytool -genkeypair -noprompt -keyalg RSA -validity 36500 \ -alias driver \ -keystore keystore-driver.jks \ -storepass keystore password \ -keypass key-password

The public and private key then need to be extracted and converted to the PEM format. To extract thepublic:

keytool -exportcert -rfc -noprompt \ -alias driver \

Page 53: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

53

-keystore keystore-driver.jks \ -storepass keystore password \ -file driver.pem

To extract and convert the private key:

keytool -importkeystore -noprompt -srcalias certificatekey -deststoretype PKCS12 \ -srcalias driver \ -srckeystore keystore-driver.jks \ -srcstorepass keystore password \ -storepass key-password \ -destkeystore keystore-driver.p12

openssl pkcs12 -nomacver -nocerts \ -in keystore-driver.p12 \ -password pass:key-password \ -passout pass:key-password \ -out driver-private.pem

Now you can load the PEM formatted public and private key. The files can be loaded using the same codefrom above in load_trusted_cert_file().

CassError rc = CASS_OK;

char* cert = NULL;size_t cert_size = 0;

// Load PEM-formatted certificate data and size into cert and cert_size...

rc = cass_ssl_set_cert(ssl, cass_string_init2(cert, cert_size));if (rc != CASS_OK) { // Handle error}

char* key = NULL;size_t key_size = 0;

// A password is required when the private key is encrypted. If the private key// is NOT password protected use NULL.const char* key_password = "<key password>";

// Load PEM-formatted private key data and size into key and key_size...

rc = cass_ssl_set_private_key(ssl, cass_string_init2(key, key_size), key_password);if (rc != CASS_OK) { // Handle error}

Setting up client authentication on the clusterThe driver’s public key or the CA chain used to sign the driver’s certificate will need to be added toCassandra’s truststore. If using self-signed certificate then the public key needs to be extracted from thedriver’s keystore generated in the previous steps.

Extract the public key from the driver’s keystore and add it to Cassandra’s truststore.

Page 54: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

54

keytool -exportcert -noprompt \ -alias driver \ -keystore keystore-driver.jks \ -storepass cassandra \ -file cassandra-driver.crt

keytool -import -noprompt \ -alias truststore \ -keystore keystore.jks \ -storepass cassandra \ -file cassandra-driver.crt

Enable client authentication in the cassandra.yaml file.

require_client_auth:true

Tuples

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

The follwing examples use this tuple schema

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

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 */}

Page 55: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

55

/* 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);

User-defined types

A user-defined types (UDT, introduced in Cassandra 2.1) allows for creating composite datatypes withmultiple fields in a single column. This can be useful for simplifying schema by grouping related fields into asingle UDT instead of using multiple columns.

Page 56: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

56

The following examples use this 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>)

Using UDT defined in 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);

Using a UDT programmatically defined

int rc;

Page 57: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

57

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 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 =

Page 58: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Reference

58

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);

Whitelist load-balancing policy

By default, the driver auto-discovers and connects to all the nodes in a Cassandra cluster. The whitelistload-balancing policy can override this behavior by only connecting to a predefined set of hosts. Thispolicy is useful for testing or debugging and is not optimal for production deployments. If the goalis to limit connections to a local data center then use the datacenter-aware load-balancing policycass_cluster_set_load_balance_dc_aware().

CassCluster* cluster = cass_cluster_new(); /* Enable a whitelist by setting a comma-delimited lists of hosts */cass_cluster_set_whitelist_filtering(cluster, "127.0.0.1, 127.0.0.2, ..."); CassFuture* future = cass_session_connect(session, cluster); /* ... */ cass_future_free(future);cass_cluster_free(cluster);

Page 59: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

FAQ

59

FAQ

Which versions of Cassandra does the driver support?

Version 2.2 of the driver supports any Cassandra version greater than 1.2 and 2.0. Version 2.1 is notsupported.

Which versions of CQL does the driver support?

It supports CQL version 1 and 2. CQL version 3 is not supported..

How do I generate a uuid or a timebased uuid?

Create a CassUuidGen object and call the cass_uuid_gen_random() for a uuid andcass_uuid_gen_time() for a timeuuid.

Should I create one client instance per module in myapplication?

Normally you should use one client instance per application. You should share that instance betweenmodules within your application.

Should I shut down the pool after executing a query?

No. You should only close a session once when your application is terminating.

API reference

DataStax C/C++ Driver 2.2 for Apache Cassandra.

Tips for using DataStax documentation

Navigating the documentsTo navigate, use the table of contents or search in the left navigation bar. Additional controls are:

Hide or display the left navigation.

Page 60: C/C++ Driver 2.2 for Apache Cassandra · 2016-10-16 · New features in 2.1 • Tuples and user-defined types (UDT) • Nested collections • Retry policies • Client-side timestamps

Tips for using DataStax documentation

60

Go back or forward through the topics as listed inthe table of contents.

Toggle highlighting of search terms.

Print page.

See doc tweets and provide feedback.

Grab to adjust the size of the navigation pane.

Appears on headings for bookmarking. Right-clickthe ¶ to get the link.

Toggles the legend for CQL statements andnodetool options.

Other resourcesYou can find more information and help at:

• Documentation home page• Datasheets• Webinars• Whitepapers• Developer blogs• Support