hemidactylus
hemidactylus

Reputation: 91

Inserting a tuple field in Cassandra with python driver

everybody,

I have a problem inserting data into a tuple-valued field on a Cassandra table through the python-driver provided by DataStax.

Either I don't get how to properly pass the tuple argument to the Session.execute command or the driver is transforming the tuple, internally, in the wrong way when dispatching it to Cassandra - because the same insertion works fine when performed in a cqlsh session.

An equivalent insertion involving not a tuple, rather a list, works fine both in-code and in cqlsh.

I am using Python 2.7.10 and cassandra-driver 3.7.1. The error which gets raised in the Python execution is InvalidRequest: Error from server: code=2200 [Invalid query] message="Invalid list literal for tuplefield of type frozen<tuple<int, int, int>>".

I paste below a minimal working code that reproduces the issue I am seeing. Could someone help me figure out whether am I doing something wrong?

(Note: I have tried already to pass an ordinary List to Session.execute in place of the tuple argument, with no luck either.)

Thank you very much.

'''
    Run with:
        python 2.7.10
        cassandra-driver==3.7.1 installed
'''

from cassandra.cluster import Cluster

if __name__=='__main__':

    serverAddress='SERVER_ADDRESS'
    keyspacename='tupletests'
    tablecreation='''create table tabletest (
                            rowid int,
                            tuplefield tuple < int, int, int >,
                            listfield list < int >,
                        primary key (rowid)
                    );'''

    # connect and create keyspace
    clu=Cluster([serverAddress])
    session=clu.connect()
    session.execute("create keyspace %s with replication = \
        {'class': 'SimpleStrategy', 'replication_factor': 1};" % keyspacename)

    # use keyspace; create a sample table
    session.set_keyspace(keyspacename)
    session.execute(tablecreation)

    # insert a row with rowid,listfield
    session.execute('insert into tabletest (rowid, listfield) values (%s,%s)', \
        [999, [10,11,12]])
    # succeeds

    # insert a row with rowid,tuplefield
    session.execute('insert into tabletest (rowid, tuplefield) values (%s,%s)', \
        [111, tuple([6,5,4])])
    # raises: *** InvalidRequest: Error from server: code=2200 [Invalid query]
    # message="Invalid list literal for tuplefield of type frozen<tuple<int, int, int>>"

    # Compare with analogous statements in cqlsh, which *both* succeed:
    # List:    insert into tabletest (rowid, listfield) values (999, [21,22,23]);
    # Tuple:   insert into tabletest (rowid, tuplefield) values (765, (121,122,123));

    # delete test keyspace
    session.execute("drop keyspace %s;" % keyspacename)    
    print "Test Finished."

Upvotes: 4

Views: 1239

Answers (1)

hemidactylus
hemidactylus

Reputation: 91

Ok, I just solved the issue. Posting solution here for everybody. In order to let the driver know how the tuple should be formatted when passing it to Cassandra, if insisting on using a non-prepared statement for the insert, one must specify the encoder to use when parsing tuples before invoking the insert:

session.encoder.mapping[tuple] = session.encoder.cql_encode_tuple

After this statement, tuples appearing in Session.execute insert statements are automatically cast in the right way for Cassandra, which does not complain anymore.

(as far as I understand, it is even better to resort to a Prepared statement, which contains enough information to make it superfluous to specify the encoder separately).

Upvotes: 5

Related Questions