Rust-Cassandra rs: cassandra-rs — bindings to the DataStax C/C++ client

Build Status Current Version License

cassandra-cpp

This is a maintained Rust project that exposes the DataStax cpp driver at https://github.com/datastax/cpp-driver/ in a somewhat-sane crate. It was originally a fork of https://github.com/tupshin/cassandra-rs but that is no longer maintained.

It is a wrapper around the raw driver binding crate cassandra-cpp-sys.

Documentation (crates.io).

Getting started

For this crate to work, you must first have installed the datastax-cpp driver. Follow the steps in the cpp driver docs to do so. Pre-built packages are available for most platforms.

Make sure that the driver (specifically libcassandra_static.a and libcassandra.so) are in your /usr/local/lib64/ directory

Documentation

See the API documentation.

The Cassandra Query Language (CQL) documentation is likely to be useful.

Since this crate provides a relatively thin wrapper around the DataStax driver, you may also find the DataStax documentation and API docs useful.

Example

For a straightforward example see simple.rs.

There are additional examples included with the project in tests and examples.

Futures (version 0.15)

Since version 0.15, this crate uses std::future, allowing your code to use futures:0.3, async/await, etc.

Previous versions (up to 0.14) used futures:0.1. You can either remain on the 0.14 stream, update your code to use std::future, or use a compatibility shim (e.g., futures::compat).

Migrating from version 0.8

The API changed significantly in version 0.10. (Version 0.9 was skipped, for consistency with the cassandra-cpp-sys version number.) For a summary of the main changes, see CHANGELOG.

License

This code is open source, licensed under the Apache License Version 2.0 as described in LICENSE.

Contributing

Please see CONTRIBUTING.md for details on how to contribute to this project.

Development

This crate is regularly built by Travis; to see details of the most recent builds click on the "build" badge at the top of this page.

You must have the DataStax driver installed on your system in order to build this crate.

The unit tests assume Cassandra is running on the local host accessible on the standard port. The easiest way to achieve this is using Docker and the standard Cassandra image, with

docker pull cassandra
docker run -d --net=host --name=cassandra cassandra

You should run them single-threaded to avoid the dreaded org.apache.cassandra.exceptions.ConfigurationException: Column family ID mismatch error. The tests share a keyspace and tables, so if run in parallel they interfere with each other.

cargo test -- --test-threads 1

Remember to destroy the container when you're done:

docker stop cassandra
docker rm cassandra

History

This project was forked from cassandra, which was no longer being maintained.

