diff --git a/CHANGES.txt b/CHANGES.txt index 8462f706dc..52a6284b34 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,7 +1,8 @@ 0.8-dev * remove Avro RPC support (CASSANDRA-926) * adds support for columns that act as incr/decr counters - (CASSANDRA-1072, 1937, 1944, 1936, 2101, 2093, 2288, 2105, 2384, 2236, 2342) + (CASSANDRA-1072, 1937, 1944, 1936, 2101, 2093, 2288, 2105, 2384, 2236, 2342, + 2454) * CQL (CASSANDRA-1703, 1704, 1705, 1706, 1707, 1708, 1710, 1711, 1940, 2124, 2302, 2277) * avoid double RowMutation serialization on write path (CASSANDRA-1800) @@ -23,6 +24,7 @@ * compaction throttling (CASSANDRA-2156) * add key type information and alias (CASSANDRA-2311, 2396) * cli no longer divides read_repair_chance by 100 (CASSANDRA-2458) + * made CompactionInfo.getTaskType return an enum (CASSANDRA-2482) 0.7.5 diff --git a/build.xml b/build.xml index 622a1576e9..506eedabb9 100644 --- a/build.xml +++ b/build.xml @@ -27,6 +27,7 @@ + @@ -383,7 +384,9 @@ - + + + diff --git a/drivers/py/README b/drivers/py/README index d783d22ad7..8beb9f06f1 100644 --- a/drivers/py/README +++ b/drivers/py/README @@ -1,2 +1,28 @@ -Python language driver for CQL, a SQL-alike query language for Apache -Cassandra. +A Python driver for CQL that adheres to py-dbapi v2 + (PEP249, Python Database API Specification v2.0: http://www.python.org/dev/peps/pep-0249/). + +Standard use: + >> import cql + >> con = cql.connect(host, port, keyspace) + >> cursor = con.cursor() + >> cursor.execute("CQL QUERY", {kw=Foo, kw2=Bar, etc...}) + + - cursor.description # None initially, list of N tuples that represent + the N columns in a row after an execute. Only + contains type and name info, not values. + - cursor.rowcount # -1 initially, N after an execute + - cursor.arraysize # variable size of a fetchmany call + - cursor.fetchone() # returns a single row + - cursor.fetchmany() # returns self.arraysize # of rows + - cursor.fetchall() # returns all rows, don't do this. + + >> cursor.execute("ANOTHER QUERY", **more_kwargs) + >> for row in cursor: # Iteration is equivalent to lots of fetchone() calls + >> doRowMagic(row) + + >> cursor.close() + >> con.close() + +Query substitution: + - Use named parameters and a dictionary of names and values. + e.g. execute("SELECT * FROM CF WHERE name=:name", name="Foo") diff --git a/drivers/py/cql/__init__.py b/drivers/py/cql/__init__.py index 4790696086..52d8702cf0 100644 --- a/drivers/py/cql/__init__.py +++ b/drivers/py/cql/__init__.py @@ -15,9 +15,82 @@ # See the License for the specific language governing permissions and # limitations under the License. -""" -Cassandra Query Language driver -""" +import exceptions +import datetime + +import connection +import marshal + + +# dbapi Error hierarchy + +class Warning(exceptions.StandardError): pass +class Error (exceptions.StandardError): pass + +class InterfaceError(Error): pass +class DatabaseError (Error): pass + +class DataError (DatabaseError): pass +class OperationalError (DatabaseError): pass +class IntegrityError (DatabaseError): pass +class InternalError (DatabaseError): pass +class ProgrammingError (DatabaseError): pass +class NotSupportedError(DatabaseError): pass + + +# Module constants + +apilevel = 1.0 +threadsafety = 1 # Threads may share the module, but not connections/cursors. +paramstyle = 'named' + +ROW_KEY = "Row Key" + +# TODO: Pull connections out of a pool instead. +def connect(host, port=9160, keyspace='system', user=None, password=None): + return connection.Connection(host, port, keyspace, user, password) + +# Module Type Objects and Constructors + +Date = datetime.date + +Time = datetime.time + +Timestamp = datetime.datetime + +Binary = buffer + +def DateFromTicks(ticks): + return Date(*time.localtime(ticks)[:3]) + +def TimeFromTicks(ticks): + return Time(*time.localtime(ticks)[3:6]) + +def TimestampFromTicks(ticks): + return Timestamp(*time.localtime(ticks)[:6]) + +class DBAPITypeObject: + + def __init__(self, *values): + self.values = values + + def __cmp__(self,other): + if other in self.values: + return 0 + if other < self.values: + return 1 + else: + return -1 + +STRING = DBAPITypeObject(marshal.BYTES_TYPE, marshal.ASCII_TYPE, marshal.UTF8_TYPE) + +BINARY = DBAPITypeObject(marshal.BYTES_TYPE, marshal.UUID_TYPE, marshal.LEXICAL_UUID_TYPE) + +NUMBER = DBAPITypeObject(marshal.LONG_TYPE, marshal.INTEGER_TYPE) + +DATETIME = DBAPITypeObject(marshal.TIME_UUID_TYPE) + +ROWID = DBAPITypeObject(marshal.BYTES_TYPE, marshal.ASCII_TYPE, marshal.UTF8_TYPE, + marshal.INTEGER_TYPE, marshal.LONG_TYPE, marshal.UUID_TYPE, + marshal.LEXICAL_UUID_TYPE, marshal.TIME_UUID_TYPE) -from connection import Connection -from connection_pool import ConnectionPool diff --git a/drivers/py/cql/connection.py b/drivers/py/cql/connection.py index 754f360f75..9e7bc4df17 100644 --- a/drivers/py/cql/connection.py +++ b/drivers/py/cql/connection.py @@ -15,183 +15,69 @@ # See the License for the specific language governing permissions and # limitations under the License. -from os.path import exists, abspath, dirname, join +from cursor import Cursor +from cassandra import Cassandra from thrift.transport import TTransport, TSocket from thrift.protocol import TBinaryProtocol -from thrift.Thrift import TApplicationException -from errors import CQLException, InvalidCompressionScheme -from marshal import prepare -from decoders import SchemaDecoder -from results import RowsProxy -import zlib, re -from cassandra import Cassandra -from cassandra.ttypes import Compression, InvalidRequestException, \ - CqlResultType, AuthenticationRequest, \ - SchemaDisagreementException - -COMPRESSION_SCHEMES = ['GZIP', 'NONE'] -DEFAULT_COMPRESSION = 'GZIP' - -__all__ = ['COMPRESSION_SCHEMES', 'DEFAULT_COMPRESSION', 'Connection'] class Connection(object): - """ - CQL connection object. - - Example usage: - >>> conn = Connection("localhost", keyspace="Keyspace1") - >>> r = conn.execute('SELECT "age" FROM Users') - >>> for row in r.rows: - ... for column in row.columns: - ... print "%s is %s years of age" % (r.key, column.age) - """ - _keyspace_re = re.compile("USE (\w+);?", re.I | re.M) - _cfamily_re = re.compile("\s*SELECT\s+.+\s+FROM\s+[\']?(\w+)", re.I | re.M) - _ddl_re = re.compile("\s*(CREATE|ALTER|DROP)\s+", re.I | re.M) - - def __init__(self, host, port=9160, keyspace=None, username=None, - password=None, decoder=None): + + def __init__(self, host, port, keyspace, user=None, password=None): """ Params: * host .........: hostname of Cassandra node. - * port .........: port number to connect to (optional). - * keyspace .....: keyspace name (optional). - * username .....: username used in authentication (optional). + * port .........: port number to connect to. + * keyspace .....: keyspace to connect to. + * user .........: username used in authentication (optional). * password .....: password used in authentication (optional). - * decoder ......: result decoder instance (optional, defaults to none). """ + self.host = host + self.port = port + self.keyspace = keyspace + socket = TSocket.TSocket(host, port) self.transport = TTransport.TFramedTransport(socket) protocol = TBinaryProtocol.TBinaryProtocolAccelerated(self.transport) self.client = Cassandra.Client(protocol) + socket.open() - - # XXX: "current" is probably a misnomer. - self._cur_keyspace = None - self._cur_column_family = None - - if username and password: - credentials = {"username": username, "password": password} + self.open_socket = True + + if user and password: + credentials = {"username": user, "password": password} self.client.login(AuthenticationRequest(credentials=credentials)) - + if keyspace: - self.execute('USE %s;' % keyspace) - self._cur_keyspace = keyspace - - if not decoder: - self.decoder = SchemaDecoder(self.__get_schema()) - else: - self.decoder = decoder + c = self.cursor() + c.execute('USE %s;' % keyspace) + c.close() - def __get_schema(self): - def columns(metadata): - results = {} - for col in metadata: - results[col.name] = col.validation_class - return results - - def column_families(cf_defs): - cfresults = {} - for cf in cf_defs: - cfresults[cf.name] = {"comparator": cf.comparator_type} - cfresults[cf.name]["default_validation_class"] = \ - cf.default_validation_class - cfresults[cf.name]["key_validation_class"] = \ - cf.key_validation_class - cfresults[cf.name]["columns"] = columns(cf.column_metadata) - return cfresults - - schema = {} - for ksdef in self.client.describe_keyspaces(): - schema[ksdef.name] = column_families(ksdef.cf_defs) - return schema - - def prepare(self, query, *args): - prepared_query = prepare(query, *args) - - # Snag the keyspace or column family and stash it for later use in - # decoding columns. These regexes don't match every query, but the - # current column family only needs to be current for SELECTs. - match = Connection._cfamily_re.match(prepared_query) - if match: - self._cur_column_family = match.group(1) - return prepared_query - match = Connection._keyspace_re.match(prepared_query) - if match: - self._cur_keyspace = match.group(1) - return prepared_query - - # If this is a CREATE, then refresh the schema for decoding purposes. - match = Connection._ddl_re.match(prepared_query) - if match: - if isinstance(self.decoder, SchemaDecoder): - self.decoder.schema = self.__get_schema() - - return prepared_query + def __str__(self): + return "{host: '%s:%s', keyspace: '%s'}"%(self.host,self.port,self.keyspace) - def execute(self, query, *args, **kwargs): - """ - Execute a CQL query on a remote node. - - Params: - * query .........: CQL query string. - * args ..........: Query parameters. - * compression ...: Query compression type (optional). - """ - if kwargs.has_key("compression"): - compress = kwargs.get("compression").upper() - else: - compress = DEFAULT_COMPRESSION - - compressed_query = Connection.compress_query(self.prepare(query, *args), - compress) - request_compression = getattr(Compression, compress) - - try: - response = self.client.execute_cql_query(compressed_query, - request_compression) - except InvalidRequestException, ire: - raise CQLException("Bad Request: %s" % ire.why) - except SchemaDisagreementException, sde: - raise CQLException("schema versions disagree, (try again later).") - except TApplicationException, tapp: - raise CQLException("Internal application error") - except Exception, exc: - raise CQLException(exc) - - if response.type == CqlResultType.ROWS: - return RowsProxy(response.rows, - self._cur_keyspace, - self._cur_column_family, - self.decoder) - - if response.type == CqlResultType.INT: - return response.num - - return None + ### + # Connection API + ### def close(self): + if not self.open_socket: + raise InternalError("Connection has been closed.") + self.transport.close() - - def is_open(self): - return self.transport.isOpen() + self.open_socket = False - @classmethod - def compress_query(cls, query, compression): + def commit(self): """ - Returns a query string compressed with the specified compression type. - - Params: - * query .........: The query string to compress. - * compression ...: Type of compression to use. + 'Database modules that do not support transactions should + implement this method with void functionality.' """ - if not compression in COMPRESSION_SCHEMES: - raise InvalidCompressionScheme(compression) + return - if compression == 'GZIP': - return zlib.compress(query) - - return query - -# vi: ai ts=4 tw=0 sw=4 et + def rollback(self): + raise NotSupportedError("Rollback functionality not present in Cassandra.") + + def cursor(self): + if not self.open_socket: + raise InternalError("Connection has been closed.") + return Cursor(self) diff --git a/drivers/py/cql/cursor.py b/drivers/py/cql/cursor.py new file mode 100644 index 0000000000..c83e8afc38 --- /dev/null +++ b/drivers/py/cql/cursor.py @@ -0,0 +1,225 @@ + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import re +import zlib + +import cql +from cql.marshal import prepare +from cql.decoders import SchemaDecoder +from cql.results import ResultSet +from cql.cassandra.ttypes import (Compression, CqlResultType, InvalidRequestException, + TApplicationException) + +class Cursor: + + _keyspace_re = re.compile("USE (\w+);?", re.I | re.M) + _cfamily_re = re.compile("\s*SELECT\s+.+\s+FROM\s+[\']?(\w+)", re.I | re.M) + _ddl_re = re.compile("\s*(CREATE|ALTER|DROP)\s+", re.I | re.M) + + def __init__(self, parent_connection): + self.open_socket = True + self.parent_connection = parent_connection + + self.result = None # Populate on execute() + self.description = None # A list of 7-tuples: + # (column_name, type_code, none, none, + # none, none, nulls_ok=True) + # Populate on execute() + + self.arraysize = 1 + self.rowcount = -1 # Populate on execute() + self.compression = 'GZIP' + + self._query_ks = self.parent_connection.keyspace + self._query_cf = None + self.decoder = SchemaDecoder(self.__get_schema()) + + ### + # Cursor API + ### + + def close(self): + self.open_socket = False + + def prepare(self, query, params): + prepared_query = prepare(query, params) + + # Snag the keyspace or column family and stash it for later use in + # decoding columns. These regexes don't match every query, but the + # current column family only needs to be current for SELECTs. + match = Cursor._cfamily_re.match(prepared_query) + if match: + self._query_cf = match.group(1) + return prepared_query + match = Cursor._keyspace_re.match(prepared_query) + if match: + self._query_ks = match.group(1) + return prepared_query + + # If this is a CREATE, then refresh the schema for decoding purposes. + match = Cursor._ddl_re.match(prepared_query) + if match: + if isinstance(self.decoder, SchemaDecoder): + self.decoder.schema = self.__get_schema() + + return prepared_query + + def __get_schema(self): + def columns(metadata): + results = {} + for col in metadata: + results[col.name] = col.validation_class + return results + + def column_families(cf_defs): + cfresults = {} + if cf_defs: + for cf in cf_defs: + cfresults[cf.name] = {"comparator": cf.comparator_type} + cfresults[cf.name]["default_validation_class"] = \ + cf.default_validation_class + cfresults[cf.name]["key_validation_class"] = \ + cf.key_validation_class + cfresults[cf.name]["columns"] = columns(cf.column_metadata) + return cfresults + + schema = {} + client = self.parent_connection.client + for ksdef in client.describe_keyspaces(): + schema[ksdef.name] = column_families(ksdef.cf_defs) + return schema + + def execute(self, cql_query, params={}): + self.__checksock() + try: + prepared_q = self.prepare(cql_query, params) + except KeyError, e: + raise cql.ProgrammingError("Unmatched named substitution: " + + "%s not given for %s" % (e, cql_query)) + + if self.compression == 'GZIP': + compressed_q = zlib.compress(prepared_q) + else: + compressed_q = prepared_q + request_compression = getattr(Compression, self.compression) + + try: + client = self.parent_connection.client + response = client.execute_cql_query(compressed_q, request_compression) + except InvalidRequestException, ire: + raise cql.ProgrammingError("Bad Request: %s" % ire.why) + except SchemaDisagreementException, sde: + raise cql.IntegrityError("Schema versions disagree, (try again later).") + except TApplicationException, tapp: + raise cql.InternalError("Internal application error") + + if response.type == CqlResultType.ROWS: + self.result = ResultSet(response.rows, + self._query_ks, + self._query_cf, + self.decoder) + self.rs_idx = 0 + self.rowcount = len(self.result) + self.description = self.result.description + + if response.type == CqlResultType.INT: + self.result = [(response.num,)] + self.rs_idx = 0 + self.rowcount = 1 + # TODO: name could be the COUNT expression + self.description = (None, None, None, None, None, None, None) + + # 'Return values are not defined.' + return True + + def executemany(self, operation_list, argslist): + self.__checksock() + opssize = len(operation_list) + argsize = len(argslist) + + if opssize > argsize: + raise cql.InterfaceError("Operations outnumber args for executemany().") + elif opssize < argsize: + raise cql.InterfaceError("Args outnumber operations for executemany().") + + for idx in xrange(opssize): + self.execute(operation_list[idx], *argslist[idx]) + + def fetchone(self): + self.__checksock() + ret = self.result[self.rs_idx] + self.rs_idx += 1 + self.description = getattr(self.result, 'description', self.description) + return ret + + def fetchmany(self, size=None): + self.__checksock() + if size is None: + size = self.arraysize + end = self.rs_idx + size + ret = self.result[self.rs_idx:end] + self.rs_idx = end + self.description = getattr(self.result, 'description', self.description) + return ret + + def fetchall(self): + self.__checksock() + ret = self.result[self.rs_idx:] + self.rs_idx = len(self.result) + self.description = self.result.description + return ret + + ### + # Iterator extension + ### + + def next(self): + raise Warning("DB-API extension cursor.next() used") + + if self.rs_idx >= len(self.result): + raise StopIteration + return self.fetchone() + + def __iter__(self): + raise Warning("DB-API extension cursor.__iter__() used") + return self + + ### + # Unsupported, unimplemented optionally + ### + + def setinputsizes(self, sizes): + pass # DO NOTHING + + def setoutputsize(self, size, *columns): + pass # DO NOTHING + + def callproc(self, procname, *args): + raise cql.NotSupportedError() + + def nextset(self): + raise cql.NotSupportedError() + + ### + # Helpers + ### + + def __checksock(self): + if not self.open_socket: + raise cql.InternalError("Cursor belonging to %s has been closed." % + (self.parent_connection, )) diff --git a/drivers/py/cql/decoders.py b/drivers/py/cql/decoders.py index 651fa98045..20c065e9d9 100644 --- a/drivers/py/cql/decoders.py +++ b/drivers/py/cql/decoders.py @@ -15,62 +15,53 @@ # See the License for the specific language governing permissions and # limitations under the License. -from os.path import abspath, dirname, join -from marshal import unmarshal +import cql +from marshal import (unmarshallers, unmarshal_noop) -class BaseDecoder(object): - def decode_column(self, keyspace, column_family, name, value): - raise NotImplementedError() - - def decode_key(self, keyspace, column_family, key): - raise NotImplementedError() - -class NoopDecoder(BaseDecoder): - def decode_column(self, keyspace, column_family, name, value): - return (name, value) - - def decode_key(self, keyspace, column_family, key): - return key - -class SchemaDecoder(BaseDecoder): +class SchemaDecoder(object): """ Decode binary column names/values according to schema. """ def __init__(self, schema={}): self.schema = schema - + def __get_column_family_def(self, keyspace, column_family): if self.schema.has_key(keyspace): if self.schema[keyspace].has_key(column_family): return self.schema[keyspace][column_family] return None - + def __comparator_for(self, keyspace, column_family): cfam = self.__get_column_family_def(keyspace, column_family) - if cfam and cfam.has_key("comparator"): + if cfam and "comparator" in cfam: return cfam["comparator"] return None - + def __validator_for(self, keyspace, column_family, name): cfam = self.__get_column_family_def(keyspace, column_family) if cfam: - if cfam["columns"].has_key(name): + if name in cfam["columns"]: return cfam["columns"][name] - else: - return cfam["default_validation_class"] + return cfam["default_validation_class"] return None - - def __keytype_for(self, keyspace, column_family, key): + + def __keytype_for(self, keyspace, column_family): cfam = self.__get_column_family_def(keyspace, column_family) - if cfam and cfam.has_key("key_validation_class"): + if cfam and "key_validation_class" in cfam: return cfam["key_validation_class"] return None - def decode_column(self, keyspace, column_family, name, value): + def decode_row(self, keyspace, column_family, row): + key_type = self.__keytype_for(keyspace, column_family) + key = unmarshallers.get(key_type, unmarshal_noop)(row.key) + description = [(cql.ROW_KEY, key_type, None, None, None, None, None, False)] + comparator = self.__comparator_for(keyspace, column_family) - validator = self.__validator_for(keyspace, column_family, name) - return (unmarshal(name, comparator), unmarshal(value, validator)) - - def decode_key(self, keyspace, column_family, key): - return unmarshal(key, self.__keytype_for(keyspace, column_family, key)) - + unmarshal = unmarshallers.get(comparator, unmarshal_noop) + values = [key] + for column in row.columns: + description.append((unmarshal(column.name), comparator, None, None, None, None, True)) + validator = self.__validator_for(keyspace, column_family, column.name) + values.append(unmarshallers.get(validator, unmarshal_noop)(column.value)) + + return description, values diff --git a/drivers/py/cql/errors.py b/drivers/py/cql/errors.py index 8021616c5a..fa87cd26c5 100644 --- a/drivers/py/cql/errors.py +++ b/drivers/py/cql/errors.py @@ -15,8 +15,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -__all__ = ['InvalidCompressionScheme', 'CQLException', 'InvalidQueryFormat'] +__all__ = ['InvalidCompressionScheme', 'InvalidQueryFormat'] class InvalidCompressionScheme(Exception): pass class InvalidQueryFormat(Exception): pass -class CQLException(Exception): pass diff --git a/drivers/py/cql/marshal.py b/drivers/py/cql/marshal.py index 641d56ffbd..c8fac8a1ec 100644 --- a/drivers/py/cql/marshal.py +++ b/drivers/py/cql/marshal.py @@ -15,68 +15,75 @@ # See the License for the specific language governing permissions and # limitations under the License. +import re +import struct from uuid import UUID -from StringIO import StringIO -from errors import InvalidQueryFormat -from struct import unpack -__all__ = ['prepare', 'marshal', 'unmarshal'] +import cql -def prepare(query, *args): - result = StringIO() - index = query.find('?') - oldindex = 0 - count = 0 - - while (index >= 0): - result.write(query[oldindex:index]) - try: - result.write(marshal(args[count])) - except IndexError: - raise InvalidQueryFormat("not enough arguments in substitution") - - oldindex = index + 1 - index = query.find('?', index + 1) - count += 1 - result.write(query[oldindex:]) - - if count < len(args): - raise InvalidQueryFormat("too many arguments in substitution") - - return result.getvalue() +__all__ = ['prepare', 'marshal', 'unmarshal_noop', 'unmarshallers'] + +if hasattr(struct, 'Struct'): # new in Python 2.5 + _have_struct = True + _long_packer = struct.Struct('>q') +else: + _have_struct = False + +_param_re = re.compile(r"(? count: + raise cql.ProgrammingError("More keywords were provided than parameters") + return new def marshal(term): - if isinstance(term, (long,int)): - return "%d" % term - elif isinstance(term, unicode): + if isinstance(term, unicode): return "'%s'" % __escape_quotes(term.encode('utf8')) elif isinstance(term, str): return "'%s'" % __escape_quotes(term) - elif isinstance(term, UUID): + else: return str(term) + +def unmarshal_noop(bytestr): + return bytestr + +def unmarshal_utf8(bytestr): + return bytestr.decode("utf8") + +def unmarshal_int(bytestr): + return decode_bigint(bytestr) + +def unmarshal_long(bytestr): + if _have_struct: + return _long_packer.unpack(bytestr)[0] else: - return str(term) - -def unmarshal(bytestr, typestr): - if typestr == "org.apache.cassandra.db.marshal.BytesType": - return bytestr - elif typestr == "org.apache.cassandra.db.marshal.AsciiType": - return bytestr - elif typestr == "org.apache.cassandra.db.marshal.UTF8Type": - return bytestr.decode("utf8") - elif typestr == "org.apache.cassandra.db.marshal.IntegerType": - return decode_bigint(bytestr) - elif typestr == "org.apache.cassandra.db.marshal.LongType": return unpack(">q", bytestr)[0] - elif typestr == "org.apache.cassandra.db.marshal.UUIDType": - return UUID(bytes=bytestr) - elif typestr == "org.apache.cassandra.db.marshal.LexicalUUIDType": - return UUID(bytes=bytestr) - elif typestr == "org.apache.cassandra.db.marshal.TimeUUIDType": - return UUID(bytes=bytestr) - else: - return bytestr - + +def unmarshal_uuid(bytestr): + return UUID(bytes=bytestr) + +unmarshallers = {BYTES_TYPE: unmarshal_noop, + ASCII_TYPE: unmarshal_noop, + UTF8_TYPE: unmarshal_utf8, + INTEGER_TYPE: unmarshal_int, + LONG_TYPE: unmarshal_long, + UUID_TYPE: unmarshal_uuid, + LEXICAL_UUID_TYPE: unmarshal_uuid, + TIME_UUID_TYPE: unmarshal_uuid} + def decode_bigint(term): val = int(term.encode('hex'), 16) if (ord(term[0]) & 128) != 0: diff --git a/drivers/py/cql/results.py b/drivers/py/cql/results.py index c878516148..68b27f8b70 100644 --- a/drivers/py/cql/results.py +++ b/drivers/py/cql/results.py @@ -15,84 +15,48 @@ # See the License for the specific language governing permissions and # limitations under the License. -class RowsProxy(object): - def __init__(self, rows, keyspace, cfam, decoder): +class ResultSet(object): + + def __init__(self, rows, keyspace, column_family, decoder): self.rows = rows - self.keyspace = keyspace - self.cfam = cfam + self.ks = keyspace + self.cf = column_family self.decoder = decoder + # We need to try to parse the first row to set the description + if len(self.rows) > 0: + self.description, self._first_vals = self.decoder.decode_row(self.ks, self.cf, self.rows[0]) + else: + self.description, self._first_vals = (None, None) + def __len__(self): return len(self.rows) - + def __getitem__(self, idx): - return Row(self.rows[idx].key, - self.rows[idx].columns, - self.keyspace, - self.cfam, - self.decoder) - + if isinstance(idx, int): + if idx == 0 and self._first_vals: + return self._first_vals + self.description, vals = self.decoder.decode_row(self.ks, self.cf, self.rows[idx]) + return vals + elif isinstance(idx, slice): + num_rows = len(self.rows) + results = [] + for i in xrange(idx.start, min(len(self.rows), idx.stop)): + if i == 0 and self._first_vals: + vals = self._first_vals + else: + self.description, vals = self.decoder.decode_row(self.ks, self.cf, self.rows[i]) + results.append(vals) + return results + else: + raise TypeError + def __iter__(self): + idx = 0 for r in self.rows: - yield Row(r.key, r.columns, self.keyspace, self.cfam, self.decoder) - -class Row(object): - def __init__(self, key, columns, keyspace, cfam, decoder): - self._key = key - self.keyspace = keyspace - self.cfam = cfam - self.decoder = decoder - self.columns = ColumnsProxy(columns, keyspace, cfam, decoder) - - def __get_key(self): - return self.decoder.decode_key(self.keyspace, self.cfam, self._key) - key = property(__get_key) - - def __iter__(self): - return iter(self.columns) - - def __getitem__(self, idx): - return self.columns[idx] - - def __len__(self): - return len(self.columns) - -class ColumnsProxy(object): - def __init__(self, columns, keyspace, cfam, decoder): - self.columns = columns - self.keyspace = keyspace - self.cfam = cfam - self.decoder = decoder - - def __len__(self): - return len(self.columns) - - def __getitem__(self, idx): - return Column(self.decoder.decode_column(self.keyspace, - self.cfam, - self.columns[idx].name, - self.columns[idx].value)) - - def __iter__(self): - for c in self.columns: - yield Column(self.decoder.decode_column(self.keyspace, - self.cfam, - c.name, - c.value)) - - def __str__(self): - return "ColumnsProxy(columns=%s)" % self.columns - - def __repr__(self): - return str(self) - -class Column(object): - def __init__(self, (name, value)): - self.name = name - self.value = value - - def __str__(self): - return "Column(%s, %s)" % (self.name, self.value) - - def __repr__(self): - return str(self) + if idx == 0 and self._first_vals: + yield self._first_vals + else: + self.description, vals = self.decoder.decode_row(self.ks, self.cf, r) + yield vals + idx += 1 diff --git a/drivers/py/cqlsh b/drivers/py/cqlsh index a99b9ba5a9..9b701c0b1c 100755 --- a/drivers/py/cqlsh +++ b/drivers/py/cqlsh @@ -26,14 +26,11 @@ import os import re try: - from cql import Connection - from cql.errors import CQLException - from cql.results import RowsProxy + import cql except ImportError: sys.path.append(os.path.abspath(os.path.dirname(__file__))) - from cql import Connection - from cql.errors import CQLException - from cql.results import RowsProxy + import cql +from cql.results import ResultSet HISTORY = os.path.join(os.path.expanduser('~'), '.cqlsh') CQLTYPES = ("bytes", "ascii", "utf8", "timeuuid", "uuid", "long", "int") @@ -55,10 +52,7 @@ class Shell(cmd.Cmd): def __init__(self, hostname, port, color=False, username=None, password=None): cmd.Cmd.__init__(self) - self.conn = Connection(hostname, - port=port, - username=username, - password=password) + self.conn = cql.connect(hostname, port, user=username, password=password) if os.path.exists(HISTORY): readline.read_history_file(HISTORY) @@ -92,10 +86,11 @@ class Shell(cmd.Cmd): statement = self.get_statement(arg) if not statement: return - result = self.conn.execute(statement) + cursor = self.conn.cursor() + cursor.execute(statement) - if isinstance(result, RowsProxy): - for row in result: + if isinstance(cursor.result, ResultSet): + for row in cursor.result.rows: self.printout(row.key, BLUE, False) for column in row.columns: self.printout(" | ", newline=False) @@ -105,7 +100,7 @@ class Shell(cmd.Cmd): self.printout(repr(column.value), YELLOW, False) self.printout("") else: - if result: print result + if cursor.result: print cursor.result[0] def emptyline(self): pass @@ -221,7 +216,7 @@ if __name__ == '__main__': except SystemExit: readline.write_history_file(HISTORY) break - except CQLException, cqlerr: + except cql.Error, cqlerr: shell.printerr(str(cqlerr), color=RED) except KeyboardInterrupt: shell.reset_statement() diff --git a/drivers/py/test/test_query_compression.py b/drivers/py/test/test_query_compression.py index a0aff7155f..7d161c7a11 100644 --- a/drivers/py/test/test_query_compression.py +++ b/drivers/py/test/test_query_compression.py @@ -15,18 +15,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -from os.path import abspath, exists, join, dirname - -if exists(join(abspath(dirname(__file__)), '..', 'cql')): - import sys; sys.path.append(join(abspath(dirname(__file__)), '..')) - import unittest, zlib -from cql import Connection class TestCompression(unittest.TestCase): def test_gzip(self): "compressing a string w/ gzip" query = "SELECT \"foo\" FROM Standard1 WHERE KEY = \"bar\";" - compressed = Connection.compress_query(query, 'GZIP') + compressed = zlib.compress(query) decompressed = zlib.decompress(compressed) assert query == decompressed, "Decompressed query did not match" diff --git a/drivers/py/test/test_query_preparation.py b/drivers/py/test/test_query_preparation.py index 5cf202907f..95d39a8fcc 100644 --- a/drivers/py/test/test_query_preparation.py +++ b/drivers/py/test/test_query_preparation.py @@ -16,26 +16,26 @@ # limitations under the License. import unittest +import cql from cql.marshal import prepare -from cql.errors import InvalidQueryFormat # TESTS[i] ARGUMENTS[i] -> STANDARDS[i] TESTS = ( """ -SELECT ?,?,?,? FROM ColumnFamily WHERE KEY = ? AND 'col' = ?; +SELECT :a,:b,:c,:d FROM ColumnFamily WHERE KEY = :e AND 'col' = :f; """, """ USE Keyspace; """, """ -SELECT ?..? FROM ColumnFamily; +SELECT :a..:b FROM ColumnFamily; """, ) ARGUMENTS = ( - (1, 3, long(1000), long(3000), "key", unicode("val")), - tuple(), - ("a'b", "c'd'e"), + {'a': 1, 'b': 3, 'c': long(1000), 'd': long(3000), 'e': "key", 'f': unicode("val")}, + {}, + {'a': "a'b", 'b': "c'd'e"}, ) STANDARDS = ( @@ -54,13 +54,12 @@ class TestPrepare(unittest.TestCase): def test_prepares(self): "test prepared queries against known standards" for (i, test) in enumerate(TESTS): - a = prepare(test, *ARGUMENTS[i]) + a = prepare(test, **ARGUMENTS[i]) b = STANDARDS[i] assert a == b, "\n%s !=\n%s" % (a, b) - + def test_bad(self): "ensure bad calls raise exceptions" - self.assertRaises(InvalidQueryFormat, prepare, "? ?", 1) - self.assertRaises(InvalidQueryFormat, prepare, "? ?", 1, 2, 3) - self.assertRaises(InvalidQueryFormat, prepare, "none", 1) - + self.assertRaises(KeyError, prepare, ":a :b", a=1) + self.assertRaises(cql.ProgrammingError, prepare, ":a :b", a=1, b=2, c=3) + self.assertRaises(cql.ProgrammingError, prepare, "none", a=1) diff --git a/lib/licenses/guava-r05.txt b/lib/licenses/guava-r08.txt similarity index 100% rename from lib/licenses/guava-r05.txt rename to lib/licenses/guava-r08.txt diff --git a/src/java/org/apache/cassandra/cache/AutoSavingCache.java b/src/java/org/apache/cassandra/cache/AutoSavingCache.java index 566d4c09e2..7609672246 100644 --- a/src/java/org/apache/cassandra/cache/AutoSavingCache.java +++ b/src/java/org/apache/cassandra/cache/AutoSavingCache.java @@ -36,6 +36,7 @@ import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.CompactionManager; import org.apache.cassandra.db.DecoratedKey; import org.apache.cassandra.io.CompactionInfo; +import org.apache.cassandra.io.CompactionType; import org.apache.cassandra.io.util.BufferedRandomAccessFile; import org.apache.cassandra.io.util.FileUtils; import org.apache.cassandra.service.StorageService; @@ -203,9 +204,18 @@ public abstract class AutoSavingCache extends InstrumentingCache bytes += translateKey(key).remaining(); // an approximation -- the keyset can change while saving estimatedTotalBytes = bytes; + CompactionType type; + + if (cacheType.equals(ColumnFamilyStore.CacheType.KEY_CACHE_TYPE)) + type = CompactionType.KEY_CACHE_SAVE; + else if (cacheType.equals(ColumnFamilyStore.CacheType.ROW_CACHE_TYPE)) + type = CompactionType.ROW_CACHE_SAVE; + else + type = CompactionType.UNKNOWN; + info = new CompactionInfo(ksname, cfname, - "Save " + getCachePath().getName(), + type, 0, estimatedTotalBytes); } diff --git a/src/java/org/apache/cassandra/cli/Cli.g b/src/java/org/apache/cassandra/cli/Cli.g index e043378f7a..1b77fee997 100644 --- a/src/java/org/apache/cassandra/cli/Cli.g +++ b/src/java/org/apache/cassandra/cli/Cli.g @@ -119,7 +119,7 @@ package org.apache.cassandra.cli; if (e instanceof NoViableAltException) { - errorMessage = "Command not found: `" + this.input + "`. Type 'help' or '?' for help."; + errorMessage = "Command not found: `" + this.input + "`. Type 'help;' or '?' for help."; } else { diff --git a/src/java/org/apache/cassandra/cli/CliClient.java b/src/java/org/apache/cassandra/cli/CliClient.java index 44d063056d..45b110b43b 100644 --- a/src/java/org/apache/cassandra/cli/CliClient.java +++ b/src/java/org/apache/cassandra/cli/CliClient.java @@ -17,7 +17,9 @@ */ package org.apache.cassandra.cli; +import java.io.IOError; import java.io.IOException; +import java.io.InputStream; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; @@ -34,6 +36,7 @@ import org.apache.cassandra.db.ColumnFamilyStoreMBean; import org.apache.cassandra.db.CompactionManagerMBean; import org.apache.cassandra.db.marshal.*; import org.apache.cassandra.io.CompactionInfo; +import org.apache.cassandra.io.CompactionType; import org.apache.cassandra.locator.SimpleSnitch; import org.apache.cassandra.service.StorageProxy; import org.apache.cassandra.thrift.*; @@ -43,9 +46,13 @@ import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.UUIDGen; import org.apache.thrift.TBaseHelper; import org.apache.thrift.TException; +import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.Loader; +import org.yaml.snakeyaml.TypeDescription; +import org.yaml.snakeyaml.Yaml; // Cli Client Side Library -public class CliClient extends CliUserHelp +public class CliClient { /** @@ -98,6 +105,34 @@ public class CliClient extends CliUserHelp STRATEGY_OPTIONS } + /* + * the add column family command requires a list of arguments, + * this enum defines which arguments are valid. + */ + protected enum ColumnFamilyArgument + { + COLUMN_TYPE, + COMPARATOR, + SUBCOMPARATOR, + COMMENT, + ROWS_CACHED, + ROW_CACHE_SAVE_PERIOD, + KEYS_CACHED, + KEY_CACHE_SAVE_PERIOD, + READ_REPAIR_CHANCE, + GC_GRACE, + COLUMN_METADATA, + MEMTABLE_OPERATIONS, + MEMTABLE_THROUGHPUT, + MEMTABLE_FLUSH_AFTER, + DEFAULT_VALIDATION_CLASS, + MIN_COMPACTION_THRESHOLD, + MAX_COMPACTION_THRESHOLD, + REPLICATE_ON_WRITE, + ROW_CACHE_PROVIDER, + KEY_VALIDATION_CLASS + } + private static final String DEFAULT_PLACEMENT_STRATEGY = "org.apache.cassandra.locator.NetworkTopologyStrategy"; private Cassandra.Client thriftClient = null; @@ -106,13 +141,45 @@ public class CliClient extends CliUserHelp private String username = null; private Map keyspacesMap = new HashMap(); private Map cfKeysComparators; - private ConsistencyLevel consistencyLevel = ConsistencyLevel.ONE; - + private ConsistencyLevel consistencyLevel = ConsistencyLevel.ONE; + private CliUserHelp help; public CliClient(CliSessionState cliSessionState, Cassandra.Client thriftClient) { this.sessionState = cliSessionState; this.thriftClient = thriftClient; this.cfKeysComparators = new HashMap(); + help = getHelp(); + } + + private CliUserHelp getHelp() + { + final InputStream is = CliClient.class.getClassLoader().getResourceAsStream("org/apache/cassandra/cli/CliHelp.yaml"); + assert is != null; + + try + { + final Constructor constructor = new Constructor(CliUserHelp.class); + TypeDescription desc = new TypeDescription(CliUserHelp.class); + desc.putListPropertyType("commands", CliCommandHelp.class); + final Yaml yaml = new Yaml(new Loader(constructor)); + return (CliUserHelp)yaml.load(is); + } + finally + { + try + { + is.close(); + } + catch (IOException e) + { + throw new IOError(e); + } + } + } + + public void printBanner() + { + sessionState.out.println(help.banner); } // Execute a CLI Statement @@ -134,7 +201,7 @@ public class CliClient extends CliUserHelp executeGetWithConditions(tree); break; case CliParser.NODE_HELP: - printCmdHelp(tree, sessionState); + executeHelp(tree); break; case CliParser.NODE_THRIFT_SET: executeSet(tree); @@ -240,7 +307,27 @@ public class CliClient extends CliUserHelp return keyspacesMap.get(keyspace); } - + + private void executeHelp(Tree tree) + { + if (tree.getChildCount() > 0) + { + String token = tree.getChild(0).getText(); + for (CliCommandHelp ch : help.commands) + { + if (token.equals(ch.name)) + { + sessionState.out.println(ch.help); + break; + } + } + } + else + { + sessionState.out.println(help.help); + } + } + private void executeCount(Tree statement) throws TException, InvalidRequestException, UnavailableException, TimedOutException { @@ -677,7 +764,7 @@ public class CliClient extends CliUserHelp // table.cf['key'] if (columnSpecCnt == 0) { - sessionState.err.println("No column name specified, (type 'help' or '?' for help on syntax)."); + sessionState.err.println("No column name specified, (type 'help;' or '?' for help on syntax)."); return; } // table.cf['key']['column'] = 'value' @@ -1552,7 +1639,7 @@ public class CliClient extends CliUserHelp for (CompactionInfo info : compactionManagerMBean.getCompactions()) { // if ongoing compaction type is index build - if (!info.getTaskType().contains("index build")) + if (info.getTaskType() != CompactionType.INDEX_BUILD) continue; sessionState.out.printf("%nCurrently building index %s, completed %d of %d bytes.%n", info.getColumnFamily(), diff --git a/src/java/org/apache/cassandra/cli/CliCommandHelp.java b/src/java/org/apache/cassandra/cli/CliCommandHelp.java new file mode 100644 index 0000000000..d4e3b34c07 --- /dev/null +++ b/src/java/org/apache/cassandra/cli/CliCommandHelp.java @@ -0,0 +1,24 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.cassandra.cli; + +public class CliCommandHelp +{ + public String name; + public String help; +} \ No newline at end of file diff --git a/src/java/org/apache/cassandra/cli/CliMain.java b/src/java/org/apache/cassandra/cli/CliMain.java index 4331895e47..47541d684a 100644 --- a/src/java/org/apache/cassandra/cli/CliMain.java +++ b/src/java/org/apache/cassandra/cli/CliMain.java @@ -181,12 +181,6 @@ public class CliMain } } - private static void printBanner() - { - sessionState.out.println("Welcome to cassandra CLI.\n"); - sessionState.out.println("Type 'help;' or '?' for help. Type 'quit;' or 'exit;' to quit."); - } - /** * Checks whether the thrift client is connected. * @return boolean - true when connected, false otherwise @@ -315,7 +309,7 @@ public class CliMain sessionState.out.close(); } - printBanner(); + cliClient.printBanner(); String prompt; String line = ""; diff --git a/src/java/org/apache/cassandra/cli/CliUserHelp.java b/src/java/org/apache/cassandra/cli/CliUserHelp.java index 5dee2462f1..a64bf8791e 100644 --- a/src/java/org/apache/cassandra/cli/CliUserHelp.java +++ b/src/java/org/apache/cassandra/cli/CliUserHelp.java @@ -17,401 +17,16 @@ */ package org.apache.cassandra.cli; -import java.util.EnumMap; - -import org.antlr.runtime.tree.Tree; +import java.util.List; /** * @author Pavel A. Yaskevich */ -public class CliUserHelp { +public class CliUserHelp +{ + public String banner; - /* - * the add column family command requires a list of arguments, - * this enum defines which arguments are valid. - */ - protected enum ColumnFamilyArgument - { - COLUMN_TYPE, - COMPARATOR, - SUBCOMPARATOR, - COMMENT, - ROWS_CACHED, - ROW_CACHE_SAVE_PERIOD, - KEYS_CACHED, - KEY_CACHE_SAVE_PERIOD, - READ_REPAIR_CHANCE, - GC_GRACE, - COLUMN_METADATA, - MEMTABLE_OPERATIONS, - MEMTABLE_THROUGHPUT, - MEMTABLE_FLUSH_AFTER, - DEFAULT_VALIDATION_CLASS, - MIN_COMPACTION_THRESHOLD, - MAX_COMPACTION_THRESHOLD, - REPLICATE_ON_WRITE, - ROW_CACHE_PROVIDER, - KEY_VALIDATION_CLASS - } - - protected EnumMap argumentExplanations = new EnumMap(ColumnFamilyArgument.class) - {{ - put(ColumnFamilyArgument.COLUMN_TYPE, "Super or Standard"); - put(ColumnFamilyArgument.COMMENT, "Human-readable column family description. Any string is acceptable"); - put(ColumnFamilyArgument.COMPARATOR, "The class used as a comparator when sorting column names.\n Valid options include: AsciiType, BytesType, LexicalUUIDType,\n LongType, IntegerType, TimeUUIDType, and UTF8Type"); - put(ColumnFamilyArgument.SUBCOMPARATOR, "Comparator for sorting subcolumn names, for Super columns only"); - put(ColumnFamilyArgument.MEMTABLE_OPERATIONS, "Flush memtables after this many operations (in millions)"); - put(ColumnFamilyArgument.MEMTABLE_THROUGHPUT, "... or after this many MB have been written"); - put(ColumnFamilyArgument.MEMTABLE_FLUSH_AFTER, "... or after this many minutes"); - put(ColumnFamilyArgument.ROWS_CACHED, "Number or percentage of rows to cache"); - put(ColumnFamilyArgument.ROW_CACHE_SAVE_PERIOD, "Period with which to persist the row cache, in seconds"); - put(ColumnFamilyArgument.KEYS_CACHED, "Number or percentage of keys to cache"); - put(ColumnFamilyArgument.KEY_CACHE_SAVE_PERIOD, "Period with which to persist the key cache, in seconds"); - put(ColumnFamilyArgument.READ_REPAIR_CHANCE, "Probability (0.0-1.0) with which to perform read repairs on CL.ONE reads"); - put(ColumnFamilyArgument.GC_GRACE, "Discard tombstones after this many seconds"); - put(ColumnFamilyArgument.MIN_COMPACTION_THRESHOLD, "Avoid minor compactions of less than this number of sstable files"); - put(ColumnFamilyArgument.MAX_COMPACTION_THRESHOLD, "Compact no more than this number of sstable files at once"); - put(ColumnFamilyArgument.REPLICATE_ON_WRITE, "Replicate every counter update from the leader to the follower replicas"); - put(ColumnFamilyArgument.ROW_CACHE_PROVIDER, "Row cache provider, opions are SerializingProvider/ConcurrentLinkedHashCacheProvider"); - }}; - - protected void printCmdHelp(Tree statement, CliSessionState state) - { - if (statement.getChildCount() > 0) - { - int helpType = statement.getChild(0).getType(); - - switch(helpType) - { - case CliParser.NODE_HELP: - state.out.println("help ;\n"); - state.out.println("Display the general help page with a list of available commands."); - break; - case CliParser.NODE_CONNECT: - state.out.println("connect / ( '')?;\n"); - state.out.println("Connect to the specified host on the specified port (using specified username and password).\n"); - state.out.println("example:"); - state.out.println("connect localhost/9160;"); - state.out.println("connect localhost/9160 user 'badpasswd';"); - state.out.println("connect 127.0.0.1/9160 user 'badpasswd';"); - break; - - case CliParser.NODE_USE_TABLE: - state.out.println("use ;"); - state.out.println("use '';\n"); - state.out.println("Switch to the specified keyspace. The optional username and password fields"); - state.out.println("are needed when performing authentication.\n"); - break; - - case CliParser.NODE_DESCRIBE_TABLE: - state.out.println("describe keyspace ()?;\n"); - state.out.println("Show additional information about the specified keyspace."); - state.out.println("Command could be used without argument if you are already authenticated to keyspace.\n"); - state.out.println("example:"); - state.out.println("describe keyspace system;"); - break; - - case CliParser.NODE_DESCRIBE_CLUSTER: - state.out.println("describe cluster;\n"); - state.out.println("Display information about cluster: snitch, partitioner, schema versions."); - break; - - case CliParser.NODE_EXIT: - state.out.println("exit;"); - state.out.println("quit;\n"); - state.out.println("Exit this utility."); - break; - - case CliParser.NODE_SHOW_CLUSTER_NAME: - state.out.println("show cluster name;\n"); - state.out.println("Displays the name of the currently connected cluster."); - break; - - case CliParser.NODE_SHOW_VERSION: - state.out.println("show api version;\n"); - state.out.println("Displays the API version number."); - break; - - case CliParser.NODE_SHOW_KEYSPACES: - state.out.println("show keyspaces;\n"); - state.out.println("Displays a list of the keyspaces available on the currently connected cluster."); - break; - - case CliParser.NODE_ADD_KEYSPACE: - state.out.println("create keyspace ;"); - state.out.println("create keyspace with =;"); - state.out.println("create keyspace with = and = ...;\n"); - state.out.println("Create a new keyspace with the specified values for the given set of attributes.\n"); - state.out.println("valid attributes are:"); - state.out.println(" placement_strategy: the fully qualified class used to place replicas in"); - state.out.println(" this keyspace. Valid values are"); - state.out.println(" org.apache.cassandra.locator.SimpleStrategy,"); - state.out.println(" org.apache.cassandra.locator.NetworkTopologyStrategy,"); - state.out.println(" and org.apache.cassandra.locator.OldNetworkTopologyStrategy"); - state.out.println(" strategy_options: additional options for placement_strategy."); - state.out.println(" Simple and OldNetworkTopology strategies require"); - state.out.println(" 'replication_factor':N, and NetworkTopologyStrategy"); - state.out.println(" requires a map of 'DCName':N per-DC."); - state.out.println("\nexamples:"); - state.out.println("create keyspace foo with"); - state.out.println(" placement_strategy = 'org.apache.cassandra.locator.SimpleStrategy'"); - state.out.println(" and strategy_options=[{replication_factor:3}];"); - state.out.println("create keyspace foo with"); - state.out.println(" placement_strategy = 'org.apache.cassandra.locator.NetworkTopologyStrategy';"); - state.out.println(" and strategy_options=[{DC1:2, DC2:2}];"); - break; - - case CliParser.NODE_UPDATE_KEYSPACE: - state.out.println("update keyspace ;"); - state.out.println("update keyspace with =;"); - state.out.println("update keyspace with = and = ...;\n"); - state.out.println("Update a keyspace with the specified values for the given set of attributes.\n"); - state.out.println("valid attributes are:"); - state.out.println(" placement_strategy: the fully qualified class used to place replicas in"); - state.out.println(" this keyspace. Valid values are"); - state.out.println(" org.apache.cassandra.locator.SimpleStrategy,"); - state.out.println(" org.apache.cassandra.locator.NetworkTopologyStrategy,"); - state.out.println(" and org.apache.cassandra.locator.OldNetworkTopologyStrategy"); - state.out.println(" strategy_options: additional options for placement_strategy."); - state.out.println(" Simple and OldNetworkTopology strategies require"); - state.out.println(" 'replication_factor':N, and NetworkTopologyStrategy"); - state.out.println(" requires a map of 'DCName':N per-DC."); - state.out.println("\nexamples:"); - state.out.println("update keyspace foo with"); - state.out.println(" placement_strategy = 'org.apache.cassandra.locator.SimpleStrategy'"); - state.out.println(" and strategy_options=[{replication_factor:4}];"); - state.out.println("update keyspace foo with"); - state.out.println(" placement_strategy = 'org.apache.cassandra.locator.NetworkTopologyStrategy';"); - state.out.println(" and strategy_options=[{DC1:3, DC2:2}];"); - break; - - case CliParser.NODE_ADD_COLUMN_FAMILY: - state.out.println("create column family Bar;"); - state.out.println("create column family Bar with =;"); - state.out.println("create column family Bar with = and =...;\n"); - state.out.println("Create a new column family with the specified values for the given set of"); - state.out.println("attributes. Note that you must be using a keyspace.\n"); - state.out.println("valid attributes are:"); - for (ColumnFamilyArgument argument : ColumnFamilyArgument.values()) - state.out.printf(" - %s: %s%n", argument.toString().toLowerCase(), argumentExplanations.get(argument)); - state.out.println(" - column_metadata: Metadata which describes columns of column family."); - state.out.println(" Supported format is [{ k:v, k:v, ... }, { ... }, ...]"); - state.out.println(" Valid attributes: column_name, validation_class (see comparator),"); - state.out.println(" index_type (integer), index_name."); - state.out.println("example:\n"); - state.out.println("create column family Bar with column_type = 'Super' and comparator = 'AsciiType'"); - state.out.println(" and rows_cached = 10000;"); - state.out.println("create column family Baz with comparator = 'LongType' and rows_cached = 10000;"); - state.out.print("create column family Foo with comparator=LongType and column_metadata="); - state.out.print("[{ column_name:Test, validation_class:IntegerType, index_type:0, index_name:IdxName"); - state.out.println("}, { column_name:'other name', validation_class:LongType }];"); - break; - - case CliParser.NODE_UPDATE_COLUMN_FAMILY: - state.out.println("update column family Bar;"); - state.out.println("update column family Bar with =;"); - state.out.println("update column family Bar with = and =...;\n"); - state.out.println("Update a column family with the specified values for the given set of"); - state.out.println("attributes. Note that you must be using a keyspace.\n"); - state.out.println("valid attributes are:"); - for (ColumnFamilyArgument argument : ColumnFamilyArgument.values()) - { - if (argument == ColumnFamilyArgument.COMPARATOR || argument == ColumnFamilyArgument.SUBCOMPARATOR) - continue; - state.out.printf(" - %s: %s%n", argument.toString().toLowerCase(), argumentExplanations.get(argument)); - } - state.out.println(" - column_metadata: Metadata which describes columns of column family."); - state.out.println(" Supported format is [{ k:v, k:v, ... }, { ... }, ...]"); - state.out.println(" Valid attributes: column_name, validation_class (see comparator),"); - state.out.println(" index_type (integer), index_name."); - state.out.println("example:\n"); - state.out.print("update column family Foo with column_metadata="); - state.out.print("[{ column_name:Test, validation_class:IntegerType, index_type:0, index_name:IdxName"); - state.out.println("}] and rows_cached=100 and comment='this is helpful comment.';"); - break; - - case CliParser.NODE_DEL_KEYSPACE: - state.out.println("drop keyspace ;\n"); - state.out.println("Drops the specified keyspace.\n"); - state.out.println("example:"); - state.out.println("drop keyspace foo;"); - break; - - case CliParser.NODE_DEL_COLUMN_FAMILY: - state.out.println("drop column family ;\n"); - state.out.println("Drops the specified column family.\n"); - state.out.println("example:"); - state.out.println("drop column family foo;"); - break; - - case CliParser.NODE_THRIFT_GET : - state.out.println("get [''];"); - state.out.println("get [''][''] (as )*;"); - state.out.println("get [''][''];"); - state.out.println("get [''][];"); - state.out.println("get [''][()][()];"); - state.out.println("get where = [and > and ...] [limit ];"); - state.out.println("Default LIMIT is 100. Available operations: =, >, >=, <, <=\n"); - state.out.println("get [''][''][''] (as )*;"); - state.out.print("Note: `as ` is optional, it dynamically converts column value to the specified type"); - state.out.println(", column value validator will be set to ."); - state.out.println("Available functions: " + CliClient.Function.getFunctionNames()); - state.out.println("Available types: IntegerType, LongType, UTF8Type, ASCIIType, TimeUUIDType, LexicalUUIDType.\n"); - state.out.println("examples:"); - state.out.println("get bar[testkey];"); - state.out.println("get bar[testkey][test_column] as IntegerType;"); - state.out.println("get bar[testkey][utf8(hello)];"); - break; - - case CliParser.NODE_THRIFT_SET: - state.out.println("set [''][''] = ;"); - state.out.println("set [''][''][''] = ;"); - state.out.println("set [''][''] = ();"); - state.out.println("set [''][''][''] = ();"); - state.out.println("set [][()] = || ;"); - state.out.println("set [][() || ] = || with ttl = ;"); - state.out.println("Available functions: " + CliClient.Function.getFunctionNames() + "\n"); - state.out.println("examples:"); - state.out.println("set bar['testkey']['my super']['test col']='this is a test';"); - state.out.println("set baz['testkey']['test col']='this is also a test';"); - state.out.println("set diz[testkey][testcol] = utf8('this is utf8 string.');"); - state.out.println("set bar[testkey][timeuuid()] = utf('hello world');"); - state.out.println("set bar[testkey][timeuuid()] = utf('hello world') with ttl = 30;"); - state.out.println("set diz[testkey][testcol] = 'this is utf8 string.' with ttl = 150;"); - break; - - case CliParser.NODE_THRIFT_INCR: - state.out.println("incr [''][''] [by ];"); - state.out.println("incr [''][''][''] [by ];"); - state.out.println("examples:"); - state.out.println("incr bar['testkey']['my super']['test col'];"); - state.out.println("incr bar['testkey']['my super']['test col'] by 42;"); - state.out.println("incr baz['testkey']['test col'] by -4;"); - break; - - case CliParser.NODE_THRIFT_DECR: - state.out.println("decr [''][''] [by ];"); - state.out.println("decr [''][''][''] [by ];"); - state.out.println("examples:"); - state.out.println("decr bar['testkey']['my super']['test col'];"); - state.out.println("decr bar['testkey']['my super']['test col'] by 42;"); - state.out.println("decr baz['testkey']['test col'] by 10;"); - break; - - case CliParser.NODE_THRIFT_DEL: - state.out.println("del [''];"); - state.out.println("del [''][''];"); - state.out.println("del [''][''][''];\n"); - state.out.println("Deletes a record, a column, or a subcolumn.\n"); - state.out.println("example:"); - state.out.println("del bar['testkey']['my super']['test col'];"); - state.out.println("del baz['testkey']['test col'];"); - state.out.println("del baz['testkey'];"); - break; - - case CliParser.NODE_THRIFT_COUNT: - state.out.println("count [''];"); - state.out.println("count [''][''];\n"); - state.out.println("Count the number of columns in the specified key or subcolumns in the specified"); - state.out.println("super column.\n"); - state.out.println("example:"); - state.out.println("count bar['testkey']['my super'];"); - state.out.println("count baz['testkey'];"); - break; - - case CliParser.NODE_LIST: - state.out.println("list ;"); - state.out.println("list [:];"); - state.out.println("list [:];"); - state.out.println("list ... limit N;"); - state.out.println("List a range of rows in the column or supercolumn family.\n"); - state.out.println("example:"); - state.out.println("list Users[j:] limit 40;"); - break; - - case CliParser.NODE_TRUNCATE: - state.out.println("truncate ;"); - state.out.println("Truncate specified column family.\n"); - state.out.println("example:"); - state.out.println("truncate Category;"); - break; - - case CliParser.NODE_ASSUME: - state.out.println("assume comparator as ;"); - state.out.println("assume sub_comparator as ;"); - state.out.println("assume validator as ;"); - state.out.println("assume keys as ;\n"); - state.out.println("Assume one of the attributes (comparator, sub_comparator, validator or keys)"); - state.out.println("of the given column family to match specified type. Available types: " + CliClient.Function.getFunctionNames()); - state.out.println("example:"); - state.out.println("assume Users comparator as lexicaluuid;"); - break; - case CliParser.NODE_CONSISTENCY_LEVEL: - state.out.println("consistencylevel as "); - state.out.println("example:"); - state.out.println("consistencylevel as QUORUM"); - break; - default: - state.out.println("?"); - break; - } - } - else - { - state.out.println("List of all CLI commands:"); - state.out.println("? Display this message."); - state.out.println("help; Display this help."); - state.out.println("help ; Display detailed, command-specific help."); - state.out.println("connect / ( '')?; Connect to thrift service."); - state.out.println("use [ 'password']; Switch to a keyspace."); - state.out.println("describe keyspace ()?; Describe keyspace."); - state.out.println("exit; Exit CLI."); - state.out.println("quit; Exit CLI."); - state.out.println("describe cluster; Display information about cluster."); - state.out.println("show cluster name; Display cluster name."); - state.out.println("show keyspaces; Show list of keyspaces."); - state.out.println("show api version; Show server API version."); - state.out.println("create keyspace [with = [and = ...]];"); - state.out.println(" Add a new keyspace with the specified attribute(s) and value(s)."); - state.out.println("update keyspace [with = [and = ...]];"); - state.out.println(" Update a keyspace with the specified attribute(s) and value(s)."); - state.out.println("create column family [with = [and = ...]];"); - state.out.println(" Create a new column family with the specified attribute(s) and value(s)."); - state.out.println("update column family [with = [and = ...]];"); - state.out.println(" Update a column family with the specified attribute(s) and value(s)."); - state.out.println("drop keyspace ; Delete a keyspace."); - state.out.println("drop column family ; Delete a column family."); - state.out.println("get ['']; Get a slice of columns."); - state.out.println("get ['']['']; Get a slice of sub columns."); - state.out.println("get where = [and > and ...] [limit int]; "); - state.out.println("get [''][''] (as )*; Get a column value."); - state.out.println("get [''][''][''] (as )*; Get a sub column value."); - state.out.println("set [''][''] = (with ttl = )*; Set a column."); - state.out.println("set [''][''][''] = (with ttl = )*;"); - state.out.println(" Set a sub column."); - state.out.println("del ['']; Delete record."); - state.out.println("del ['']['']; Delete column."); - state.out.println("del ['']['']['']; Delete sub column."); - state.out.println("count ['']; Count columns in record."); - state.out.println("count ['']['']; Count columns in a super column."); - state.out.println("incr [''][''] [by ]; Increment a counter column."); - state.out.println("incr [''][''][''] [by ];"); - state.out.println(" Increment a counter sub-column."); - state.out.println("decr [''][''] [by ]; Decrement a counter column."); - state.out.println("decr [''][''][''] [by ];"); - state.out.println(" Decrement a counter sub-column."); - state.out.println("truncate ; Truncate specified column family."); - state.out.println("assume as ;"); - state.out.println(" Assume a given column family attributes to match a specified type."); - state.out.println("consistencylevel as ;"); - state.out.println(" Change the consistency level for set,get, and list operations."); - state.out.println("list ; List all rows in the column family."); - state.out.println("list [:];"); - state.out.println(" List rows in the column family beginning with ."); - state.out.println("list [:];"); - state.out.println(" List rows in the column family in the range from to ."); - state.out.println("list ... limit N; Limit the list results to N."); - } - } + public String help; + public List commands; } diff --git a/src/java/org/apache/cassandra/config/CFMetaData.java b/src/java/org/apache/cassandra/config/CFMetaData.java index c6b1e18ca8..bff93d9e76 100644 --- a/src/java/org/apache/cassandra/config/CFMetaData.java +++ b/src/java/org/apache/cassandra/config/CFMetaData.java @@ -657,6 +657,7 @@ public final class CFMetaData if (cf_def.isSetMerge_shards_chance()) { newCFMD.mergeShardsChance(cf_def.merge_shards_chance); } if (cf_def.isSetRow_cache_provider()) { newCFMD.rowCacheProvider(FBUtilities.newCacheProvider(cf_def.row_cache_provider)); } if (cf_def.isSetKey_alias()) { newCFMD.keyAlias(cf_def.key_alias); } + if (cf_def.isSetKey_validation_class()) { newCFMD.keyValidator(DatabaseDescriptor.getComparator(cf_def.key_validation_class)); } return newCFMD.comment(cf_def.comment) .rowCacheSize(cf_def.row_cache_size) diff --git a/src/java/org/apache/cassandra/db/CompactionManager.java b/src/java/org/apache/cassandra/db/CompactionManager.java index 8369d2efc0..1ecbbcad5b 100644 --- a/src/java/org/apache/cassandra/db/CompactionManager.java +++ b/src/java/org/apache/cassandra/db/CompactionManager.java @@ -28,7 +28,6 @@ import java.security.MessageDigest; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.management.MBeanServer; @@ -529,7 +528,9 @@ public class CompactionManager implements CompactionManagerMBean // so in our single-threaded compaction world this is a valid way of determining if we're compacting // all the sstables (that existed when we started) boolean major = cfs.isCompleteSSTables(sstables); - String type = major ? "Major" : "Minor"; + CompactionType type = major + ? CompactionType.MAJOR + : CompactionType.MINOR; logger.info("Compacting {}: {}", type, sstables); long startTime = System.currentTimeMillis(); @@ -1147,7 +1148,7 @@ public class CompactionManager implements CompactionManagerMBean { public ValidationCompactionIterator(ColumnFamilyStore cfs, Range range) throws IOException { - super("Validation", + super(CompactionType.VALIDATION, getCollatingIterator(cfs.getSSTables(), range), new CompactionController(cfs, cfs.getSSTables(), true, getDefaultGcBefore(cfs), false)); } @@ -1348,7 +1349,7 @@ public class CompactionManager implements CompactionManagerMBean { return new CompactionInfo(sstable.descriptor.ksname, sstable.descriptor.cfname, - "Cleanup of " + sstable.getColumnFamilyName(), + CompactionType.CLEANUP, scanner.getFilePointer(), scanner.getFileLength()); } @@ -1375,7 +1376,7 @@ public class CompactionManager implements CompactionManagerMBean { return new CompactionInfo(sstable.descriptor.ksname, sstable.descriptor.cfname, - "Scrub " + sstable, + CompactionType.SCRUB, dataFile.getFilePointer(), dataFile.length()); } diff --git a/src/java/org/apache/cassandra/db/Table.java b/src/java/org/apache/cassandra/db/Table.java index 4161882a0d..77c385623d 100644 --- a/src/java/org/apache/cassandra/db/Table.java +++ b/src/java/org/apache/cassandra/db/Table.java @@ -40,6 +40,7 @@ import org.apache.cassandra.db.filter.QueryFilter; import org.apache.cassandra.db.filter.QueryPath; import org.apache.cassandra.dht.LocalToken; import org.apache.cassandra.io.CompactionInfo; +import org.apache.cassandra.io.CompactionType; import org.apache.cassandra.io.sstable.ReducingKeyIterator; import org.apache.cassandra.io.sstable.SSTableDeletingReference; import org.apache.cassandra.io.sstable.SSTableReader; @@ -614,7 +615,7 @@ public class Table { return new CompactionInfo(cfs.table.name, cfs.columnFamily, - String.format("Secondary index build %s", cfs.columnFamily), + CompactionType.INDEX_BUILD, iter.getTotalBytes(), iter.getBytesRead()); } diff --git a/src/java/org/apache/cassandra/io/CompactionInfo.java b/src/java/org/apache/cassandra/io/CompactionInfo.java index f4d1d29515..147197d89d 100644 --- a/src/java/org/apache/cassandra/io/CompactionInfo.java +++ b/src/java/org/apache/cassandra/io/CompactionInfo.java @@ -23,13 +23,15 @@ import java.io.Serializable; /** Implements serializable to allow structured info to be returned via JMX. */ public final class CompactionInfo implements Serializable { + + private final String ksname; private final String cfname; - private final String tasktype; + private final CompactionType tasktype; private final long bytesComplete; private final long totalBytes; - public CompactionInfo(String ksname, String cfname, String tasktype, long bytesComplete, long totalBytes) + public CompactionInfo(String ksname, String cfname, CompactionType tasktype, long bytesComplete, long totalBytes) { this.ksname = ksname; this.cfname = cfname; @@ -64,7 +66,7 @@ public final class CompactionInfo implements Serializable return totalBytes; } - public String getTaskType() + public CompactionType getTaskType() { return tasktype; } diff --git a/src/java/org/apache/cassandra/io/CompactionIterator.java b/src/java/org/apache/cassandra/io/CompactionIterator.java index 892295fd57..744e009082 100644 --- a/src/java/org/apache/cassandra/io/CompactionIterator.java +++ b/src/java/org/apache/cassandra/io/CompactionIterator.java @@ -24,18 +24,14 @@ package org.apache.cassandra.io; import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; -import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Set; import org.apache.commons.collections.iterators.CollatingIterator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.cassandra.config.DatabaseDescriptor; -import org.apache.cassandra.db.ColumnFamily; -import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.CompactionManager; import org.apache.cassandra.io.sstable.SSTableIdentityIterator; import org.apache.cassandra.io.sstable.SSTableReader; @@ -53,7 +49,7 @@ implements Closeable, CompactionInfo.Holder public static final int FILE_BUFFER_SIZE = 1024 * 1024; protected final List rows = new ArrayList(); - protected final String type; + protected final CompactionType type; protected final CompactionController controller; private long totalBytes; @@ -68,13 +64,13 @@ implements Closeable, CompactionInfo.Holder // current target bytes to compact per millisecond private int targetBytesPerMS = -1; - public CompactionIterator(String type, Iterable sstables, CompactionController controller) throws IOException + public CompactionIterator(CompactionType type, Iterable sstables, CompactionController controller) throws IOException { this(type, getCollatingIterator(sstables), controller); } @SuppressWarnings("unchecked") - protected CompactionIterator(String type, Iterator iter, CompactionController controller) + protected CompactionIterator(CompactionType type, Iterator iter, CompactionController controller) { super(iter); this.type = type; diff --git a/src/java/org/apache/cassandra/io/CompactionType.java b/src/java/org/apache/cassandra/io/CompactionType.java new file mode 100644 index 0000000000..5aefc86c02 --- /dev/null +++ b/src/java/org/apache/cassandra/io/CompactionType.java @@ -0,0 +1,27 @@ +package org.apache.cassandra.io; + +public enum CompactionType +{ + MAJOR("Major"), + MINOR("Minor"), + VALIDATION("Validation"), + KEY_CACHE_SAVE("Key cache save"), + ROW_CACHE_SAVE("Row cache save"), + CLEANUP("Cleanup"), + SCRUB("Scrub"), + INDEX_BUILD("Secondary index build"), + SSTABLE_BUILD("SSTable build"), + UNKNOWN("Unkown compaction type"); + + private final String type; + + CompactionType(String type) + { + this.type = type; + } + + public String toString() + { + return type; + } +} diff --git a/src/java/org/apache/cassandra/io/sstable/SSTableWriter.java b/src/java/org/apache/cassandra/io/sstable/SSTableWriter.java index 4c2f13c195..78f773d076 100644 --- a/src/java/org/apache/cassandra/io/sstable/SSTableWriter.java +++ b/src/java/org/apache/cassandra/io/sstable/SSTableWriter.java @@ -28,6 +28,7 @@ import java.util.Set; import com.google.common.collect.Sets; +import org.apache.cassandra.io.*; import org.apache.cassandra.utils.ByteBufferUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,13 +39,7 @@ import org.apache.cassandra.db.ColumnFamily; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.DecoratedKey; import org.apache.cassandra.db.Table; -import org.apache.cassandra.db.marshal.AbstractCommutativeType; import org.apache.cassandra.dht.IPartitioner; -import org.apache.cassandra.io.AbstractCompactedRow; -import org.apache.cassandra.io.CompactionController; -import org.apache.cassandra.io.CompactionInfo; -import org.apache.cassandra.io.LazilyCompactedRow; -import org.apache.cassandra.io.PrecompactedRow; import org.apache.cassandra.io.util.BufferedRandomAccessFile; import org.apache.cassandra.io.util.FileMark; import org.apache.cassandra.io.util.FileUtils; @@ -271,7 +266,7 @@ public class SSTableWriter extends SSTable // both file offsets are still valid post-close return new CompactionInfo(desc.ksname, desc.cfname, - "SSTable rebuild", + CompactionType.SSTABLE_BUILD, indexer.dfile.getFilePointer(), indexer.dfile.length()); } diff --git a/src/java/org/apache/cassandra/service/StorageProxy.java b/src/java/org/apache/cassandra/service/StorageProxy.java index 720a947073..41dfba437a 100644 --- a/src/java/org/apache/cassandra/service/StorageProxy.java +++ b/src/java/org/apache/cassandra/service/StorageProxy.java @@ -76,6 +76,7 @@ public class StorageProxy implements StorageProxyMBean private static final WritePerformer standardWritePerformer; private static final WritePerformer counterWritePerformer; + private static final WritePerformer counterWriteOnCoordinatorPerformer; public static final StorageProxy instance = new StorageProxy(); @@ -102,11 +103,25 @@ public class StorageProxy implements StorageProxyMBean } }; + /* + * We execute counter writes in 2 places: either directly in the coordinator node if it is a replica, or + * in CounterMutationVerbHandler on a replica othewise. The write must be executed on the MUTATION stage + * but on the latter case, the verb handler already run on the MUTATION stage, so we must not execute the + * underlying on the stage otherwise we risk a deadlock. Hence two different performer. + */ counterWritePerformer = new WritePerformer() { public void apply(IMutation mutation, Multimap hintedEndpoints, IWriteResponseHandler responseHandler, String localDataCenter, ConsistencyLevel consistency_level) throws IOException { - applyCounterMutation(mutation, hintedEndpoints, responseHandler, localDataCenter, consistency_level); + applyCounterMutation(mutation, hintedEndpoints, responseHandler, localDataCenter, consistency_level, false); + } + }; + + counterWriteOnCoordinatorPerformer = new WritePerformer() + { + public void apply(IMutation mutation, Multimap hintedEndpoints, IWriteResponseHandler responseHandler, String localDataCenter, ConsistencyLevel consistency_level) throws IOException + { + applyCounterMutation(mutation, hintedEndpoints, responseHandler, localDataCenter, consistency_level, true); } }; } @@ -367,7 +382,7 @@ public class StorageProxy implements StorageProxyMBean if (endpoint.equals(FBUtilities.getLocalAddress())) { - applyCounterMutationOnLeader(cm); + applyCounterMutationOnCoordinator(cm); } else { @@ -423,7 +438,14 @@ public class StorageProxy implements StorageProxyMBean write(Collections.singletonList(cm), cm.consistency(), counterWritePerformer, false); } - private static void applyCounterMutation(final IMutation mutation, final Multimap hintedEndpoints, final IWriteResponseHandler responseHandler, final String localDataCenter, final ConsistencyLevel consistency_level) + // Same as applyCounterMutationOnLeader but must with the difference that it use the MUTATION stage to execute the write (while + // applyCounterMutationOnLeader assumes it is on the MUTATION stage already) + public static void applyCounterMutationOnCoordinator(CounterMutation cm) throws UnavailableException, TimeoutException, IOException + { + write(Collections.singletonList(cm), cm.consistency(), counterWriteOnCoordinatorPerformer, false); + } + + private static void applyCounterMutation(final IMutation mutation, final Multimap hintedEndpoints, final IWriteResponseHandler responseHandler, final String localDataCenter, final ConsistencyLevel consistency_level, boolean executeOnMutationStage) { // we apply locally first, then send it to other replica if (logger.isDebugEnabled()) @@ -456,7 +478,10 @@ public class StorageProxy implements StorageProxyMBean } } }; - StageManager.getStage(Stage.MUTATION).execute(runnable); + if (executeOnMutationStage) + StageManager.getStage(Stage.MUTATION).execute(runnable); + else + runnable.run(); } /** diff --git a/src/resources/org/apache/cassandra/cli/CliHelp.yaml b/src/resources/org/apache/cassandra/cli/CliHelp.yaml new file mode 100644 index 0000000000..2f8e38e7af --- /dev/null +++ b/src/resources/org/apache/cassandra/cli/CliHelp.yaml @@ -0,0 +1,1150 @@ +# Help file for online commands in Yaml. + +banner: | + Welcome to the Cassandra CLI. + + Type 'help;' or '?' for help. + Type 'quit;' or 'exit;' to quit. + +help: | + Getting around: + ? Display this help. + help; Display this help. + help ; Display command-specific help. + exit; Exit this utility. + quit; Exit this utility. + + Commands: + assume Apply client side validation. + connect Connect to a Cassandra node. + consistencylevel Sets consisteny level for the client to use. + count Count columns or super columns. + create column family Add a column family to an existing keyspace. + create keyspace Add a keyspace to the cluster. + del Delete a column, super column or row. + decr Decrements a counter column. + describe cluster Describe the cluster configuration. + describe keyspace Describe a keyspace and it's column families. + drop column family Remove a column family and it's data. + drop keyspace Remove a keyspace and it's data. + get Get rows and columns. + incr Increments a counter column. + list List rows in a column family. + set Set columns. + show api version Show the server API version. + show cluster name Show the cluster name. + show keyspaces Show all keyspaces and their column families. + truncate Drop the data in a column family. + update column family Update the settings for a column family. + update keyspace Update the settings for a keyspace. + use Switch to a keyspace. + +commands: + - name: NODE_HELP + help: | + help ; + + Display the general help page with a list of available commands.; + - name: NODE_CONNECT + help: | + connect / ( '')?; + + Connect to the a Cassandra node on the specified port. + + If a username and password are supplied the login will occur when the + 'use' statement is executed. If the server does not support authentication + it will silently ignore credentials. + + For information on configuring authentication and authorisation see the + conf/cassandra.yaml file or the project documentation. + + Required Parameters: + - hostname: Machine name or IP address of the node to connect to. + + - port: rpc_port to connect to the node on, as defined in + conf/Cassandra.yaml for the node. The default port is 9160. + + Optional Parameters: + - password: Password for the supplied username. + + - username: Username to authenticate to the node as. + + Examples: + connect localhost/9160; + connect localhost/9160 user 'badpasswd'; + connect 127.0.0.1/9160 user 'badpasswd'; + - name: NODE_USE_TABLE + help: | + use ; + use ''; + + Use the specified keyspace. + + If a username and password are supplied they will be used to authorize + against the keyspace. Otherwise the credentials supplied to the 'connect' + statement will be used to authorize the user . If the server does not + support authentication it will silently ignore credentials. + + Required Parameters: + - keyspace: Name of the keyspace to use. The keyspace must exist. + + Optional Parameters: + - password: Password for the supplied username. + + - username: Username to login to the node as. + + Examples: + use Keyspace1; + use Keyspace1 user 'badpasswd'; + - name: NODE_DESCRIBE_TABLE + help: | + describe keyspace ()?; + + Describes the settings for the current or named keyspace, and the settings + for all column families in the keyspace. + + Optional Parameters: + - keyspace: Name of the keyspace to describe. + + Examples: + describe keyspace; + describe keyspace system; + - name: NODE_DESCRIBE_CLUSTER + help: | + describe cluster; + + Describes the snitch, partitioner and schema versions for the currently + connected cluster. + + NOTE: The cluster should only report one schema version. Multiple versions + may indicate a failed schema modification, consult the project documentation. + + Examples: + describe cluster; + - name: NODE_EXIT + help: | + exit; + quit; + + Exit this utility. + + Examples: + exit; + quit; + - name: NODE_SHOW_CLUSTER_NAME + help: | + show cluster name; + + Displays the name of the currently connected cluster. + + Examples: + show cluster name; + - name: NODE_SHOW_VERSION + help: | + show api version; + + Displays the API version number. + + This version number is used by high level clients and is not the same as + the server release version. + + Examples: + show api version; + - name: NODE_SHOW_KEYSPACES + help: | + show keyspaces; + + Describes the settings and the column families for all keyspaces on the + currently connected cluster. + + Examples: + show keyspaces; + - name: NODE_ADD_KEYSPACE + help: | + create keyspace ; + create keyspace with =; + create keyspace with = and = ...; + + Create a keyspace with the specified attributes. + + Required Parameters: + - keyspace: Name of the new keyspace, "system" is reserved for + Cassandra internals. Names may only contain letters, numbers and + underscores. + + Keyspace Attributes (all are optional): + - placement_strategy: Class used to determine how replicas + are distributed among nodes. Defaults to NetworkTopologyStrategy with + one datacenter defined with a replication factor of 1 ("[datacenter1:1]"). + + Supported values are: + - org.apache.Cassandra.locator.SimpleStrategy + - org.apache.Cassandra.locator.NetworkTopologyStrategy + - org.apache.Cassandra.locator.OldNetworkTopologyStrategy + + SimpleStrategy merely places the first replica at the node whose + token is closest to the key (as determined by the Partitioner), and + additional replicas on subsequent nodes along the ring in increasing + Token order. + + Supports a single strategy option 'replication_factor' that + specifies the replication factor for the cluster. + + With NetworkTopologyStrategy, for each datacenter, you can specify + how many replicas you want on a per-keyspace basis. Replicas are + placed on different racks within each DC, if possible. + + Supports strategy options which specify the replication factor for + each datacenter. The replication factor for the entire cluster is the + sum of all per datacenter values. Note that the datacenter names + must match those used in conf/cassandra-topology.properties. + + OldNetworkToplogyStrategy [formerly RackAwareStrategy] + places one replica in each of two datacenters, and the third on a + different rack in in the first. Additional datacenters are not + guaranteed to get a replica. Additional replicas after three are + placed in ring order after the third without regard to rack or + datacenter. + + Supports a single strategy option 'replication_factor' that + specifies the replication factor for the cluster. + + - strategy_options: Optional additional options for placement_strategy. + Options have the form [{key:value}], see the information on each + strategy and the examples. + + Examples: + create keyspace Keyspace2 + with placement_strategy = 'org.apache.cassandra.locator.SimpleStrategy' + and strategy_options = [{replication_factor:4}]; + create keyspace Keyspace3 + with placement_strategy = 'org.apache.cassandra.locator.NetworkTopologyStrategy' + and strategy_options=[{DC1:2, DC2:2}]; + create keyspace Keyspace4 + with placement_strategy = 'org.apache.cassandra.locator.OldNetworkTopologyStrategy' + and strategy_options = [{replication_factor:1}]; + - name: NODE_UPDATE_KEYSPACE + help: | + update keyspace ; + update keyspace with =; + update keyspace with = and = ...; + + Update a keyspace with the specified attributes. + + Note: updating some keyspace properties may require additional maintenance + actions. Consult project documentation for more details. + + Required Parameters: + - keyspace: Name of the keyspace to update. + + Keyspace Attributes (all are optional): + - placement_strategy: Class used to determine how replicas + are distributed among nodes. Defaults to NetworkTopologyStrategy with + one datacenter defined with a replication factor of 1 ("[datacenter1:1]"). + + Supported values are: + - org.apache.Cassandra.locator.SimpleStrategy + - org.apache.Cassandra.locator.NetworkTopologyStrategy + - org.apache.Cassandra.locator.OldNetworkTopologyStrategy + + SimpleStrategy merely places the first replica at the node whose + token is closest to the key (as determined by the Partitioner), and + additional replicas on subsequent nodes along the ring in increasing + Token order. + + Supports a single strategy option 'replication_factor' that + specifies the replication factor for the cluster. + + With NetworkTopologyStrategy, for each datacenter, you can specify + how many replicas you want on a per-keyspace basis. Replicas are + placed on different racks within each DC, if possible. + + Supports strategy options which specify the replication factor for + each datacenter. The replication factor for the entire cluster is the + sum of all per datacenter values. Note that the datacenter names + must match those used in conf/cassandra-topology.properties. + + OldNetworkToplogyStrategy [formerly RackAwareStrategy] + places one replica in each of two datacenters, and the third on a + different rack in in the first. Additional datacenters are not + guaranteed to get a replica. Additional replicas after three are + placed in ring order after the third without regard to rack or + datacenter. + + Supports a single strategy option 'replication_factor' that + specifies the replication factor for the cluster. + + - strategy_options: Optional additional options for placement_strategy. + Options have the form [{key:value}], see the information on each + strategy and the examples. + + Examples: + update keyspace Keyspace2 + with placement_strategy = 'org.apache.cassandra.locator.SimpleStrategy' + and strategy_options = [{replication_factor:4}]; + update keyspace Keyspace3 + with placement_strategy = 'org.apache.cassandra.locator.NetworkTopologyStrategy' + and strategy_options=[{DC1:2, DC2:2}]; + update keyspace Keyspace4 + with placement_strategy = 'org.apache.cassandra.locator.OldNetworkTopologyStrategy' + and strategy_options = [{replication_factor:1}]; + - name: NODE_ADD_COLUMN_FAMILY + help: | + create column family ; + create column family with =; + create column family with = and =...; + + Create a column family in the current keyspace with the specified + attributes. + + Required Parameters: + - name: Name of the new column family. Names may only contain letters, + numbers and underscores. + + column family Attributes (all are optional): + - column_metadata: Defines the validation and indexes for known columns in + this column family. + + Columns not listed in the column_metadata section will use the + default_validator to validate their values. + + Column Required parameters: + - name: Binds a validator (and optionally an indexer) to columns + with this name in any row of the enclosing column family. + + - validator: Validator to use for values for this column. + + Supported values are: + - AsciiType + - BytesType + - CounterColumnType (distributed counter column) + - IntegerType (a generic variable-length integer type) + - LexicalUUIDType + - LongType + - UTF8Type + + It is also valid to specify the fully-qualified class name to a class + that extends org.apache.Cassandra.db.marshal.AbstractType. + + Column Optional parameters: + - index_name: Name for the index. Both an index name and + type must be specified. + + - index_type: The type of index to be created. + + Suported values are: + - 0: for a KEYS based index + + - column_type: Type of columns this column family holds, valid values are + Standard and Super. Default is Standard. + + - comment: Human readable column family description. + + - comparator: Validator to use to validate and compare column names in + this column family. For Standard column families it applies to columns, for + Super column families applied to super columns. Also see the subcomparator + attribute. Default is BytesType, which is a straight forward lexical + comparison of the bytes in each column. + + Supported values are: + - AsciiType + - BytesType + - CounterColumnType (distributed counter column) + - IntegerType (a generic variable-length integer type) + - LexicalUUIDType + - LongType + - UTF8Type + + It is also valid to specify the fully-qualified class name to a class that + extends org.apache.Cassandra.db.marshal.AbstractType. + + - default_validation_class: Validator to use for values in columns which are + not listed in the column_metadata. Default is BytesType which applies + no validation. + + Supported values are: + - AsciiType + - BytesType + - CounterColumnType (distributed counter column) + - IntegerType (a generic variable-length integer type) + - LexicalUUIDType + - LongType + - UTF8Type + + It is also valid to specify the fully-qualified class name to a class that + extends org.apache.Cassandra.db.marshal.AbstractType. + + - key_valiation_class: Validator to use for keys. + Default is BytesType which applies no validation. + + Supported values are: + - AsciiType + - BytesType + - IntegerType (a generic variable-length integer type) + - LexicalUUIDType + - LongType + - UTF8Type + + It is also valid to specify the fully-qualified class name to a class that + extends org.apache.Cassandra.db.marshal.AbstractType. + + - gc_grace: Time to wait in seconds before garbage collecting tombstone + deletion markers. Default value is 864000 or 10 days. + + Set this to a large enough value that you are confident that the deletion + markers will be propagated to all replicas by the time this many seconds + has elapsed, even in the face of hardware failures. + + See http://wiki.apache.org/Cassandra/DistributedDeletes + + - keys_cached: Maximum number of keys to cache in memory. Valid values are + either a double between 0 and 1 (inclusive on both ends) denoting what + fraction should be cached. Or an absolute number of rows to cache. + Default value is 200000. + + Each key cache hit saves 1 seek and each row cache hit saves 2 seeks at the + minimum, sometimes more. The key cache is fairly tiny for the amount of + time it saves, so it's worthwhile to use it at large numbers all the way + up to 1.0 (all keys cached). The row cache saves even more time, but must + store the whole values of its rows, so it is extremely space-intensive. + It's best to only use the row cache if you have hot rows or static rows. + + - keys_cached_save_period: Duration in seconds after which Cassandra should + safe the keys cache. Caches are saved to saved_caches_directory as + specified in conf/Cassandra.yaml. Default is 14400 or 4 hours. + + Saved caches greatly improve cold-start speeds, and is relatively cheap in + terms of I/O for the key cache. Row cache saving is much more expensive and + has limited use. + + - memtable_flush_after: Maximum number of minutes to leave a dirty + memtable unflushed. This value needs to be large enough that it won't cause + a flush storm of all your memtables flushing at once because none have + hit the size or count thresholds yet. For production a larger value such + as 1440 is recommended. Default is 60. + + NOTE: While any affected column families have unflushed data from a commit + log segment, that segment cannot be deleted. + + - memtable_operations: Number of operations in millions before the memtable + is flushed. Default is memtable_throughput / 64 * 0.3 + + - memtable_throughput: Maximum size in MB to let a memtable get to before + it is flushed. Default is to use 1/16 the JVM heap size. + + - read_repair_chance: Probability (0.0-1.0) with which to perform read + repairs for any read operation. Default is 1.0 to enable read repair. + + Note that disabling read repair entirely means that the dynamic snitch + will not have any latency information from all the replicas to recognize + when one is performing worse than usual. + + - rows_cached: Maximum number of rows whose entire contents we + cache in memory. Valid values are either a double between 0 and 1 ( + inclusive on both ends) denoting what fraction should be cached. Or an + absolute number of rows to cache. Default value is 0, to disable row + caching. + + Each key cache hit saves 1 seek and each row cache hit saves 2 seeks at the + minimum, sometimes more. The key cache is fairly tiny for the amount of + time it saves, so it's worthwhile to use it at large numbers all the way + up to 1.0 (all keys cached). The row cache saves even more time, but must + store the whole values of its rows, so it is extremely space-intensive. + It's best to only use the row cache if you have hot rows or static rows. + + - row_cache_save_period: Duration in seconds after which Cassandra should + safe the row cache. Caches are saved to saved_caches_directory as specified + in conf/Cassandra.yaml. Default is 0 to disable saving the row cache. + + Saved caches greatly improve cold-start speeds, and is relatively cheap in + terms of I/O for the key cache. Row cache saving is much more expensive and + has limited use. + + - subcomparator: Validator to use to validate and compare sub column names + in this column family. Only applied to Super column families. Default is + BytesType, which is a straight forward lexical comparison of the bytes in + each column. + + Supported values are: + - AsciiType + - BytesType + - CounterColumnType (distributed counter column) + - IntegerType (a generic variable-length integer type) + - LexicalUUIDType + - LongType + - UTF8Type + + It is also valid to specify the fully-qualified class name to a class that + extends org.apache.Cassandra.db.marshal.AbstractType. + + - max_compaction_threshold: The maximum number of SSTables allowed before a + minor compaction is forced. Default is 32, setting to 0 disables minor + compactions. + + Decreasing this will cause minor compactions to start more frequently and + be less intensive. The min_compaction_threshold and max_compaction_threshold + boundaries are the number of tables Cassandra attempts to merge together at + once. + + - min_compaction_threshold: The minimum number of SSTables needed + to start a minor compaction. Default is 4, setting to 0 disables minor + compactions. + + Increasing this will cause minor compactions to start less frequently and + be more intensive. The min_compaction_threshold and max_compaction_threshold + boundaries are the number of tables Cassandra attempts to merge together at + once. + + - replicate_on_write: Replicate every counter update from the leader to the + follower replicas. Accepts the values true and false. + + - row_cache_provider: The provider for the row cache to use for this + column family. Defaults to ConcurrentLinkedHashCacheProvider. . + + Supported values are: + - ConcurrentLinkedHashCacheProvider + - SerializingCacheProvider + + It is also valid to specify the fully-qualified class name to a class + that implements org.apache.cassandra.cache.IRowCacheProvider. + + ConcurrentLinkedHashCacheProvider provides the same features as the versions + prior to Cassandra v0.8. Row data is cached using the Java JVM heap. + + SerializingCacheProvider serialises the contents of the row and + stores the data off the JVM Heap. This may reduce the GC pressure. + NOTE: Thsi provider requires JNA.jar to be in the class path to + enable native methods. + + Examples: + create column family Super4 + with column_type = 'Super' + and comparator = 'AsciiType' + and rows_cached = 10000; + create column family Standard3 + with comparator = 'LongType' + and rows_cached = 10000; + create column family Standard4 + with comparator = AsciiType + and column_metadata = + [{ + column_name : Test, + validation_class : IntegerType, + index_type : 0, + index_name : IdxName}, + { + column_name : 'other name', + validation_class : LongType + }]; + - name: NODE_UPDATE_COLUMN_FAMILY + help: | + update column family ; + update column family with =; + update column family with = and =...; + + Updates the settings for a column family in the current keyspace. + + Required Parameters: + - name: Name of the column family to update. + + column family Attributes (all are optional): + - column_metadata: Defines the validation and indexes for known columns in + this column family. + + Columns not listed in the column_metadata section will use the + default_validator to validate their values. + + Column Required parameters: + - name: Binds a validator (and optionally an indexer) to columns + with this name in any row of the enclosing column family. + + - validator: Validator to use for values for this column. + + Supported values are: + - AsciiType + - BytesType + - CounterColumnType (distributed counter column) + - IntegerType (a generic variable-length integer type) + - LexicalUUIDType + - LongType + - UTF8Type + + It is also valid to specify the fully-qualified class name to a class + that extends org.apache.Cassandra.db.marshal.AbstractType. + + Column Optional parameters: + - index_name: Name for the index. Both an index name and + type must be specified. + + NOTE: After the update has completed the column family will only + contain the secondary indexes listed in the update statement. Existing + indexes will be dropped if they are not present in the update. + + - index_type: The type of index to be created. + + Suported values are: + - 0: for a KEYS based index + + - column_type: Type of columns this column family holds, valid values are + Standard and Super. Default is Standard. + + - comment: Column family description. + + - comparator: Validator to use to validate and compare column names in + this column family. For Standard column families it applies to columns, for + Super column families applied to super columns. Also see the subcomparator + attribute. Default is BytesType, which is a straight forward lexical + comparison of the bytes in each column. + + Supported values are: + - AsciiType + - BytesType + - CounterColumnType (distributed counter column) + - IntegerType (a generic variable-length integer type) + - LexicalUUIDType + - LongType + - UTF8Type + + It is also valid to specify the fully-qualified class name to a class that + extends org.apache.Cassandra.db.marshal.AbstractType. + + - default_validation_class: Validator to use for values in columns which are + not listed in the column_metadata. Default is BytesType which applies + no validation. + + Supported values are: + - AsciiType + - BytesType + - CounterColumnType (distributed counter column) + - IntegerType (a generic variable-length integer type) + - LexicalUUIDType + - LongType + - UTF8Type + + It is also valid to specify the fully-qualified class name to a class that + extends org.apache.Cassandra.db.marshal.AbstractType. + + - key_valiation_class: Validator to use for keys. + Default is BytesType which applies no validation. + + Supported values are: + - AsciiType + - BytesType + - IntegerType (a generic variable-length integer type) + - LexicalUUIDType + - LongType + - UTF8Type + + It is also valid to specify the fully-qualified class name to a class that + extends org.apache.Cassandra.db.marshal.AbstractType. + + - gc_grace: Time to wait in seconds before garbage collecting tombstone + deletion markers. Default value is 864000 or 10 days. + + Set this to a large enough value that you are confident that the deletion + markers will be propagated to all replicas by the time this many seconds + has elapsed, even in the face of hardware failures. + + See http://wiki.apache.org/Cassandra/DistributedDeletes + + - keys_cached: Maximum number of keys to cache in memory. Valid values are + either a double between 0 and 1 (inclusive on both ends) denoting what + fraction should be cached. Or an absolute number of rows to cache. + Default value is 200000. + + Each key cache hit saves 1 seek and each row cache hit saves 2 seeks at the + minimum, sometimes more. The key cache is fairly tiny for the amount of + time it saves, so it's worthwhile to use it at large numbers all the way + up to 1.0 (all keys cached). The row cache saves even more time, but must + store the whole values of its rows, so it is extremely space-intensive. + It's best to only use the row cache if you have hot rows or static rows. + + - keys_cached_save_period: Duration in seconds after which Cassandra should + safe the keys cache. Caches are saved to saved_caches_directory as + specified in conf/Cassandra.yaml. Default is 14400 or 4 hours. + + Saved caches greatly improve cold-start speeds, and is relatively cheap in + terms of I/O for the key cache. Row cache saving is much more expensive and + has limited use. + + - memtable_flush_after: Maximum number of minutes to leave a dirty + memtable unflushed. This value needs to be large enough that it won't cause + a flush storm of all your memtables flushing at once because none have + hit the size or count thresholds yet. For production a larger value such + as 1440 is recommended. Default is 60. + + NOTE: While any affected column families have unflushed data from a commit + log segment, that segment cannot be deleted. + + - memtable_operations: Number of operations in millions before the memtable + is flushed. Default is memtable_throughput / 64 * 0.3 + + - memtable_throughput: Maximum size in MB to let a memtable get to before + it is flushed. Default is to use 1/16 the JVM heap size. + + - read_repair_chance: Probability (0.0-1.0) with which to perform read + repairs for any read operation. Default is 1.0 to enable read repair. + + - rows_cached: Maximum number of rows whose entire contents we + cache in memory. Valid values are either a double between 0 and 1 ( + inclusive on both ends) denoting what fraction should be cached. Or an + absolute number of rows to cache. Default value is 0, to disable row + caching. + + Each key cache hit saves 1 seek and each row cache hit saves 2 seeks at the + minimum, sometimes more. The key cache is fairly tiny for the amount of + time it saves, so it's worthwhile to use it at large numbers all the way + up to 1.0 (all keys cached). The row cache saves even more time, but must + store the whole values of its rows, so it is extremely space-intensive. + It's best to only use the row cache if you have hot rows or static rows. + + - row_cache_save_period: Duration in seconds after which Cassandra should + safe the row cache. Caches are saved to saved_caches_directory as specified + in conf/Cassandra.yaml. Default is 0 to disable saving the row cache. + + Saved caches greatly improve cold-start speeds, and is relatively cheap in + terms of I/O for the key cache. Row cache saving is much more expensive and + has limited use. + + - subcomparator: Validator to use to validate and compare sub column names + in this column family. Only applied to Super column families. Default is + BytesType, which is a straight forward lexical comparison of the bytes in + each column. + + Supported values are: + - AsciiType + - BytesType + - CounterColumnType (distributed counter column) + - IntegerType (a generic variable-length integer type) + - LexicalUUIDType + - LongType + - UTF8Type + + It is also valid to specify the fully-qualified class name to a class that + extends org.apache.Cassandra.db.marshal.AbstractType. + + - max_compaction_threshold: The maximum number of SSTables allowed before a + minor compaction is forced. Default is 32, setting to 0 disables minor + compactions. + + Decreasing this will cause minor compactions to start more frequently and + be less intensive. The min_compaction_threshold and max_compaction_threshold + boundaries are the number of tables Cassandra attempts to merge together at + once. + + - min_compaction_threshold: The minimum number of SSTables needed + to start a minor compaction. Default is 4, setting to 0 disables minor + compactions. + + Increasing this will cause minor compactions to start less frequently and + be more intensive. The min_compaction_threshold and max_compaction_threshold + boundaries are the number of tables Cassandra attempts to merge together at + once. + + - replicate_on_write: Replicate every counter update from the leader to the + follower replicas. Accepts the values true and false. + + - row_cache_provider: The provider for the row cache to use for this + column family. Defaults to ConcurrentLinkedHashCacheProvider. . + + Supported values are: + - ConcurrentLinkedHashCacheProvider + - SerializingCacheProvider + + It is also valid to specify the fully-qualified class name to a class + that implements org.apache.cassandra.cache.IRowCacheProvider. + + ConcurrentLinkedHashCacheProvider provides the same features as the versions + prior to Cassandra v0.8. Row data is cached using the Java JVM heap. + + SerializingCacheProvider serialises the contents of the row and + stores the data off the JVM Heap. This may reduce the GC pressure. + NOTE: Thsi provider requires JNA.jar to be in the class path to + enable native methods. + + Examples: + update column family Super4 + with column_type = 'Super' + and comparator = 'AsciiType' + and rows_cached = 10000; + update column family Standard3 + with comparator = 'LongType' + and rows_cached = 10000; + update column family Standard4 + with comparator = AsciiType + and column_metadata = + [{ + column_name : Test, + validation_class : IntegerType, + index_type : 0, + index_name : IdxName}, + { + column_name : 'other name', + validation_class : LongType + }]; + - name: NODE_DEL_KEYSPACE + help: | + drop keyspace ; + + Drops the specified keyspace. + + A snapshot of the data is created in a sub directory of the Keyspace data directory. The files + must be manually deleted using either "nodetool clearsnapshot" or the command line. + + Required Parameters: + - keyspace: Name of the keyspace to delete. + + Example: + drop keyspace Keyspace1; + - name: NODE_DEL_COLUMN_FAMILY + help: | + drop column family ; + + Drops the specified column family. + + A snapshot of the data is created in a sub directory of the Keyspace data directory. The files + must be manually deleted using either "nodetool clearsnapshot" or the command line. + + Required Parameters: + - cf: Name of the column family to delete. + + Example: + drop column family Standard2; + - name: NODE_THRIFT_GET + help: | + get ['']; + get [''][''] (as )*; + get [''][''][''] (as )*; + get ['']['']; + get [''][]; + get [function()][()][()]; + get where [ + and and ...] [limit ]; + get where () [ + and and ...] [limit ]; + + Gets columns or super columns for the specified column family and key. Or + returns all columns from rows which meet the specified criteria when using + the 'where' form. + + Note: The implementation of secondary indexes in Cassandra 0.7 has some + restrictions, see + http://www.datastax.com/dev/blog/whats-new-Cassandra-07-secondary-indexes + + Required Parameters: + - cf: Name of the column family to read from. + + Optional Parameters: + - col: Name of the column to read. Or in the 'where' form name of the column + to test the value of. + + - function: Name of a function to call to parse the supplied argument to the + specified type. Some functions will generate values without needing an + argument. + + Valid options are: + - ascii + - bytes: if used without arguments generates a zero length byte array + - integer + - lexicaluuid: if used without arguments generates a new random uuid + - long + - timeuuid: if used without arguments generates a new time uuid + - utf8 + + - key: Key for the row to read columns from. This parameter is + required in all cases except when the 'where' clause is used. + + - limit: Number of rows to return. Default is 100. + + - operator: Operator to test the column value with. Supported operators are + =, >, >=, <, <= . + + In Cassandra 0.7 at least one = operator must be present. + + - super: Name of the super column to read from. If super is supplied without + col then all columns from the super column are returned. + + - type: Data type to interpret the the columns value as for display. + + Valid options are: + - AsciiType + - BytesType + - CounterColumnType (distributed counter column) + - IntegerType (a generic variable-length integer type) + - LexicalUUIDType + - LongType + - UTF8Type + + - value: The value to test the column for, if a function is provided the + value is parsed by the function. Otherwise the meta data for the target + column is used to determine the correct type. + + Examples: + get Standard1[ascii('testkey')]; + #tell cli to convert keys from ascii to bytes + assume Standard1 keys as ascii; + get Standard1[testkey][test_column] as IntegerType; + get Standard1[testkey][utf8(hello)]; + get Indexed1 where birthdate=19750403; + - name: NODE_THRIFT_SET + help: | + set [''][''] = ; + set [''][''][''] = ; + set [''][''] = (); + set [''][''][''] = (); + set [][()] = || ; + set [()][() || ] = + || with ttl = ; + + Sets the column value for the specified column family and key. + + Required Parameters: + - cf: Name of the column family to set columns in. + + - col: Name of the column to set. + + - key: Key for the row to set columns in. + + Optional Parameters: + - function: Name of a function to call to parse the supplied argument to the + specified type. Some functions will generate values without needing an + argument. + + Valid options are: + - ascii + - bytes: if used without arguments generates a zero length byte array + - integer + - lexicaluuid: if used without arguments generates a new random uuid + - long + - timeuuid: if used without arguments generates a new time uuid + - utf8 + + - secs: Time To Live for the column in seconds. Defaults to no ttl. + + - super: Name of the super column to contain the column. + + - value: The value to set the column to. + + Examples: + set Super1[ascii('testkey')][ascii('my super')][ascii('test col')]='this is a test'; + set Standard1['testkey']['test col']='this is also a test'; + set Standard1[testkey][testcol] = utf8('this is utf8 string.'); + set Standard1[testkey][timeuuid()] = utf8('hello world'); + set Standard1[testkey][timeuuid()] = utf8('hello world') with ttl = 30; + - name: NODE_THRIFT_DEL + help: | + del ['']; + del ['']['']; + del ['']['']; + del ['']['']['']; + del [()][()][()]; + + Deletes a row, a column, or a subcolumn. + + Required Parameters: + - cf: Name of the column family to delete from. + + - key: Key for the row delete from. + + Optional Parameters: + - col: Name of the column to delete. + + - function: Name of a function to call to parse the supplied argument to the + specified type. Some functions will generate values without needing an + argument. + + Supported values are: + - ascii + - bytes: if used without arguments generates a zero length byte array + - integer + - lexicaluuid: if used without arguments generates a new random uuid + - long + - timeuuid: if used without arguments generates a new time uuid + - utf8 + + - super: Name of the super column to delete from. If col is not specified + the super column and all sub columns will be deleted. + + Examples: + del Super1[ascii('testkey')][ascii('my_super')][ascii('test_col')]; + del Standard1['testkey'][ascii('test col')]; + del Standard1['testkey']; + del Standard1[utf8('testkey')]; + - name: NODE_THRIFT_COUNT + help: | + count ['']; + count ['']['']; + + Count the number of columns in the row with the specified key, or + subcolumns in the specified super column. + + Required Parameters: + - cf: Name of the column family to read from.. + + - key: Key for the row to count. + + Optional Parameters: + - super: Name of the super column to count subcolumns in. + + Examples: + count Super1['testkey']['my super']; + count Standard1['testkey']; + - name: NODE_LIST + help: | + list ; + list [:]; + list [:]; + list [:] limit ; + + List a range of rows, and all of their columns, in the specified column + family. + + The order of rows returned is dependant on the Partitioner in use. + + Required Parameters: + - cf: Name of the column family to list rows from. + + Optional Parameters: + - endKey: Key to end the range at. The end key will be included + in the result. Defaults to an empty byte array. + + - limit: Number of rows to return. Default is 100. + + - startKey: Key start the range from. The start key will be + included in the result. Defaults to an empty byte array. + + Examples: + list Standard1; + list Super1[j:]; + list Standard1[j:k] limit 40; + - name: NODE_TRUNCATE + help: | + truncate ; + + Truncate specified column family. + + Note: All nodes in the cluster must be up to truncate command to execute. + + A snapshot of the data is created, which is deleted asyncronously during a + 'graveyard' compaction. + + Required Parameters: + - cf: Name of the column family to truncate. + + Examples: + truncate Standard1; + - name: NODE_ASSUME + help: | + assume comparator as ; + assume sub_comparator as ; + assume validator as ; + assume keys as ; + + Assume one of the attributes (comparator, sub_comparator, validator or keys) + of the given column family match specified type. The specified type will + be used when displaying data returned from the column family. + + This statement does not change the column family definition stored in + Cassandra. It only affects the cli and how it will transform values + to be sent to and interprets results from Cassandra. + + If results from Cassandra do not validate according to the assumptions an + error is displayed in the cli. + + Required Parameters: + - cf: Name of the column family to make the assumption about. + + - type: Validator type to use when processing values. + + Supported values are: + - AsciiType + - BytesType + - CounterColumnType (distributed counter column) + - IntegerType (a generic variable-length integer type) + - LexicalUUIDType + - LongType + - UTF8Type + + It is also valid to specify the fully-qualified class name to a class that + extends org.apache.Cassandra.db.marshal.AbstractType. + + Examples: + assume Standard1 comparator as lexicaluuid; + assume Standard1 keys as ascii; + - name: NODE_THRIFT_INCR + help: | + incr [''][''] [by ]; + incr [''][''][''] [by ]; + + Increment the specified counter column by the supplied value. + + Note: Counter columns must be defined using a 'create column family' or + 'update column family' statement in the column_metadata as using the + ColumnCounterType validator. + + Required Parameters: + - cf: Name of the column family to increment the column in. + + - col: Name of the counter column to increment. + + - key: Key for the row to increment the counter in. + + Optional Parameters: + - super: Name of the super column that contains the counter column. + + - value: Signed integer value to increment the column by. If not supplied + 1 is used. + + Examples: + incr Counter1[ascii('testkey')][ascii('test col')]; + incr SuperCounter1[ascii('testkey')][ascii('my super')][ascii('test col')] by 42; + incr Counter1[ascii('testkey')][ascii('test col')] by -4; + - name: NODE_THRIFT_DECR + help: | + decr [''][''] [by ]; + decr [''][''][''] [by ]; + + Decrement the specified column by the supplied value. + + Note: Counter columns must be defined using a 'create column family' or + 'update column family' statement in the column_metadata as using the + ColumnCounterType validator. + + Required Parameters: + - cf: Name of the column family to decrement the column in. + + - col: Name of the counter column to increment. + + - key: Key for the row to decrement the counter in. + + Optional Parameters: + - super: Name of the super column that contains the counter column. + + - value: Signed integer value to decrement the column by. If not supplied + 1 is used. + + Examples: + decr Counter1[ascii('testkey')][ascii('test col')]; + decr SuperCounter1[ascii('testkey')][ascii('my super')][ascii('test col')] by 42; + decr Counter1[ascii('testkey')][ascii('test col')] by 10; + - name: NODE_CONSISTENCY_LEVEL + help: | + consistencylevel as + + Sets the consistency level for the client to use. Defaults to One. + + Required Parameters: + - level: Consistency level the client should use. Value is case + insensitive. + + Supported values are: + - ONE + - TWO + - THREE + - QUORUM + - ALL + - LOCAL_QUORUM + - EACH_QUORUM + - ANY + + Note: Consistency level ANY can only be used for write operations. \ No newline at end of file diff --git a/test/long/org/apache/cassandra/db/LongCompactionSpeedTest.java b/test/long/org/apache/cassandra/db/LongCompactionSpeedTest.java index db9e4958cf..eda6d74b5f 100644 --- a/test/long/org/apache/cassandra/db/LongCompactionSpeedTest.java +++ b/test/long/org/apache/cassandra/db/LongCompactionSpeedTest.java @@ -20,28 +20,29 @@ package org.apache.cassandra.db; import java.io.IOException; import java.net.InetAddress; +import java.nio.ByteBuffer; import java.util.*; -import org.apache.cassandra.Util; - import org.junit.Test; -import org.apache.cassandra.io.util.FileUtils; +import org.apache.cassandra.CleanupHelper; +import org.apache.cassandra.Util; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.io.util.FileUtils; +import org.apache.cassandra.io.sstable.Component; import org.apache.cassandra.io.sstable.SSTableReader; import org.apache.cassandra.io.sstable.SSTableUtils; import org.apache.cassandra.io.sstable.SSTableWriter; -import org.apache.cassandra.CleanupHelper; -import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.streaming.OperationType; import org.apache.cassandra.utils.ByteBufferUtil; -import java.nio.ByteBuffer; -import org.apache.cassandra.io.sstable.Component; +import org.apache.cassandra.utils.FBUtilities; +import org.apache.cassandra.utils.NodeId; +import static org.apache.cassandra.db.context.CounterContext.ContextState; + import static junit.framework.Assert.assertEquals; public class LongCompactionSpeedTest extends CleanupHelper { public static final String TABLE1 = "Keyspace1"; - public static final InetAddress LOCAL = FBUtilities.getLocalAddress(); /** * Test compaction with a very wide row. @@ -188,12 +189,12 @@ public class LongCompactionSpeedTest extends CleanupHelper protected CounterColumn createCounterColumn(String name) { - byte[] context = Util.concatByteArrays( - FBUtilities.getLocalAddress().getAddress(), FBUtilities.toByteArray(9L), FBUtilities.toByteArray(3L), - FBUtilities.toByteArray(2), FBUtilities.toByteArray(4L), FBUtilities.toByteArray(2L), - FBUtilities.toByteArray(4), FBUtilities.toByteArray(3L), FBUtilities.toByteArray(3L), - FBUtilities.toByteArray(8), FBUtilities.toByteArray(2L), FBUtilities.toByteArray(4L) - ); - return new CounterColumn(ByteBufferUtil.bytes(name), ByteBuffer.wrap(context), 0L); + ContextState context = ContextState.allocate(4, 1); + context.writeElement(NodeId.fromInt(1), 4L, 2L, true); + context.writeElement(NodeId.fromInt(2), 4L, 2L); + context.writeElement(NodeId.fromInt(4), 3L, 3L); + context.writeElement(NodeId.fromInt(8), 2L, 4L); + + return new CounterColumn(ByteBufferUtil.bytes(name), context.context, 0L); } } diff --git a/test/system/test_cql.py b/test/system/test_cql.py index e63dd3e1da..dfcb42d1f3 100644 --- a/test/system/test_cql.py +++ b/test/system/test_cql.py @@ -23,18 +23,18 @@ import sys, uuid, time sys.path.append(join(abspath(dirname(__file__)), '../../drivers/py')) -from cql import Connection -from cql.errors import CQLException +import cql +from cql.connection import Connection from __init__ import ThriftTester from __init__ import thrift_client # TODO: temporary -def assert_raises(exception, method, *args): +def assert_raises(exception, method, *args, **kwargs): try: - method(*args) + method(*args, **kwargs) except exception: return raise AssertionError("failed to see expected exception") - + def uuid1bytes_to_millis(uuidbytes): return (uuid.UUID(bytes=uuidbytes).get_time() / 10000) - 12219292800000L @@ -72,12 +72,12 @@ def load_sample(dbconn): WITH comparator = ascii AND default_validation = ascii; """) dbconn.execute("CREATE INDEX ON IndexedA (birthdate)") - - query = "UPDATE StandardString1 SET ? = ?, ? = ? WHERE KEY = ?" - dbconn.execute(query, "ca1", "va1", "col", "val", "ka") - dbconn.execute(query, "cb1", "vb1", "col", "val", "kb") - dbconn.execute(query, "cc1", "vc1", "col", "val", "kc") - dbconn.execute(query, "cd1", "vd1", "col", "val", "kd") + + query = "UPDATE StandardString1 SET :c1 = :v1, :c2 = :v2 WHERE KEY = :key" + dbconn.execute(query, dict(c1="ca1", v1="va1", c2="col", v2="val", key="ka")) + dbconn.execute(query, dict(c1="cb1", v1="vb1", c2="col", v2="val", key="kb")) + dbconn.execute(query, dict(c1="cc1", v1="vc1", c2="col", v2="val", key="kc")) + dbconn.execute(query, dict(c1="cd1", v1="vd1", c2="col", v2="val", key="kd")) dbconn.execute(""" BEGIN BATCH USING CONSISTENCY ONE @@ -90,7 +90,7 @@ def load_sample(dbconn): UPDATE StandardLongA SET 5='5', 6='6', 7='8', 9='9' WHERE KEY='ag' APPLY BATCH """) - + dbconn.execute(""" BEGIN BATCH USING CONSISTENCY ONE UPDATE StandardIntegerA SET 10='a', 20='b', 30='c', 40='d' WHERE KEY='k1'; @@ -114,255 +114,294 @@ def load_sample(dbconn): """) def init(keyspace="Keyspace1"): - dbconn = Connection('localhost', 9170, keyspace) - load_sample(dbconn) - return dbconn + dbconn = cql.connect('localhost', 9170, keyspace) + cursor = dbconn.cursor() + load_sample(cursor) + return cursor class TestCql(ThriftTester): def test_select_simple(self): "retrieve a column" - conn = init() - r = conn.execute("SELECT 'ca1' FROM StandardString1 WHERE KEY='ka'") - assert r[0].key == 'ka' - assert r[0].columns[0].name == 'ca1' - assert r[0].columns[0].value == 'va1' + cursor = init() + cursor.execute("SELECT 'ca1' FROM StandardString1 WHERE KEY='ka'") + r = cursor.fetchone() + d = cursor.description + + assert d[0][0] == cql.ROW_KEY + assert r[0] == 'ka' + + assert d[1][0] == 'ca1' + assert r[1] == 'va1' def test_select_columns(self): "retrieve multiple columns" - conn = init() - r = conn.execute(""" + cursor = init() + cursor.execute(""" SELECT 'cd1', 'col' FROM StandardString1 WHERE KEY = 'kd' """) - assert "cd1" in [i.name for i in r[0].columns] - assert "col" in [i.name for i in r[0].columns] + + d = cursor.description + assert "cd1" in [col_dscptn[0] for col_dscptn in d] + assert "col" in [col_dscptn[0] for col_dscptn in d] def test_select_row_range(self): "retrieve a range of rows with columns" - conn = init() - r = conn.execute(""" + cursor = init() + cursor.execute(""" SELECT 4 FROM StandardLongA WHERE KEY > 'ad' AND KEY < 'ag'; """) - assert len(r) == 4 - assert r[0].key == "ad" - assert r[1].key == "ae" - assert r[2].key == "af" - assert len(r[0].columns) == 1 - assert len(r[1].columns) == 1 - assert len(r[2].columns) == 1 + keys = ['ad', 'ae', 'af'] + assert cursor.rowcount == 4 + for i in range(3): + r = cursor.fetchone() + assert len(r) == 2 + assert r[0] == keys[i] def test_select_row_range_with_limit(self): "retrieve a limited range of rows with columns" - conn = init() - r = conn.execute(""" + cursor = init() + cursor.execute(""" SELECT 1,5,9 FROM StandardLongA WHERE KEY > 'aa' AND KEY < 'ag' LIMIT 3 """) - assert len(r) == 3 - - r = conn.execute(""" + assert cursor.rowcount == 3 + + cursor.execute(""" SELECT 20,40 FROM StandardIntegerA WHERE KEY > 'k1' AND KEY < 'k7' LIMIT 5 """) - assert len(r) == 5 - r[0].key == "k1" - r[4].key == "k5" + assert cursor.rowcount == 5 + for i in range(5): + r = cursor.fetchone() + assert r[0] == "k%d" % (i+1) def test_select_columns_slice(self): "range of columns (slice) by row" - conn = init() - r = conn.execute("SELECT 1..3 FROM StandardLongA WHERE KEY = 'aa';") - assert len(r) == 1 - assert r[0].columns[0].value == "1" - assert r[0].columns[1].value == "2" - assert r[0].columns[2].value == "3" - - r = conn.execute("SELECT 10..30 FROM StandardIntegerA WHERE KEY='k1'") - assert len(r) == 1 - assert r[0].columns[0].value == "a" - assert r[0].columns[1].value == "b" - assert r[0].columns[2].value == "c" - + cursor = init() + + cursor.execute("SELECT 1..3 FROM StandardLongA WHERE KEY = 'aa';") + assert cursor.rowcount == 1 + r = cursor.fetchone() + assert r[0] == "aa" + assert r[1] == "1" + assert r[2] == "2" + assert r[3] == "3" + + cursor.execute("SELECT 10..30 FROM StandardIntegerA WHERE KEY='k1'") + assert cursor.rowcount == 1 + r = cursor.fetchone() + assert r[0] == "k1" + assert r[1] == "a" + assert r[2] == "b" + assert r[3] == "c" + def test_select_columns_slice_all(self): "slice all columns in a row" - conn = init() - r = conn.execute("SELECT * FROM StandardString1 WHERE KEY = 'ka';") - assert len(r[0].columns) == 2 - r = conn.execute("SELECT ''..'' FROM StandardString1 WHERE KEY = 'ka';") - assert len(r[0].columns) == 2 + cursor = init() + cursor.execute("SELECT * FROM StandardString1 WHERE KEY = 'ka';") + r = cursor.fetchone() + assert len(r) == 3 + cursor.execute("SELECT ''..'' FROM StandardString1 WHERE KEY = 'ka';") + r = cursor.fetchone() + assert len(r) == 3 def test_select_columns_slice_with_limit(self): "range of columns (slice) by row with limit" - conn = init() - r = conn.execute(""" + cursor = init() + cursor.execute(""" SELECT FIRST 1 1..3 FROM StandardLongA WHERE KEY = 'aa'; """) - assert len(r) == 1 - assert len(r[0].columns) == 1 - assert r[0].columns[0].value == "1" + assert cursor.rowcount == 1 + r = cursor.fetchone() + assert len(r) == 2 + assert r[0] == "aa" + assert r[1] == "1" def test_select_columns_slice_reversed(self): "range of columns (slice) by row reversed" - conn = init() - r = conn.execute(""" + cursor= init() + cursor.execute(""" SELECT FIRST 2 REVERSED 3..1 FROM StandardLongA WHERE KEY = 'aa'; """) - assert len(r) == 1, "%d != 1" % len(r) - assert len(r[0].columns) == 2 - assert r[0].columns[0].value == "3" - assert r[0].columns[1].value == "2" + assert cursor.rowcount == 1, "%d != 1" % cursor.rowcount + r = cursor.fetchone() + assert len(r) == 3 + assert r[0] == 'aa' + assert r[1] == "3" + assert r[2] == "2" def test_error_on_multiple_key_by(self): "ensure multiple key-bys in where clause excepts" - conn = init() - assert_raises(CQLException, conn.execute, """ + cursor = init() + assert_raises(cql.ProgrammingError, cursor.execute, """ SELECT 'col' FROM StandardString1 WHERE KEY = 'ka' AND KEY = 'kb'; """) def test_index_scan_equality(self): "indexed scan where column equals value" - conn = init() - r = conn.execute(""" + cursor = init() + cursor.execute(""" SELECT 'birthdate' FROM IndexedA WHERE 'birthdate' = 100 """) + assert cursor.rowcount == 2 + + r = cursor.fetchone() + assert r[0] == "asmith" + assert len(r) == 2 + + r = cursor.fetchone() + assert r[0] == "dozer" assert len(r) == 2 - assert r[0].key == "asmith" - assert r[1].key == "dozer" - assert len(r[0].columns) == 1 - assert len(r[1].columns) == 1 def test_index_scan_greater_than(self): "indexed scan where a column is greater than a value" - conn = init() - r = conn.execute(""" + cursor = init() + cursor.execute(""" SELECT 'birthdate' FROM IndexedA WHERE 'birthdate' = 100 AND 'unindexed' > 200 """) - assert len(r) == 1 - assert r[0].key == "asmith" + assert cursor.rowcount == 1 + r = cursor.fetchone() + assert r[0] == "asmith" def test_index_scan_with_start_key(self): "indexed scan with a starting key" - conn = init() - r = conn.execute(""" + cursor = init() + cursor.execute(""" SELECT 'birthdate' FROM IndexedA WHERE 'birthdate' = 100 AND KEY > 'asmithZ' """) - assert len(r) == 1 - assert r[0].key == "dozer" + assert cursor.rowcount == 1 + r = cursor.fetchone() + assert r[0] == "dozer" def test_no_where_clause(self): "empty where clause (range query w/o start key)" - conn = init() - r = conn.execute("SELECT 'col' FROM StandardString1 LIMIT 3") - assert len(r) == 3 - assert r[0].key == "ka" - assert r[1].key == "kb" - assert r[2].key == "kc" + cursor = init() + cursor.execute("SELECT 'col' FROM StandardString1 LIMIT 3") + assert cursor.rowcount == 3 + rows = cursor.fetchmany(3) + assert rows[0][0] == "ka" + assert rows[1][0] == "kb" + assert rows[2][0] == "kc" def test_column_count(self): "getting a result count instead of results" - conn = init() - r = conn.execute(""" + cursor = init() + cursor.execute(""" SELECT COUNT(1..4) FROM StandardLongA WHERE KEY = 'aa'; """) - assert r == 4, "expected 4 results, got %d" % (r and r or 0) + r = cursor.fetchone() + assert r[0] == 4, "expected 4 results, got %d" % (r and r or 0) def test_truncate_columnfamily(self): "truncating a column family" - conn = init() - conn.execute('TRUNCATE StandardString1;') - r = conn.execute("SELECT 'cd1' FROM StandardString1 WHERE KEY = 'kd'") - assert len(r) == 0 + cursor = init() + cursor.execute('TRUNCATE StandardString1;') + cursor.execute("SELECT 'cd1' FROM StandardString1 WHERE KEY = 'kd'") + assert cursor.rowcount == 0 def test_delete_columns(self): "delete columns from a row" - conn = init() - r = conn.execute(""" + cursor = init() + cursor.execute(""" SELECT 'cd1', 'col' FROM StandardString1 WHERE KEY = 'kd' """) - assert "cd1" in [i.name for i in r[0].columns] - assert "col" in [i.name for i in r[0].columns] - conn.execute(""" + colnames = [col_d[0] for col_d in cursor.description] + assert "cd1" in colnames + assert "col" in colnames + cursor.execute(""" DELETE 'cd1', 'col' FROM StandardString1 WHERE KEY = 'kd' """) - r = conn.execute(""" + cursor.execute(""" SELECT 'cd1', 'col' FROM StandardString1 WHERE KEY = 'kd' """) - assert len(r[0].columns) == 0 + r = cursor.fetchone() + assert len(r) == 1 def test_delete_columns_multi_rows(self): "delete columns from multiple rows" - conn = init() - r = conn.execute("SELECT 'col' FROM StandardString1 WHERE KEY = 'kc'") - assert len(r[0].columns) == 1 - r = conn.execute("SELECT 'col' FROM StandardString1 WHERE KEY = 'kd'") - assert len(r[0].columns) == 1 + cursor = init() - conn.execute(""" + cursor.execute("SELECT 'col' FROM StandardString1 WHERE KEY = 'kc'") + r = cursor.fetchone() + assert len(r) == 2 + + cursor.execute("SELECT 'col' FROM StandardString1 WHERE KEY = 'kd'") + r = cursor.fetchone() + assert len(r) == 2 + + cursor.execute(""" DELETE 'col' FROM StandardString1 WHERE KEY IN ('kc', 'kd') """) - r = conn.execute("SELECT 'col' FROM StandardString1 WHERE KEY = 'kc'") - assert len(r[0].columns) == 0 - r = conn.execute("SELECT 'col' FROM StandardString1 WHERE KEY = 'kd'") - assert len(r[0].columns) == 0 + cursor.execute("SELECT 'col' FROM StandardString1 WHERE KEY = 'kc'") + r = cursor.fetchone() + assert len(r) == 1 + + cursor.execute("SELECT 'col' FROM StandardString1 WHERE KEY = 'kd'") + r = cursor.fetchone() + assert len(r) == 1 def test_delete_rows(self): "delete entire rows" - conn = init() - r = conn.execute(""" + cursor = init() + cursor.execute(""" SELECT 'cd1', 'col' FROM StandardString1 WHERE KEY = 'kd' """) - assert "cd1" in [i.name for i in r[0].columns] - assert "col" in [i.name for i in r[0].columns] - conn.execute("DELETE FROM StandardString1 WHERE KEY = 'kd'") - r = conn.execute(""" + colnames = [col_d[0] for col_d in cursor.description] + assert "cd1" in colnames + assert "col" in colnames + cursor.execute("DELETE FROM StandardString1 WHERE KEY = 'kd'") + cursor.execute(""" SELECT 'cd1', 'col' FROM StandardString1 WHERE KEY = 'kd' """) - assert len(r[0].columns) == 0 - + r = cursor.fetchone() + assert len(r) == 1 + def test_create_keyspace(self): "create a new keyspace" - init().execute(""" + cursor = init() + cursor.execute(""" CREATE KEYSPACE TestKeyspace42 WITH strategy_options:DC1 = '1' AND strategy_class = 'NetworkTopologyStrategy' """) - + # TODO: temporary (until this can be done with CQL). ksdef = thrift_client.describe_keyspace("TestKeyspace42") - + strategy_class = "org.apache.cassandra.locator.NetworkTopologyStrategy" assert ksdef.strategy_class == strategy_class assert ksdef.strategy_options['DC1'] == "1" - + def test_drop_keyspace(self): "removing a keyspace" - conn = init() - conn.execute(""" - CREATE KEYSPACE Keyspace4Drop - WITH strategy_class = SimpleStrategy AND strategy_options:replication_factor = 1 + cursor = init() + cursor.execute(""" + CREATE KEYSPACE Keyspace4Drop WITH strategy_options:replication_factor = '1' + AND strategy_class = 'SimpleStrategy' """) - + # TODO: temporary (until this can be done with CQL). thrift_client.describe_keyspace("Keyspace4Drop") - - conn.execute('DROP KEYSPACE Keyspace4Drop;') - + + cursor.execute('DROP KEYSPACE Keyspace4Drop;') + # Technically this should throw a ttypes.NotFound(), but this is # temporary and so not worth requiring it on PYTHONPATH. assert_raises(Exception, thrift_client.describe_keyspace, "Keyspace4Drop") - + def test_create_column_family(self): "create a new column family" - conn = init() - conn.execute(""" - CREATE KEYSPACE CreateCFKeyspace WITH strategy_options:replication_factor = 1 - AND strategy_class = 'SimpleStrategy'; + cursor = init() + cursor.execute(""" + CREATE KEYSPACE CreateCFKeyspace WITH strategy_options:replication_factor = '1' + AND strategy_class = 'SimpleStrategy'; """) - conn.execute("USE CreateCFKeyspace;") - - conn.execute(""" + cursor.execute("USE CreateCFKeyspace;") + + cursor.execute(""" CREATE COLUMNFAMILY NewCf1 ( KEY varint PRIMARY KEY, 'username' text, @@ -371,7 +410,7 @@ class TestCql(ThriftTester): 'id' uuid ) WITH comment = 'shiny, new, cf' AND default_validation = ascii; """) - + # TODO: temporary (until this can be done with CQL). ksdef = thrift_client.describe_keyspace("CreateCFKeyspace") assert len(ksdef.cf_defs) == 1, \ @@ -383,27 +422,27 @@ class TestCql(ThriftTester): assert cfam.default_validation_class == "org.apache.cassandra.db.marshal.AsciiType" assert cfam.comparator_type == "org.apache.cassandra.db.marshal.UTF8Type" assert cfam.key_validation_class == "org.apache.cassandra.db.marshal.IntegerType" - + # Missing primary key - assert_raises(CQLException, conn.execute, "CREATE COLUMNFAMILY NewCf2") - + assert_raises(cql.ProgrammingError, cursor.execute, "CREATE COLUMNFAMILY NewCf2") + # Too many primary keys - assert_raises(CQLException, - conn.execute, + assert_raises(cql.ProgrammingError, + cursor.execute, """CREATE COLUMNFAMILY NewCf2 (KEY varint PRIMARY KEY, KEY text PRIMARY KEY)""") - + # No column defs - conn.execute("""CREATE COLUMNFAMILY NewCf3 + cursor.execute("""CREATE COLUMNFAMILY NewCf3 (KEY varint PRIMARY KEY) WITH comparator = bigint""") ksdef = thrift_client.describe_keyspace("CreateCFKeyspace") assert len(ksdef.cf_defs) == 2, \ "expected 3 column families total, found %d" % len(ksdef.cf_defs) cfam = [i for i in ksdef.cf_defs if i.name == "NewCf3"][0] assert cfam.comparator_type == "org.apache.cassandra.db.marshal.LongType" - + # Column defs, defaults otherwise - conn.execute("""CREATE COLUMNFAMILY NewCf4 + cursor.execute("""CREATE COLUMNFAMILY NewCf4 (KEY varint PRIMARY KEY, 'a' varint, 'b' varint) WITH comparator = text;""") ksdef = thrift_client.describe_keyspace("CreateCFKeyspace") @@ -415,34 +454,34 @@ class TestCql(ThriftTester): for coldef in cfam.column_metadata: assert coldef.name in ("a", "b"), "Unknown column name " + coldef.name assert coldef.validation_class.endswith("marshal.IntegerType") - + def test_drop_columnfamily(self): "removing a column family" - conn = init() - conn.execute(""" - CREATE KEYSPACE Keyspace4CFDrop WITH strategy_options:replication_factor = 1 - AND strategy_class = 'SimpleStrategy'; + cursor = init() + cursor.execute(""" + CREATE KEYSPACE Keyspace4CFDrop WITH strategy_options:replication_factor = '1' + AND strategy_class = 'SimpleStrategy'; """) - conn.execute('USE Keyspace4CFDrop;') - conn.execute('CREATE COLUMNFAMILY CF4Drop (KEY varint PRIMARY KEY);') - + cursor.execute('USE Keyspace4CFDrop;') + cursor.execute('CREATE COLUMNFAMILY CF4Drop (KEY varint PRIMARY KEY);') + # TODO: temporary (until this can be done with CQL). ksdef = thrift_client.describe_keyspace("Keyspace4CFDrop") assert len(ksdef.cf_defs), "Column family not created!" - - conn.execute('DROP COLUMNFAMILY CF4Drop;') - + + cursor.execute('DROP COLUMNFAMILY CF4Drop;') + ksdef = thrift_client.describe_keyspace("Keyspace4CFDrop") assert not len(ksdef.cf_defs), "Column family not deleted!" - + def test_create_indexs(self): "creating column indexes" - conn = init() - conn.execute("USE Keyspace1") - conn.execute("CREATE COLUMNFAMILY CreateIndex1 (KEY text PRIMARY KEY)") - conn.execute("CREATE INDEX namedIndex ON CreateIndex1 (items)") - conn.execute("CREATE INDEX ON CreateIndex1 (stuff)") - + cursor = init() + cursor.execute("USE Keyspace1") + cursor.execute("CREATE COLUMNFAMILY CreateIndex1 (KEY text PRIMARY KEY)") + cursor.execute("CREATE INDEX namedIndex ON CreateIndex1 (items)") + cursor.execute("CREATE INDEX ON CreateIndex1 (stuff)") + # TODO: temporary (until this can be done with CQL). ksdef = thrift_client.describe_keyspace("Keyspace1") cfam = [i for i in ksdef.cf_defs if i.name == "CreateIndex1"][0] @@ -455,205 +494,208 @@ class TestCql(ThriftTester): assert stuff.index_type == 0, "missing index" # already indexed - assert_raises(CQLException, - conn.execute, + assert_raises(cql.ProgrammingError, + cursor.execute, "CREATE INDEX ON CreateIndex1 (stuff)") def test_time_uuid(self): "store and retrieve time-based (type 1) uuids" - conn = init() - + cursor = init() + # Store and retrieve a timeuuid using it's hex-formatted string timeuuid = uuid.uuid1() - conn.execute(""" + cursor.execute(""" UPDATE StandardTimeUUID SET '%s' = 10 WHERE KEY = 'uuidtest' """ % str(timeuuid)) - - r = conn.execute(""" + + cursor.execute(""" SELECT '%s' FROM StandardTimeUUID WHERE KEY = 'uuidtest' """ % str(timeuuid)) - assert r[0].columns[0].name == timeuuid - + d = cursor.description + assert d[1][0] == timeuuid, "%s, %s" % (str(d[1][0]), str(timeuuid)) + # Tests a node-side conversion from bigint to UUID. ms = uuid1bytes_to_millis(uuid.uuid1().bytes) - conn.execute(""" + cursor.execute(""" UPDATE StandardTimeUUIDValues SET 'id' = %d WHERE KEY = 'uuidtest' """ % ms) - - r = conn.execute(""" + + cursor.execute(""" SELECT 'id' FROM StandardTimeUUIDValues WHERE KEY = 'uuidtest' """) - assert uuid1bytes_to_millis(r[0].columns[0].value.bytes) == ms - + r = cursor.fetchone() + assert uuid1bytes_to_millis(r[1].bytes) == ms + # Tests a node-side conversion from ISO8601 to UUID. - conn.execute(""" + cursor.execute(""" UPDATE StandardTimeUUIDValues SET 'id2' = '2011-01-31 17:00:00-0000' WHERE KEY = 'uuidtest' """) - - r = conn.execute(""" + + cursor.execute(""" SELECT 'id2' FROM StandardTimeUUIDValues WHERE KEY = 'uuidtest' """) # 2011-01-31 17:00:00-0000 == 1296493200000ms - ms = uuid1bytes_to_millis(r[0].columns[0].value.bytes) + r = cursor.fetchone() + ms = uuid1bytes_to_millis(r[1].bytes) assert ms == 1296493200000, \ "%d != 1296493200000 (2011-01-31 17:00:00-0000)" % ms # Tests node-side conversion of timeuuid("now") to UUID - conn.execute(""" + cursor.execute(""" UPDATE StandardTimeUUIDValues SET 'id3' = 'now' WHERE KEY = 'uuidtest' """) - - r = conn.execute(""" + + cursor.execute(""" SELECT 'id3' FROM StandardTimeUUIDValues WHERE KEY = 'uuidtest' """) - ms = uuid1bytes_to_millis(r[0].columns[0].value.bytes) + r = cursor.fetchone() + ms = uuid1bytes_to_millis(r[1].bytes) assert ((time.time() * 1e3) - ms) < 100, \ "new timeuuid not within 100ms of now (UPDATE vs. SELECT)" uuid_range = [] - update = "UPDATE StandardTimeUUID SET ? = ? WHERE KEY = slicetest" + update = "UPDATE StandardTimeUUID SET :name = :val WHERE KEY = slicetest" for i in range(5): uuid_range.append(uuid.uuid1()) - conn.execute(update, uuid_range[i], i) + cursor.execute(update, dict(name=uuid_range[i], val=i)) + + cursor.execute(""" + SELECT :start..:finish FROM StandardTimeUUID WHERE KEY = slicetest + """, dict(start=uuid_range[0], finish=uuid_range[len(uuid_range)-1])) + d = cursor.description + for (i, col_d) in enumerate(d[1:]): + assert uuid_range[i] == col_d[0] + - r = conn.execute(""" - SELECT ?..? FROM StandardTimeUUID WHERE KEY = slicetest - """, uuid_range[0], uuid_range[len(uuid_range)-1]) - - for (i, col) in enumerate(r[0]): - assert uuid_range[i] == col.name - - def test_lexical_uuid(self): "store and retrieve lexical uuids" - conn = init() + cursor = init() uid = uuid.uuid4() - conn.execute("UPDATE StandardUUID SET ? = 10 WHERE KEY = 'uuidtest'", - uid) - - r = conn.execute("SELECT ? FROM StandardUUID WHERE KEY = 'uuidtest'", - uid) - assert r[0].columns[0].name == uid, r[0].columns[0].name - + cursor.execute("UPDATE StandardUUID SET :name = 10 WHERE KEY = 'uuidtest'", + dict(name=uid)) + + cursor.execute("SELECT :name FROM StandardUUID WHERE KEY = 'uuidtest'", + dict(name=uid)) + d = cursor.description + assert d[1][0] == uid, "expected %s, got %s (%s)" % \ + (uid.bytes.encode('hex'), str(d[1][0]).encode('hex'), d[1][1]) + # TODO: slices of uuids from cf w/ LexicalUUIDType comparator - + def test_utf8_read_write(self): "reading and writing utf8 values" - conn = init() + cursor = init() # Sorting: ¢ (u00a2) < © (u00a9) < ® (u00ae) < ¿ (u00bf) - conn.execute("UPDATE StandardUtf82 SET ? = v1 WHERE KEY = k1", "¿") - conn.execute("UPDATE StandardUtf82 SET ? = v1 WHERE KEY = k1", "©") - conn.execute("UPDATE StandardUtf82 SET ? = v1 WHERE KEY = k1", "®") - conn.execute("UPDATE StandardUtf82 SET ? = v1 WHERE KEY = k1", "¢") - - r = conn.execute("SELECT * FROM StandardUtf82 WHERE KEY = k1") - assert r[0][0].name == u"¢" - assert r[0][1].name == u"©" - assert r[0][2].name == u"®" - assert r[0][3].name == u"¿" - - r = conn.execute("SELECT ?..'' FROM StandardUtf82 WHERE KEY = k1", "©") - assert len(r[0]) == 3 - assert r[0][0].name == u"©" - assert r[0][1].name == u"®" - assert r[0][2].name == u"¿" - + cursor.execute("UPDATE StandardUtf82 SET :name = v1 WHERE KEY = k1", dict(name="¿")) + cursor.execute("UPDATE StandardUtf82 SET :name = v1 WHERE KEY = k1", dict(name="©")) + cursor.execute("UPDATE StandardUtf82 SET :name = v1 WHERE KEY = k1", dict(name="®")) + cursor.execute("UPDATE StandardUtf82 SET :name = v1 WHERE KEY = k1", dict(name="¢")) + + cursor.execute("SELECT * FROM StandardUtf82 WHERE KEY = k1") + d = cursor.description + assert d[1][0] == u"¢", d[1][0] + assert d[2][0] == u"©", d[2][0] + assert d[3][0] == u"®", d[3][0] + assert d[4][0] == u"¿", d[4][0] + + cursor.execute("SELECT :start..'' FROM StandardUtf82 WHERE KEY = k1", dict(start="©")) + r = cursor.fetchone() + assert len(r) == 4 + d = cursor.description + assert d[1][0] == u"©" + assert d[2][0] == u"®" + assert d[3][0] == u"¿" + def test_read_write_negative_numerics(self): "reading and writing negative numeric values" - conn = init() + cursor = init() for cf in ("StandardIntegerA", "StandardLongA"): for i in range(10): - conn.execute("UPDATE ? SET ? = ? WHERE KEY = negatives;", - cf, - -(i + 1), - i) - r = conn.execute("SELECT ?..? FROM ? WHERE KEY = negatives;", - -10, - -1, - cf) - assert len(r[0]) == 10, \ - "returned %d columns, expected %d" % (len(r[0]), 10) - assert r[0][0].name == -10 - assert r[0][9].name == -1 - + cursor.execute("UPDATE :cf SET :name = :val WHERE KEY = negatives;", + dict(cf=cf, name=-(i + 1), val=i)) + + cursor.execute("SELECT :start..:finish FROM :cf WHERE KEY = negatives;", + dict(start=-10, finish=-1, cf=cf)) + r = cursor.fetchone() + assert len(r) == 11, \ + "returned %d columns, expected %d" % (len(r) - 1, 10) + d = cursor.description + assert d[1][0] == -10 + assert d[10][0] == -1 + def test_escaped_quotes(self): "reading and writing strings w/ escaped quotes" - conn = init() - - conn.execute(""" - UPDATE StandardString1 SET 'x''and''y' = z WHERE KEY = ? - """, "test_escaped_quotes") - - r = conn.execute(""" - SELECT 'x''and''y' FROM StandardString1 WHERE KEY = ? - """, "test_escaped_quotes") - assert (len(r) == 1) and (len(r[0]) == 1), "wrong number of results" - assert r[0][0].name == "x\'and\'y" - + cursor = init() + + cursor.execute(""" + UPDATE StandardString1 SET 'x''and''y' = z WHERE KEY = :key + """, dict(key="test_escaped_quotes")) + + cursor.execute(""" + SELECT 'x''and''y' FROM StandardString1 WHERE KEY = :key + """, dict(key="test_escaped_quotes")) + assert cursor.rowcount == 1 + r = cursor.fetchone() + assert len(r) == 2, "wrong number of results" + d = cursor.description + assert d[1][0] == "x\'and\'y" + def test_typed_keys(self): "using typed keys" - conn = init() - r = conn.execute("SELECT * FROM StandardString1 WHERE KEY = ?", "ka") - assert isinstance(r[0].key, unicode), \ - "wrong key-type returned, expected unicode, got %s" % type(r[0].key) - + cursor = init() + cursor.execute("SELECT * FROM StandardString1 WHERE KEY = :key", dict(key="ka")) + r = cursor.fetchone() + assert isinstance(r[0], unicode), \ + "wrong key-type returned, expected unicode, got %s" % type(r[0]) + # FIXME: The above is woefully inadequate, but the test config uses # CollatingOrderPreservingPartitioner which only supports UTF8. - + def test_write_using_insert(self): "peforming writes using \"insert\"" - conn = init() - conn.execute("INSERT INTO StandardUtf82 (KEY, ?, ?) VALUES (?, ?, ?)", - "pork", - "beef", - "meat", - "bacon", - "brisket") - - r = conn.execute("SELECT * FROM StandardUtf82 WHERE KEY = ?", "meat") - assert r[0][0].name == "beef" - assert r[0][0].value == "brisket" - assert r[0][1].name == "pork" - assert r[0][1].value == "bacon" - + cursor = init() + cursor.execute("INSERT INTO StandardUtf82 (KEY, :c1, :c2) VALUES (:key, :v1, :v2)", + dict(c1="pork", c2="beef", key="meat", v1="bacon", v2="brisket")) + + cursor.execute("SELECT * FROM StandardUtf82 WHERE KEY = :key", dict(key="meat")) + r = cursor.fetchone() + d = cursor.description + assert d[1][0] == "beef" + assert r[1] == "brisket" + + assert d[2][0] == "pork" + assert r[2] == "bacon" + # Bad writes. - + # Too many column values - assert_raises(CQLException, - conn.execute, - "INSERT INTO StandardUtf82 (KEY, ?) VALUES (?, ?, ?)", - "name1", - "key0", - "value1", - "value2") - + assert_raises(cql.ProgrammingError, + cursor.execute, + "INSERT INTO StandardUtf82 (KEY, :c1) VALUES (:key, :v1, :v2)", + dict(c1="name1", key="key0", v1="value1", v2="value2")) + # Too many column names, (not enough column values) - assert_raises(CQLException, - conn.execute, - "INSERT INTO StandardUtf82 (KEY, ?, ?) VALUES (?, ?)", - "name1", - "name2", - "key0", - "value1") - + assert_raises(cql.ProgrammingError, + cursor.execute, + "INSERT INTO StandardUtf82 (KEY, :c1, :c2) VALUES (:key, :v1)", + dict(c1="name1", c2="name2", key="key0", v1="value1")) + def test_compression_disabled(self): "reading and writing w/ compression disabled" - conn = init() - conn.execute("UPDATE StandardString1 SET ? = ? WHERE KEY = ?", - "some_name", - "some_value", - "compression_test", - compression='NONE') - - r = conn.execute("SELECT ? FROM StandardString1 WHERE KEY = ?", - "some_name", - "compression_test", - compression='NONE') - - assert len(r) == 1, "expected 1 result, got %d" % len(r) - assert r[0][0].name == "some_name", \ - "unrecognized name '%s'" % r[0][0].name - assert r[0][0].value == "some_value", \ - "unrecognized value '%s'" % r[0][0].value + cursor = init() + cursor.compression = 'NONE' + cursor.execute("UPDATE StandardString1 SET :name = :val WHERE KEY = :key", + dict(name="some_name", val="some_value", key="compression_test")) + + cursor.execute("SELECT :name FROM StandardString1 WHERE KEY = :key", + dict(name="some_name", key="compression_test")) + + assert cursor.rowcount == 1, "expected 1 result, got %d" % cursor.rowcount + colnames = [col_d[0] for col_d in cursor.description] + assert colnames[1] == "some_name", \ + "unrecognized name '%s'" % colnames[1] + r = cursor.fetchone() + assert r[1] == "some_value", \ + "unrecognized value '%s'" % r[1] diff --git a/test/unit/org/apache/cassandra/io/LazilyCompactedRowTest.java b/test/unit/org/apache/cassandra/io/LazilyCompactedRowTest.java index 0ee2e175b3..fe9aab704b 100644 --- a/test/unit/org/apache/cassandra/io/LazilyCompactedRowTest.java +++ b/test/unit/org/apache/cassandra/io/LazilyCompactedRowTest.java @@ -27,9 +27,7 @@ import java.io.*; import java.nio.ByteBuffer; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.HashSet; import java.util.Collection; -import java.util.Set; import java.util.concurrent.ExecutionException; import org.apache.cassandra.CleanupHelper; @@ -309,7 +307,7 @@ public class LazilyCompactedRowTest extends CleanupHelper { public LazyCompactionIterator(Iterable sstables, CompactionController controller) throws IOException { - super("Lazy", sstables, controller); + super(CompactionType.UNKNOWN, sstables, controller); } @Override @@ -323,7 +321,7 @@ public class LazilyCompactedRowTest extends CleanupHelper { public PreCompactingIterator(Iterable sstables, CompactionController controller) throws IOException { - super("Pre", sstables, controller); + super(CompactionType.UNKNOWN, sstables, controller); } @Override diff --git a/tools/stress/src/org/apache/cassandra/stress/Session.java b/tools/stress/src/org/apache/cassandra/stress/Session.java index 942ffdec30..942c3467df 100644 --- a/tools/stress/src/org/apache/cassandra/stress/Session.java +++ b/tools/stress/src/org/apache/cassandra/stress/Session.java @@ -229,9 +229,6 @@ public class Session if (cmd.hasOption("g")) keysPerCall = Integer.parseInt(cmd.getOptionValue("g")); - if (cmd.hasOption("l")) - replicationStrategyOptions.put("replication_factor", String.valueOf(Integer.parseInt(cmd.getOptionValue("l")))); - if (cmd.hasOption("e")) consistencyLevel = ConsistencyLevel.valueOf(cmd.getOptionValue("e").toUpperCase()); @@ -241,6 +238,11 @@ public class Session if (cmd.hasOption("R")) replicationStrategy = cmd.getOptionValue("R"); + if (cmd.hasOption("l")) + replicationStrategyOptions.put("replication_factor", String.valueOf(Integer.parseInt(cmd.getOptionValue("l")))); + else if (replicationStrategy.endsWith("SimpleStrategy")) + replicationStrategyOptions.put("replication_factor", "1"); + if (cmd.hasOption("O")) { String[] pairs = StringUtils.split(cmd.getOptionValue("O"), ','); @@ -421,19 +423,18 @@ public class Session keyspace.setCf_defs(new ArrayList(Arrays.asList(standardCfDef, superCfDef, counterCfDef, counterSuperCfDef))); - Cassandra.Client client = getClient(false); try { client.system_add_keyspace(keyspace); out.println(String.format("Created keyspaces. Sleeping %ss for propagation.", nodes.length)); - Thread.sleep(nodes.length * 1000); // seconds } catch (InvalidRequestException e) { - out.println(e.getWhy()); + out.println("Unable to create stress keyspace: " + e.getWhy()); + System.exit(1); } catch (Exception e) {