Comments

  • Clean up clippy warnings
    Clean up clippy warnings

    Dec 10, 2017

    As noted here, mostly clippy complains about functions unnecessarily consuming their parameters - of which there are rather a lot of examples.

    But that's not all. WIBNI to clean up generally...

    cleanup help wanted 
    Reply
  • About how varint is calculated
    About how varint is calculated

    Jun 5, 2019

    When I used cassandra-sys-rs, I added a piece of code to calculate the 128-bit varint:

       ```
            let mut var = mem::zeroed();
            let mut var_length = mem::zeroed();
    
            cass_value_get_bytes(items_number_value, &mut var, &mut var_length);
    
            let mut slice = slice::from_raw_parts(var, var_length);
            let mut counter: i128 = 0;
            for slice_number in 0..var_length {
                let i = var_length - slice_number- 1;
                let pow = 256i128.pow(i as u32);
                let result = slice[slice_number] as i128 * pow;
                counter += result;
            }
            return counter
    
    This can convert bytes to i128. Is it better to calculate varint in this way?
    awaiting info question 
    Reply
  • feature: derive bind helper
    feature: derive bind helper

    Apr 16, 2020

    We've been working on this internally, and wondering if it'd be worth up upstreaming (would you accept this change-set?). Essentially it writes all the boring code of converting to/from rust types and CQL types.

    Essentially, you can:

    #[derive(Debug, CassandraRow)]
    struct Foo {
        bar: i64,
        baz: String,
    }
    
    #[tokio::main]
    async fn main() -> Result<(), cassandra::Error> {
        let mut cluster = cassandra::Cluster::default();
        cluster.set_contact_points("127.0.0.1")?;
        let session = cluster.connect()?;
        let statement = session
            .prepare("SELECT * FROM foo.bar LIMIT 50")?
            .await?;
    
        let result = session.execute(&statement).await?;
        for row in &result {
            let foo: Foo= row.try_into()?;
            println!("Foo: {:?}", foo);
        }
    }
    

    With support for UDT's and binding to insert statements as well via a nice macro:

        cql_execute!(
            bound_statement,
            "channel_id" => 5_i64,
            "id" => 5_i64,
            "bucket" => 5_i32,
        )
        .await?;
    
    enhancement 
    Reply
  • Make bind_string more efficient
    Make bind_string more efficient

    May 3, 2020

    This is an example of a change that could be made in several places. It removes an allocation, copy, and call to strlen(). In general, the Datastax C++ driver has 2 functions. One that takes a char* and one that takes a char* and length.

    CASS_EXPORT CassError
    cass_statement_bind_string(CassStatement* statement,
                               size_t index,
                               const char* value);
    
    CASS_EXPORT CassError
    cass_statement_bind_string_n(CassStatement* statement,
                                 size_t index,
                                 const char* value,
                                 size_t value_length);
    

    Under the covers, the first variant simply calls the second using strlen() on the char*.

    CassError cass_statement_bind_string(CassStatement* statement, size_t index, const char* value) {
      return cass_statement_bind_string_n(statement, index, value, SAFE_STRLEN(value));
    }
    

    So, it saves a call to strlen() if we can use the second variant.

    Because a Rust &str already knows the length and we no longer need to call strlen(), there is no need to null terminate the string. This means that we can simply use a raw pointer to the &str instead of converting it to a CString. Not having to convert to a CString saves an allocation and a copy of the data.

    If the general approach is accepted, additional commits will be added. In general, all uses of CString should be analyzed to see if the simpler interface can be used.

    I ran this under Valgrind just to make extra sure that there was no leaked memory.

    Reply
  • Segfault when sending Row across threads
    Segfault when sending Row across threads

    May 20, 2020

    I have a utility that executes a query and send each Row though a channel and implements paging. But I experienced some segfault:

    #0  core::str::run_utf8_validation () at src/libcore/str/mod.rs:1531
    #1  0x000055dd508053ac in core::str::from_utf8 () at src/libcore/str/mod.rs:342
    #2  0x000055dd506db27a in cassandra_cpp::cassandra::value::Value::get_str::{{closure}} ()
        at registry/src/github.com-1ecc6299db9ec823/cassandra-cpp-0.15.0/src/cassandra/value.rs:421
    #3  core::result::Result<T,E>::and_then (self=..., op=...)
        at /rustc/5e7af4669f80e5f682141f050193ab679afdb4b1/src/libcore/result.rs:727
    #4  cassandra_cpp::cassandra::value::Value::get_str (self=<optimized out>)
        at registry/src/github.com-1ecc6299db9ec823/cassandra-cpp-0.15.0/src/cassandra/value.rs:416
    #5  0x000055dd506db3cc in cassandra_cpp::cassandra::value::Value::get_string (self=0x0)
        at registry/src/github.com-1ecc6299db9ec823/cassandra-cpp-0.15.0/src/cassandra/value.rs:428
    #6  0x000055dd5050e758 in <cassandra_cpp::cassandra::row::Row as cassandra_cpp::cassandra::row::AsRustType<alloc::string::String>>::get_by_name (self=<optimized out>, name=...)
        at registry/src/github.com-1ecc6299db9ec823/cassandra-cpp-0.15.0/src/cassandra/row.rs:98
    #7  <cassandra_cpp::cassandra::row::Row as services::cql::extensions::AsRustTypeExt<T>>::get_by_name (self=<optimized out>, name=...)
        at services/src/cql/extensions.rs:45
    
    (gdb) select-frame 2
    (gdb) info locals
    slice = &[u8] {data_ptr: 0x0, length: 34}
    message_length = <optimized out>
    message_ptr = <optimized out>
    slice = <optimized out>
    err = <optimized out>
    val = <optimized out>
    

    It makes sense though, as iterating with cass_iterator_get_row invalidates the previous row. https://docs.datastax.com/en/developer/cpp-driver/1.0/api/struct.CassIterator/#function-cass_iterator_get_row

    https://github.com/Metaswitch/cassandra-rs/blob/09d5bbbd05c9525e3600be8a15d3dcb2c03239b8/src/cassandra/result.rs#L187-L192

    So, I suppose the Row type should not be Send or the iterator item should be a reference with a lifetime reflecting that, to prevent misuse like I did.

    bug 
    Reply
  • add function to fetch the pagination token #64
    add function to fetch the pagination token #64

    Jun 9, 2020

    This is useful when you want to fetch the pagination token and not only set.

    Reply
  • Convert to owned repository
    Convert to owned repository

    Feb 5, 2020

    being forked doesn't allow code search on GitHub which is very convenient. is it possible to convert this fork to a first-class repository?

    also have you contacted the original author @tupshin about transfering the repo? that repo still shows up first in Google search from what I've seen

    cleanup 
    Reply
  • Thoroughly fix the error handling
    Thoroughly fix the error handling

    Jul 27, 2017

    This is a thorough root-and-branch replacement of all the error handling with something sane.

    There is now a single error type defined in errors.rs, which has variants for plain Cassandra errors (just a return code), detailed errors (with additional info), internal errors (currently only "unexpected type") or various foreign errors (in particular, problems converting strings to/from C).

    This error type is used appropriately everywhere, and has appropriate conversions as necessary.

    In addition I've audited and removed all (well, almost all) uses of unwrap, expect, panic!, unimplemented, and friends, replacing them with principled use of ? and Result. There are a few that remain where they are appropriate, and one (Statement::new) I'm not 100% happy with but think it best to leave.

    This is very much a breaking API change - several return types have been changed (from T to Result<T, Error>, or from Result<T, WrongError> to Result<T, Error>, usually, though some other changes have been made too).

    Some other choice details:

    • Enhance the enhance_nullary_enum macro to cope with omitted variants. We don't want CASS_OK or CASS_ERROR_LAST_ENTRY to count as errors which you need to consider in match; we should ensure these just never happen.
    • Move some error-handling code from futures to error module. Add some other nice error building helpers. Remove direct tests of CASS_OK elsewhere and depend on helpers in error module.
    • Define WriteType and ValueType as nice modern enums.
    • Fix cassandra_sys in https://github.com/Metaswitch/cassandra-sys-rs/pull/7 to not attempt to add limited error-chain support to the bare return code; instead use an extension trait in this crate to do it (adding to_result). Also add some missing symbols while we're at it.
    • Get rid of all uses of .chain_err(|| "pointless text"); the Cassandra errors speak for themselves and don't need the addition of an extra wrapper.

    Enjoy!

    Reply
  • Fails to compile on macOS due to stdc++ library ref
    Fails to compile on macOS due to stdc++ library ref

    Jan 31, 2020

    The following line in build.rs causes the problem: println!("cargo:rustc-flags=-l dylib=stdc++");

          ld: library not found for -lstdc++
          clang: error: linker command failed with exit code 1 (use -v to see invocation)
    

    Can you please disable it, at least for macOS builds? Thanks.

    bug help wanted 
    Reply
  • List data cannot be correctly displayed in the terminal
    List data cannot be correctly displayed in the terminal

    Dec 11, 2018

    I tried using simple.rs to generate information and apply it. But when I generate a new field and specify the data type as list, the println! on the terminal is [<error>]. How do I display the correct list information on the terminal?

    bug 
    Reply
  • update cassandra-sys-rs
    update cassandra-sys-rs

    Dec 3, 2017

    see Metaswitch/cassandra-sys-rs#14

    Reply
  • add missing rust binding types for Row and Statement
    add missing rust binding types for Row and Statement

    Jan 15, 2019

    I found a couple of missing binding types for both Row and Statement, not sure I should add tests for this though, please advise.

    Reply