cassandra/src/java/org/apache/cassandra/cli/CliClient.java

1989 lines
83 KiB
Java

/**
* 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;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.*;
import com.google.common.base.Charsets;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.Tree;
import org.apache.cassandra.auth.SimpleAuthenticator;
import org.apache.cassandra.config.ConfigurationException;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.thrift.*;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.UUIDGen;
import org.apache.thrift.TException;
// Cli Client Side Library
public class CliClient
{
/**
* Available value conversion functions
* Used by convertValueByFunction(Tree functionCall) method
*/
private enum Function
{
BYTES (BytesType.instance),
INTEGER (IntegerType.instance),
LONG (LongType.instance),
LEXICALUUID (LexicalUUIDType.instance),
TIMEUUID (TimeUUIDType.instance),
UTF8 (UTF8Type.instance),
ASCII (AsciiType.instance);
private AbstractType validator;
Function(AbstractType validator)
{
this.validator = validator;
}
public AbstractType getValidator()
{
return this.validator;
}
public static String getFunctionNames()
{
Function[] functions = Function.values();
StringBuilder functionNames = new StringBuilder();
for (int i = 0; i < functions.length; i++)
{
StringBuilder currentName = new StringBuilder(functions[i].name().toLowerCase());
functionNames.append(currentName.append(((i != functions.length-1) ? ", " : ".")));
}
return functionNames.toString();
}
}
/*
* the <i>add column family</i> command requires a list of arguments,
* this enum defines which arguments are valid.
*/
private 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,
}
private EnumMap<ColumnFamilyArgument, String> argumentExplanations = new EnumMap<ColumnFamilyArgument, String>(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, 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");
put(ColumnFamilyArgument.MEMTABLE_THROUGHPUT, "... or after this many bytes have been written");
put(ColumnFamilyArgument.MEMTABLE_FLUSH_AFTER, "... or after this many seconds");
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");
}};
/*
* the <i>add keyspace</i> command requires a list of arguments,
* this enum defines which arguments are valid
*/
private enum AddKeyspaceArgument {
REPLICATION_FACTOR,
PLACEMENT_STRATEGY,
STRATEGY_OPTIONS
}
private Cassandra.Client thriftClient_ = null;
private CliSessionState css_ = null;
private String keySpace = null;
private String username = null;
private Map<String, KsDef> keyspacesMap = new HashMap<String, KsDef>();
private final String DEFAULT_PLACEMENT_STRATEGY = "org.apache.cassandra.locator.SimpleStrategy";
public CliClient(CliSessionState cliSessionState, Cassandra.Client thriftClient)
{
css_ = cliSessionState;
thriftClient_ = thriftClient;
}
// Execute a CLI Statement
public void executeCLIStmt(String stmt) throws TException, NotFoundException, InvalidRequestException, UnavailableException, TimedOutException, IllegalAccessException, ClassNotFoundException, InstantiationException, NoSuchFieldException
{
CommonTree ast = CliCompiler.compileQuery(stmt);
switch (ast.getType())
{
case CliParser.NODE_EXIT:
cleanupAndExit();
break;
case CliParser.NODE_THRIFT_GET:
executeGet(ast);
break;
case CliParser.NODE_THRIFT_GET_WITH_CONDITIONS:
executeGetWithConditions(ast);
break;
case CliParser.NODE_HELP:
printCmdHelp(ast);
break;
case CliParser.NODE_THRIFT_SET:
executeSet(ast);
break;
case CliParser.NODE_THRIFT_DEL:
executeDelete(ast);
break;
case CliParser.NODE_THRIFT_COUNT:
executeCount(ast);
break;
case CliParser.NODE_ADD_KEYSPACE:
executeAddKeyspace(ast.getChild(0));
break;
case CliParser.NODE_ADD_COLUMN_FAMILY:
executeAddColumnFamily(ast.getChild(0));
break;
case CliParser.NODE_UPDATE_KEYSPACE:
executeUpdateKeyspace(ast.getChild(0));
break;
case CliParser.NODE_UPDATE_COLUMN_FAMILY:
executeUpdateColumnFamily(ast.getChild(0));
break;
case CliParser.NODE_DEL_COLUMN_FAMILY:
executeDelColumnFamily(ast);
break;
case CliParser.NODE_DEL_KEYSPACE:
executeDelKeyspace(ast);
break;
case CliParser.NODE_SHOW_CLUSTER_NAME:
executeShowClusterName();
break;
case CliParser.NODE_SHOW_VERSION:
executeShowVersion();
break;
case CliParser.NODE_SHOW_TABLES:
executeShowTables();
break;
case CliParser.NODE_DESCRIBE_TABLE:
executeDescribeTable(ast);
break;
case CliParser.NODE_USE_TABLE:
executeUseTable(ast);
break;
case CliParser.NODE_CONNECT:
executeConnect(ast);
break;
case CliParser.NODE_LIST:
executeList(ast);
break;
case CliParser.NODE_NO_OP:
// comment lines come here; they are treated as no ops.
break;
default:
css_.err.println("Invalid Statement (Type: " + ast.getType() + ")");
if (css_.batch)
System.exit(2);
break;
}
}
private void printCmdHelp(CommonTree ast)
{
if (ast.getChildCount() > 0)
{
int helpType = ast.getChild(0).getType();
switch(helpType)
{
case CliParser.NODE_HELP:
css_.out.println("help <command>\n");
css_.out.println("Display the general help page with a list of available commands.");
break;
case CliParser.NODE_CONNECT:
css_.out.println("connect <hostname>/<port>\n");
css_.out.println("Connect to the specified host on the specified port.\n");
css_.out.println("example:");
css_.out.println("connect localhost/9160");
break;
case CliParser.NODE_USE_TABLE:
css_.out.println("use <keyspace>");
css_.out.println("use <keyspace> <username> '<password>'\n");
css_.out.println("Switch to the specified keyspace. The optional username and password fields");
css_.out.println("are needed when performing authentication.\n");
break;
case CliParser.NODE_DESCRIBE_TABLE:
css_.out.println("describe keyspace <keyspace>\n");
css_.out.println("Show additional information about the specified keyspace.\n");
css_.out.println("example:");
css_.out.println("describe keyspace system");
break;
case CliParser.NODE_EXIT:
css_.out.println("exit");
css_.out.println("quit\n");
css_.out.println("Exit this utility.");
break;
case CliParser.NODE_SHOW_CLUSTER_NAME:
css_.out.println("show cluster name\n");
css_.out.println("Displays the name of the currently connected cluster.");
break;
case CliParser.NODE_SHOW_VERSION:
css_.out.println("show api version\n");
css_.out.println("Displays the API version number.");
break;
case CliParser.NODE_SHOW_TABLES:
css_.out.println("show keyspaces\n");
css_.out.println("Displays a list of the keyspaces available on the currently connected cluster.");
break;
case CliParser.NODE_ADD_KEYSPACE:
css_.out.println("create keyspace <keyspace>");
css_.out.println("create keyspace <keyspace> with <att1>=<value1>");
css_.out.println("create keyspace <keyspace> with <att1>=<value1> and <att2>=<value2> ...\n");
css_.out.println("Create a new keyspace with the specified values for the given set of attributes.\n");
css_.out.println("valid attributes are:");
css_.out.println(" replication_factor: to how many nodes should entries to this keyspace be");
css_.out.println(" replicated. Valid entries are integers greater than 0.");
css_.out.println(" placement_strategy: the fully qualified class used to place replicas in");
css_.out.println(" this keyspace. Valid values are");
css_.out.println(" org.apache.cassandra.locator.SimpleStrategy,");
css_.out.println(" org.apache.cassandra.locator.NetworkTopologyStrategy,");
css_.out.println(" and org.apache.cassandra.locator.OldNetworkTopologyStrategy");
css_.out.println(" strategy_options: additional options for placement_strategy.\n");
css_.out.println("example:");
css_.out.println("create keyspace foo with replication_factor = 3 and ");
css_.out.println(" placement_strategy = 'org.apache.cassandra.locator.SimpleStrategy'");
css_.out.println(" and strategy_options=[{DC1:2, DC2:2}]");
break;
case CliParser.NODE_UPDATE_KEYSPACE:
css_.out.println("update keyspace <keyspace>");
css_.out.println("update keyspace <keyspace> with <att1>=<value1>");
css_.out.println("update keyspace <keyspace> with <att1>=<value1> and <att2>=<value2> ...\n");
css_.out.println("Update a keyspace with the specified values for the given set of attributes.\n");
css_.out.println("valid attributes are:");
css_.out.println(" replication_factor: to how many nodes should entries to this keyspace be");
css_.out.println(" replicated. Valid entries are integers greater than 0.");
css_.out.println(" placement_strategy: the fully qualified class used to place replicas in");
css_.out.println(" this keyspace. Valid values are");
css_.out.println(" org.apache.cassandra.locator.SimpleStrategy,");
css_.out.println(" org.apache.cassandra.locator.NetworkTopologyStrategy,");
css_.out.println(" and org.apache.cassandra.locator.OldNetworkTopologyStrategy");
css_.out.println(" strategy_options: additional options for placement_strategy.\n");
css_.out.println("example:");
css_.out.println("update keyspace foo with replication_factor = 2 and ");
css_.out.println(" placement_strategy = 'org.apache.cassandra.locator.LocalStrategy'");
css_.out.println(" and strategy_options=[{DC1:1, DC2:4, DC3:2}]");
break;
case CliParser.NODE_ADD_COLUMN_FAMILY:
css_.out.println("create column family Bar");
css_.out.println("create column family Bar with <att1>=<value1>");
css_.out.println("create column family Bar with <att1>=<value1> and <att2>=<value2>...\n");
css_.out.println("Create a new column family with the specified values for the given set of");
css_.out.println("attributes. Note that you must be using a keyspace.\n");
css_.out.println("valid attributes are:");
for (ColumnFamilyArgument argument : ColumnFamilyArgument.values())
css_.out.printf(" - %s: %s\n", argument.toString().toLowerCase(), argumentExplanations.get(argument));
css_.out.println(" - column_metadata: Metadata which describes columns of column family.");
css_.out.println(" Supported format is [{ k:v, k:v, ... }, { ... }, ...]");
css_.out.println(" Valid attributes: column_name, validation_class (see comparator),");
css_.out.println(" index_type (integer), index_name.");
css_.out.println("example:\n");
css_.out.println("create column family Bar with column_type = 'Super' and comparator = 'AsciiType'");
css_.out.println(" and rows_cached = 10000");
css_.out.println("create column family Baz with comparator = 'LongType' and rows_cached = 10000");
css_.out.print("create column family Foo with comparator=LongType and column_metadata=");
css_.out.print("[{ column_name:Test, validation_class:IntegerType, index_type:0, index_name:IdxName");
css_.out.println("}, { column_name:'other name', validation_class:LongType }]");
break;
case CliParser.NODE_UPDATE_COLUMN_FAMILY:
css_.out.println("update column family Bar");
css_.out.println("update column family Bar with <att1>=<value1>");
css_.out.println("update column family Bar with <att1>=<value1> and <att2>=<value2>...\n");
css_.out.println("Update a column family with the specified values for the given set of");
css_.out.println("attributes. Note that you must be using a keyspace.\n");
css_.out.println("valid attributes are:");
for (ColumnFamilyArgument argument : ColumnFamilyArgument.values())
{
if (argument == ColumnFamilyArgument.COMPARATOR || argument == ColumnFamilyArgument.SUBCOMPARATOR)
continue;
css_.out.printf(" - %s: %s\n", argument.toString().toLowerCase(), argumentExplanations.get(argument));
}
css_.out.println(" - column_metadata: Metadata which describes columns of column family.");
css_.out.println(" Supported format is [{ k:v, k:v, ... }, { ... }, ...]");
css_.out.println(" Valid attributes: column_name, validation_class (see comparator),");
css_.out.println(" index_type (integer), index_name.");
css_.out.println("example:\n");
css_.out.print("update column family Foo with column_metadata=");
css_.out.print("[{ column_name:Test, validation_class:IntegerType, index_type:0, index_name:IdxName");
css_.out.println("}] and rows_cached=100 and comment='this is helpful comment.'");
break;
case CliParser.NODE_DEL_KEYSPACE:
css_.out.println("drop keyspace <keyspace>\n");
css_.out.println("Drops the specified keyspace.\n");
css_.out.println("example:");
css_.out.println("drop keyspace foo");
break;
case CliParser.NODE_DEL_COLUMN_FAMILY:
css_.out.println("drop column family <name>\n");
css_.out.println("Drops the specified column family.\n");
css_.out.println("example:");
css_.out.println("drop column family foo");
break;
case CliParser.NODE_THRIFT_GET :
css_.out.println("get <cf>['<key>']");
css_.out.println("get <cf>['<key>']['<col>'] (as <type>)*");
css_.out.println("get <cf>['<key>']['<super>']");
css_.out.println("get <cf> where <column> = <value> [and <column> > <value> and ...] [limit <integer>]");
css_.out.println("Default LIMIT is 100. Available operations: =, >, >=, <, <=\n");
css_.out.println("get <cf>['<key>']['<super>']['<col>'] (as <type>)*");
css_.out.print("Note: `as <type>` is optional, it dynamically converts column value to the specified type");
css_.out.println(", column value validator will be set to <type>.");
css_.out.println("Available types: IntegerType, LongType, UTF8Type, ASCIIType, TimeUUIDType, LexicalUUIDType.\n");
css_.out.println("examples:");
css_.out.println("get bar[testkey]");
css_.out.println("get bar[testkey][test_column] as IntegerType");
break;
case CliParser.NODE_THRIFT_SET:
css_.out.println("set <cf>['<key>']['<col>'] = <value>");
css_.out.println("set <cf>['<key>']['<super>']['<col>'] = <value>");
css_.out.println("set <cf>['<key>']['<col>'] = <function>(<argument>)");
css_.out.println("set <cf>['<key>']['<super>']['<col>'] = <function>(<argument>)");
css_.out.println("Available functions: " + Function.getFunctionNames() + "\n");
css_.out.println("examples:");
css_.out.println("set bar['testkey']['my super']['test col']='this is a test'");
css_.out.println("set baz['testkey']['test col']='this is also a test'");
css_.out.println("set diz[testkey][testcol] = utf8('this is utf8 string.')");
break;
case CliParser.NODE_THRIFT_DEL:
css_.out.println("del <cf>['<key>'] ");
css_.out.println("del <cf>['<key>']['<col>'] ");
css_.out.println("del <cf>['<key>']['<super>']['<col>']\n");
css_.out.println("Deletes a record, a column, or a subcolumn.\n");
css_.out.println("example:");
css_.out.println("del bar['testkey']['my super']['test col']");
css_.out.println("del baz['testkey']['test col']");
css_.out.println("del baz['testkey']");
break;
case CliParser.NODE_THRIFT_COUNT:
css_.out.println("count <cf>['<key>']");
css_.out.println("count <cf>['<key>']['<super>']\n");
css_.out.println("Count the number of columns in the specified key or subcolumns in the specified");
css_.out.println("super column.\n");
css_.out.println("example:");
css_.out.println("count bar['testkey']['my super']");
css_.out.println("count baz['testkey']");
break;
case CliParser.NODE_LIST:
css_.out.println("list <cf>");
css_.out.println("list <cf>[<startKey>:]");
css_.out.println("list <cf>[<startKey>:<endKey>]");
css_.out.println("list ... limit N");
css_.out.println("List a range of rows in the column or supercolumn family.\n");
css_.out.println("example:");
css_.out.println("list Users[j:] limit 40");
break;
default:
css_.out.println("?");
break;
}
}
else
{
css_.out.println("List of all CLI commands:");
css_.out.println("? Display this message.");
css_.out.println("help Display this help.");
css_.out.println("help <command> Display detailed, command-specific help.");
css_.out.println("connect <hostname>/<port> Connect to thrift service.");
css_.out.println("use <keyspace> [<username> 'password'] Switch to a keyspace.");
css_.out.println("describe keyspace <keyspacename> Describe keyspace.");
css_.out.println("exit Exit CLI.");
css_.out.println("quit Exit CLI.");
css_.out.println("show cluster name Display cluster name.");
css_.out.println("show keyspaces Show list of keyspaces.");
css_.out.println("show api version Show server API version.");
css_.out.println("create keyspace <keyspace> [with <att1>=<value1> [and <att2>=<value2> ...]]");
css_.out.println(" Add a new keyspace with the specified attribute(s) and value(s).");
css_.out.println("update keyspace <keyspace> [with <att1>=<value1> [and <att2>=<value2> ...]]");
css_.out.println(" Update a keyspace with the specified attribute(s) and value(s).");
css_.out.println("create column family <cf> [with <att1>=<value1> [and <att2>=<value2> ...]]");
css_.out.println(" Create a new column family with the specified attribute(s) and value(s).");
css_.out.println("update column family <cf> [with <att1>=<value1> [and <att2>=<value2> ...]]");
css_.out.println(" Update a column family with the specified attribute(s) and value(s).");
css_.out.println("drop keyspace <keyspace> Delete a keyspace.");
css_.out.println("drop column family <cf> Delete a column family.");
css_.out.println("rename keyspace <keyspace> <keyspace_new_name> Rename a keyspace.");
css_.out.println("rename column family <cf> <new_name> Rename a column family.");
css_.out.println("get <cf>['<key>'] Get a slice of columns.");
css_.out.println("get <cf>['<key>']['<super>'] Get a slice of sub columns.");
css_.out.println("get <cf> where <column> = <value> [and <column> > <value> and ...] [limit int]. ");
css_.out.println("get <cf>['<key>']['<col>'] (as <type>)* Get a column value.");
css_.out.println("get <cf>['<key>']['<super>']['<col>'] (as <type>)* Get a sub column value.");
css_.out.println("set <cf>['<key>']['<col>'] = <value> Set a column.");
css_.out.println("set <cf>['<key>']['<super>']['<col>'] = <value> Set a sub column.");
css_.out.println("del <cf>['<key>'] Delete record.");
css_.out.println("del <cf>['<key>']['<col>'] Delete column.");
css_.out.println("del <cf>['<key>']['<super>']['<col>'] Delete sub column.");
css_.out.println("count <cf>['<key>'] Count columns in record.");
css_.out.println("count <cf>['<key>']['<super>'] Count columns in a super column.");
css_.out.println("list <cf> List all rows in the column family.");
css_.out.println("list <cf>[<startKey>:]");
css_.out.println(" List rows in the column family beginning with <startKey>.");
css_.out.println("list <cf>[<startKey>:<endKey>]");
css_.out.println(" List rows in the column family in the range from <startKey> to <endKey>.");
css_.out.println("list ... limit N Limit the list results to N.");
}
}
private void cleanupAndExit()
{
CliMain.disconnect();
System.exit(0);
}
KsDef getKSMetaData(String keyspace) throws NotFoundException, InvalidRequestException, TException
{
// Lazily lookup keyspace meta-data.
if (!(keyspacesMap.containsKey(keyspace)))
keyspacesMap.put(keyspace, thriftClient_.describe_keyspace(keyspace));
return keyspacesMap.get(keyspace);
}
private void executeCount(CommonTree ast) throws TException, InvalidRequestException, UnavailableException, TimedOutException
{
if (!CliMain.isConnected() || !hasKeySpace())
return;
int childCount = ast.getChildCount();
assert(childCount == 1);
CommonTree columnFamilySpec = (CommonTree)ast.getChild(0);
assert(columnFamilySpec.getType() == CliParser.NODE_COLUMN_ACCESS);
String key = CliCompiler.getKey(columnFamilySpec);
String columnFamily = CliCompiler.getColumnFamily(columnFamilySpec);
int columnSpecCnt = CliCompiler.numColumnSpecifiers(columnFamilySpec);
ColumnParent colParent;
if (columnSpecCnt == 0)
{
colParent = new ColumnParent(columnFamily).setSuper_column((ByteBuffer)null);
}
else
{
assert (columnSpecCnt == 1);
colParent = new ColumnParent(columnFamily).setSuper_column(CliCompiler.getColumn(columnFamilySpec, 0).getBytes(Charsets.UTF_8));
}
SliceRange range = new SliceRange(FBUtilities.EMPTY_BYTE_BUFFER, FBUtilities.EMPTY_BYTE_BUFFER, false, Integer.MAX_VALUE);
SlicePredicate predicate = new SlicePredicate().setColumn_names(null).setSlice_range(range);
int count = thriftClient_.get_count(ByteBuffer.wrap(key.getBytes(Charsets.UTF_8)), colParent, predicate, ConsistencyLevel.ONE);
css_.out.printf("%d columns\n", count);
}
private void executeDelete(CommonTree ast) throws TException, InvalidRequestException, UnavailableException, TimedOutException
{
if (!CliMain.isConnected() || !hasKeySpace())
return;
int childCount = ast.getChildCount();
assert(childCount == 1);
CommonTree columnFamilySpec = (CommonTree)ast.getChild(0);
assert(columnFamilySpec.getType() == CliParser.NODE_COLUMN_ACCESS);
String key = CliCompiler.getKey(columnFamilySpec);
String columnFamily = CliCompiler.getColumnFamily(columnFamilySpec);
int columnSpecCnt = CliCompiler.numColumnSpecifiers(columnFamilySpec);
byte[] superColumnName = null;
byte[] columnName = null;
boolean isSuper;
List<String> cfnames = new ArrayList<String>();
for (CfDef cfd : keyspacesMap.get(keySpace).cf_defs) {
cfnames.add(cfd.name);
}
int idx = cfnames.indexOf(columnFamily);
if (idx == -1)
{
css_.out.println("No such column family: " + columnFamily);
return;
}
isSuper = keyspacesMap.get(keySpace).cf_defs.get(idx).column_type.equals("Super");
if ((columnSpecCnt < 0) || (columnSpecCnt > 2))
{
css_.out.println("Invalid row, super column, or column specification.");
return;
}
if (columnSpecCnt == 1)
{
// table.cf['key']['column']
if (isSuper)
superColumnName = CliCompiler.getColumn(columnFamilySpec, 0).getBytes(Charsets.UTF_8);
else
columnName = CliCompiler.getColumn(columnFamilySpec, 0).getBytes(Charsets.UTF_8);
}
else if (columnSpecCnt == 2)
{
// table.cf['key']['column']['column']
superColumnName = CliCompiler.getColumn(columnFamilySpec, 0).getBytes(Charsets.UTF_8);
columnName = CliCompiler.getColumn(columnFamilySpec, 1).getBytes(Charsets.UTF_8);
}
ColumnPath path = new ColumnPath(columnFamily);
if(superColumnName != null)
path.setSuper_column(superColumnName);
if(columnName != null)
path.setColumn(columnName);
thriftClient_.remove(ByteBuffer.wrap(key.getBytes(Charsets.UTF_8)), path,
FBUtilities.timestampMicros(), ConsistencyLevel.ONE);
css_.out.println(String.format("%s removed.", (columnSpecCnt == 0) ? "row" : "column"));
}
private void doSlice(String keyspace, String key, String columnFamily, byte[] superColumnName)
throws InvalidRequestException, UnavailableException, TimedOutException, TException, IllegalAccessException, NotFoundException, InstantiationException, NoSuchFieldException
{
ColumnParent parent = new ColumnParent(columnFamily);
if(superColumnName != null)
parent.setSuper_column(superColumnName);
SliceRange range = new SliceRange(FBUtilities.EMPTY_BYTE_BUFFER, FBUtilities.EMPTY_BYTE_BUFFER, true, 1000000);
List<ColumnOrSuperColumn> columns = thriftClient_.get_slice(ByteBuffer.wrap(key.getBytes(Charsets.UTF_8)),parent,
new SlicePredicate().setColumn_names(null).setSlice_range(range), ConsistencyLevel.ONE);
int size = columns.size();
AbstractType validator;
CfDef cfDef = getCfDef(columnFamily);
// Print out super columns or columns.
for (ColumnOrSuperColumn cosc : columns)
{
if (cosc.isSetSuper_column())
{
SuperColumn superColumn = cosc.super_column;
css_.out.printf("=> (super_column=%s,", formatSuperColumnName(keyspace, columnFamily, superColumn));
for (Column col : superColumn.getColumns())
{
validator = getValidatorForValue(cfDef, col.getName());
css_.out.printf("\n (column=%s, value=%s, timestamp=%d)", formatSubcolumnName(keyspace, columnFamily, col),
validator.getString(col.value), col.timestamp);
}
css_.out.println(")");
}
else
{
Column column = cosc.column;
validator = getValidatorForValue(cfDef, column.getName());
css_.out.printf("=> (column=%s, value=%s, timestamp=%d)\n", formatColumnName(keyspace, columnFamily, column),
validator.getString(column.value), column.timestamp);
}
}
css_.out.println("Returned " + size + " results.");
}
private String formatSuperColumnName(String keyspace, String columnFamily, SuperColumn column) throws NotFoundException, TException, IllegalAccessException, InstantiationException, NoSuchFieldException
{
return getFormatTypeForColumn(getCfDef(keyspace,columnFamily).comparator_type).getString(column.name);
}
private String formatSubcolumnName(String keyspace, String columnFamily, Column subcolumn) throws NotFoundException, TException, IllegalAccessException, InstantiationException, NoSuchFieldException
{
return getFormatTypeForColumn(getCfDef(keyspace,columnFamily).subcomparator_type).getString(subcolumn.name);
}
private String formatColumnName(String keyspace, String columnFamily, Column column) throws NotFoundException, TException, IllegalAccessException, InstantiationException, NoSuchFieldException
{
return getFormatTypeForColumn(getCfDef(keyspace,columnFamily).comparator_type).getString(column.name);
}
private AbstractType getFormatTypeForColumn(String compareWith)
{
Function function;
try
{
function = Function.valueOf(compareWith.toUpperCase());
}
catch (IllegalArgumentException e)
{
try
{
return FBUtilities.getComparator(compareWith);
}
catch (ConfigurationException e1)
{
StringBuilder errorMessage = new StringBuilder("Unknown comparator '" + compareWith + "'. ");
errorMessage.append("Available functions: ");
throw new RuntimeException(errorMessage.append(Function.getFunctionNames()).toString());
}
}
return function.validator;
}
// Execute GET statement
private void executeGet(CommonTree ast) throws TException, NotFoundException, InvalidRequestException, UnavailableException, TimedOutException, IllegalAccessException, InstantiationException, ClassNotFoundException, NoSuchFieldException
{
if (!CliMain.isConnected() || !hasKeySpace())
return;
CommonTree columnFamilySpec = (CommonTree) ast.getChild(0);
String key = CliCompiler.getKey(columnFamilySpec);
String columnFamily = CliCompiler.getColumnFamily(columnFamilySpec);
int columnSpecCnt = CliCompiler.numColumnSpecifiers(columnFamilySpec);
CfDef columnFamilyDef = getCfDef(columnFamily);
boolean isSuper = columnFamilyDef.comparator_type.equals("Super");
byte[] superColumnName = null;
String columnName;
// table.cf['key'] -- row slice
if (columnSpecCnt == 0)
{
doSlice(keySpace, key, columnFamily, superColumnName);
return;
}
// table.cf['key']['column'] -- slice of a super, or get of a standard
if (columnSpecCnt == 1)
{
if (isSuper)
{
superColumnName = CliCompiler.getColumn(columnFamilySpec, 0).getBytes(Charsets.UTF_8);
doSlice(keySpace, key, columnFamily, superColumnName);
return;
}
else
{
columnName = CliCompiler.getColumn(columnFamilySpec, 0);
}
}
// table.cf['key']['column']['column'] -- get of a sub-column
else if (columnSpecCnt == 2)
{
superColumnName = CliCompiler.getColumn(columnFamilySpec, 0).getBytes(Charsets.UTF_8);
columnName = CliCompiler.getColumn(columnFamilySpec, 1);
}
// The parser groks an arbitrary number of these so it is possible to get here.
else
{
css_.out.println("Invalid row, super column, or column specification.");
return;
}
ByteBuffer columnNameInBytes = columnNameAsBytes(columnName, columnFamily);
AbstractType validator = getValidatorForValue(columnFamilyDef, columnNameInBytes.array());
// Perform a get()
ColumnPath path = new ColumnPath(columnFamily);
if(superColumnName != null) path.setSuper_column(superColumnName);
if(columnNameInBytes != null) path.setColumn(columnNameInBytes);
Column column = thriftClient_.get(ByteBuffer.wrap(key.getBytes(Charsets.UTF_8)), path, ConsistencyLevel.ONE).column;
byte[] columnValue = column.getValue();
String valueAsString;
// we have ^(CONVERT_TO_TYPE <type>) inside of GET statement
// which means that we should try to represent byte[] value according
// to specified type
if (ast.getChildCount() == 2)
{
// getting ^(CONVERT_TO_TYPE <type>) tree
Tree typeTree = ast.getChild(1).getChild(0);
// .getText() will give us <type>
String typeName = CliUtils.unescapeSQLString(typeTree.getText());
// building AbstractType from <type>
AbstractType valueValidator = getFormatTypeForColumn(typeName);
// setting value for output
valueAsString = valueValidator.getString(ByteBuffer.wrap(columnValue));
// updating column value validator class
updateColumnMetaData(columnFamilyDef, columnNameInBytes, valueValidator.getClass().getName());
}
else
{
valueAsString = (validator == null) ? new String(columnValue, Charsets.UTF_8) : validator.getString(ByteBuffer.wrap(columnValue));
}
// print results
css_.out.printf("=> (column=%s, value=%s, timestamp=%d)\n",
formatColumnName(keySpace, columnFamily, column), valueAsString, column.timestamp);
}
/**
* Process get operation with conditions (using Thrift get_indexed_slices method)
* @param statement - tree representation of the current statement
* Format: ^(NODE_THRIFT_GET_WITH_CONDITIONS cf ^(CONDITIONS ^(CONDITION >= column1 value1) ...) ^(NODE_LIMIT int)*)
*/
private void executeGetWithConditions(Tree statement)
{
if (!CliMain.isConnected() || !hasKeySpace())
return;
IndexClause clause = new IndexClause();
String columnFamily = statement.getChild(0).getText();
// ^(CONDITIONS ^(CONDITION $column $value) ...)
Tree conditions = statement.getChild(1);
// fetching column family definition
CfDef columnFamilyDef = getCfDef(columnFamily);
// fetching all columns
SlicePredicate predicate = new SlicePredicate();
SliceRange sliceRange = new SliceRange();
sliceRange.setStart(new byte[0]).setFinish(new byte[0]);
predicate.setSlice_range(sliceRange);
for (int i = 0; i < conditions.getChildCount(); i++)
{
// ^(CONDITION operator $column $value)
Tree condition = conditions.getChild(i);
// =, >, >=, <, <=
String operator = condition.getChild(0).getText();
String columnNameString = CliUtils.unescapeSQLString(condition.getChild(1).getText());
// it could be a basic string or function call
Tree valueTree = condition.getChild(2);
try
{
ByteBuffer value;
ByteBuffer columnName = columnNameAsBytes(columnNameString, columnFamily);
if (valueTree.getType() == CliParser.FUNCTION_CALL)
{
value = convertValueByFunction(valueTree, columnFamilyDef, columnName);
}
else
{
String valueString = CliUtils.unescapeSQLString(valueTree.getText());
value = columnValueAsBytes(columnName, columnFamily, valueString);
}
// index operator from string
IndexOperator idxOperator = CliUtils.getIndexOperator(operator);
// adding new index expression into index clause
clause.addToExpressions(new IndexExpression(columnName, idxOperator, value));
}
catch (Exception e)
{
throw new RuntimeException(e.getMessage());
}
}
List<KeySlice> slices;
clause.setStart_key(new byte[] {});
// when we have ^(NODE_LIMIT Integer)
if (statement.getChildCount() == 3)
{
Tree limitNode = statement.getChild(2);
int limitValue = Integer.valueOf(limitNode.getChild(0).getText());
if (limitValue == 0)
{
throw new IllegalArgumentException("LIMIT should be greater than zero.");
}
clause.setCount(limitValue);
}
try
{
ColumnParent parent = new ColumnParent(columnFamily);
slices = thriftClient_.get_indexed_slices(parent, clause, predicate, ConsistencyLevel.ONE);
printSliceList(columnFamilyDef, slices);
}
catch (InvalidRequestException e)
{
throw new RuntimeException(e.getWhy());
}
catch (Exception e)
{
throw new RuntimeException(e.getMessage());
}
}
// Execute SET statement
private void executeSet(CommonTree ast)
throws TException, InvalidRequestException, UnavailableException, TimedOutException, NoSuchFieldException, InstantiationException, IllegalAccessException
{
if (!CliMain.isConnected() || !hasKeySpace())
return;
// ^(NODE_COLUMN_ACCESS <cf> <key> <column>)
CommonTree columnFamilySpec = (CommonTree) ast.getChild(0);
String key = CliCompiler.getKey(columnFamilySpec);
String columnFamily = CliCompiler.getColumnFamily(columnFamilySpec);
int columnSpecCnt = CliCompiler.numColumnSpecifiers(columnFamilySpec);
String value = CliUtils.unescapeSQLString(ast.getChild(1).getText());
Tree valueTree = ast.getChild(1);
byte[] superColumnName = null;
String columnName;
// table.cf['key']
if (columnSpecCnt == 0)
{
css_.err.println("No column name specified, (type 'help' or '?' for help on syntax).");
return;
}
// table.cf['key']['column'] = 'value'
else if (columnSpecCnt == 1)
{
// get the column name
columnName = CliCompiler.getColumn(columnFamilySpec, 0);
}
// table.cf['key']['super_column']['column'] = 'value'
else
{
assert (columnSpecCnt == 2) : "serious parsing error (this is a bug).";
// get the super column and column names
superColumnName = CliCompiler.getColumn(columnFamilySpec, 0).getBytes(Charsets.UTF_8);
columnName = CliCompiler.getColumn(columnFamilySpec, 1);
}
ByteBuffer columnNameInBytes = columnNameAsBytes(columnName, columnFamily);
ByteBuffer columnValueInBytes;
switch (valueTree.getType())
{
case CliParser.FUNCTION_CALL:
columnValueInBytes = convertValueByFunction(valueTree, getCfDef(columnFamily), columnNameInBytes, true);
break;
default:
columnValueInBytes = columnValueAsBytes(columnNameInBytes, columnFamily, value);
}
ColumnParent parent = new ColumnParent(columnFamily);
if(superColumnName != null)
parent.setSuper_column(superColumnName);
// do the insert
thriftClient_.insert(ByteBuffer.wrap(key.getBytes(Charsets.UTF_8)), parent,
new Column(columnNameInBytes, columnValueInBytes, FBUtilities.timestampMicros()), ConsistencyLevel.ONE);
css_.out.println("Value inserted.");
}
private void executeShowClusterName() throws TException
{
if (!CliMain.isConnected())
return;
css_.out.println(thriftClient_.describe_cluster_name());
}
/**
* Add a keyspace
* @param statement - a token tree representing current statement
*/
private void executeAddKeyspace(Tree statement)
{
if (!CliMain.isConnected())
{
return;
}
// defaults
List<CfDef> columnList = new LinkedList<CfDef>();
// first value is the keyspace name, after that it is all key=value
String keyspaceName = statement.getChild(0).getText();
KsDef ksDef = new KsDef(keyspaceName, DEFAULT_PLACEMENT_STRATEGY, 1, columnList);
try
{
css_.out.println(thriftClient_.system_add_keyspace(updateKsDefAttributes(statement, ksDef)));
keyspacesMap.put(keyspaceName, thriftClient_.describe_keyspace(keyspaceName));
}
catch (InvalidRequestException e)
{
throw new RuntimeException(e.getWhy());
}
catch (Exception e)
{
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* Add a column family
* @param statement - a token tree representing current statement
*/
private void executeAddColumnFamily(Tree statement)
{
if (!CliMain.isConnected() || !hasKeySpace())
{
return;
}
// first value is the column family name, after that it is all key=value
String columnFamilyName = statement.getChild(0).getText();
CfDef cfDef = new CfDef(keySpace, columnFamilyName);
try
{
css_.out.println(thriftClient_.system_add_column_family(updateCfDefAttributes(statement, cfDef)));
keyspacesMap.put(keySpace, thriftClient_.describe_keyspace(keySpace));
}
catch (InvalidRequestException e)
{
throw new RuntimeException(e.getWhy());
}
catch (Exception e)
{
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* Update existing keyspace identified by name
* @param statement - tree represeting statement
*/
private void executeUpdateKeyspace(Tree statement)
{
if (!CliMain.isConnected())
{
return;
}
String keyspaceName = statement.getChild(0).getText();
try
{
KsDef currentKsDef = getKSMetaData(keyspaceName);
KsDef updatedKsDef = updateKsDefAttributes(statement, currentKsDef);
css_.out.println(thriftClient_.system_update_keyspace(updatedKsDef));
keyspacesMap.put(keyspaceName, thriftClient_.describe_keyspace(keyspaceName));
}
catch (InvalidRequestException e)
{
throw new RuntimeException(e.getWhy());
}
catch (Exception e)
{
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* Update existing column family identified by name
* @param statement - tree represeting statement
*/
private void executeUpdateColumnFamily(Tree statement)
{
if (!CliMain.isConnected() || !hasKeySpace())
{
return;
}
String columnFamilyName = statement.getChild(0).getText();
CfDef cfDef = getCfDef(columnFamilyName);
try
{
css_.out.println(thriftClient_.system_update_column_family(updateCfDefAttributes(statement, cfDef)));
keyspacesMap.put(keySpace, thriftClient_.describe_keyspace(keySpace));
}
catch (InvalidRequestException e)
{
throw new RuntimeException(e.getWhy());
}
catch (Exception e)
{
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* Used to update keyspace definition attributes
* @param statement - ANTRL tree representing current statement
* @param ksDefToUpdate - keyspace definition to update
* @return ksDef - updated keyspace definition
*/
private KsDef updateKsDefAttributes(Tree statement, KsDef ksDefToUpdate)
{
KsDef ksDef = new KsDef(ksDefToUpdate);
// removing all column definitions - thrift system_update_keyspace method requires that
ksDef.setCf_defs(new LinkedList<CfDef>());
for(int i = 1; i < statement.getChildCount(); i += 2)
{
String currentStatement = statement.getChild(i).getText().toUpperCase();
AddKeyspaceArgument mArgument = AddKeyspaceArgument.valueOf(currentStatement);
String mValue = statement.getChild(i + 1).getText();
switch(mArgument)
{
case PLACEMENT_STRATEGY:
ksDef.setStrategy_class(CliUtils.unescapeSQLString(mValue));
break;
case REPLICATION_FACTOR:
ksDef.setReplication_factor(Integer.parseInt(mValue));
break;
case STRATEGY_OPTIONS:
ksDef.setStrategy_options(getStrategyOptionsFromTree(statement.getChild(i + 1)));
break;
default:
//must match one of the above or we'd throw an exception at the valueOf statement above.
assert(false);
}
}
return ksDef;
}
/**
* Update column family definition attributes
* @param statement - ANTLR tree representing current statement
* @param cfDefToUpdate - column family definition to apply updates on
* @return cfDef - updated column family definition
*/
private CfDef updateCfDefAttributes(Tree statement, CfDef cfDefToUpdate)
{
CfDef cfDef = new CfDef(cfDefToUpdate);
for (int i = 1; i < statement.getChildCount(); i += 2)
{
String currentArgument = statement.getChild(i).getText().toUpperCase();
ColumnFamilyArgument mArgument = ColumnFamilyArgument.valueOf(currentArgument);
String mValue = statement.getChild(i + 1).getText();
switch(mArgument)
{
case COLUMN_TYPE:
cfDef.setColumn_type(CliUtils.unescapeSQLString(mValue));
break;
case COMPARATOR:
cfDef.setComparator_type(CliUtils.unescapeSQLString(mValue));
break;
case SUBCOMPARATOR:
cfDef.setSubcomparator_type(CliUtils.unescapeSQLString(mValue));
break;
case COMMENT:
cfDef.setComment(CliUtils.unescapeSQLString(mValue));
break;
case ROWS_CACHED:
cfDef.setRow_cache_size(Double.parseDouble(mValue));
break;
case KEYS_CACHED:
cfDef.setKey_cache_size(Double.parseDouble(mValue));
break;
case READ_REPAIR_CHANCE:
cfDef.setRead_repair_chance(Double.parseDouble(mValue));
break;
case GC_GRACE:
cfDef.setGc_grace_seconds(Integer.parseInt(mValue));
break;
case COLUMN_METADATA:
Tree arrayOfMetaAttributes = statement.getChild(i + 1);
if (!arrayOfMetaAttributes.getText().equals("ARRAY"))
throw new RuntimeException("'column_metadata' format - [{ k:v, k:v, ..}, { ... }, ...]");
cfDef.setColumn_metadata(getCFColumnMetaFromTree(arrayOfMetaAttributes));
break;
case MEMTABLE_OPERATIONS:
cfDef.setMemtable_operations_in_millions(Double.parseDouble(mValue));
break;
case MEMTABLE_FLUSH_AFTER:
cfDef.setMemtable_flush_after_mins(Integer.parseInt(mValue));
break;
case MEMTABLE_THROUGHPUT:
cfDef.setMemtable_throughput_in_mb(Integer.parseInt(mValue));
break;
case ROW_CACHE_SAVE_PERIOD:
cfDef.setRow_cache_save_period_in_seconds(Integer.parseInt(mValue));
break;
case KEY_CACHE_SAVE_PERIOD:
cfDef.setKey_cache_save_period_in_seconds(Integer.parseInt(mValue));
break;
case DEFAULT_VALIDATION_CLASS:
cfDef.setDefault_validation_class(mValue);
break;
case MIN_COMPACTION_THRESHOLD:
cfDef.setMin_compaction_threshold(Integer.parseInt(mValue));
break;
case MAX_COMPACTION_THRESHOLD:
cfDef.setMax_compaction_threshold(Integer.parseInt(mValue));
break;
default:
//must match one of the above or we'd throw an exception at the valueOf statement above.
assert(false);
}
}
return cfDef;
}
/**
* Delete a keyspace
* @param ast - a token tree representing current statement
* @throws TException - exception
* @throws InvalidRequestException - exception
* @throws NotFoundException - exception
*/
private void executeDelKeyspace(CommonTree ast) throws TException, InvalidRequestException, NotFoundException
{
if (!CliMain.isConnected())
{
return;
}
String keyspaceName = ast.getChild(0).getText();
css_.out.println(thriftClient_.system_drop_keyspace(keyspaceName));
}
/**
* Delete a column family
* @param ast - a token tree representing current statement
* @throws TException - exception
* @throws InvalidRequestException - exception
* @throws NotFoundException - exception
*/
private void executeDelColumnFamily(CommonTree ast) throws TException, InvalidRequestException, NotFoundException
{
if (!CliMain.isConnected() || !hasKeySpace())
{
return;
}
String columnName = ast.getChild(0).getText();
css_.out.println(thriftClient_.system_drop_column_family(columnName));
}
private void executeList(CommonTree ast)
throws TException, InvalidRequestException, NotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException, UnavailableException, TimedOutException
{
if (!CliMain.isConnected())
return;
Iterator<CommonTree> iter = ast.getChildren().iterator();
// extract column family
String columnFamily = iter.next().getText();
String startKey = "";
String endKey = "";
int limitCount = Integer.MAX_VALUE; // will reset to default later if it's not specified
// optional arguments: key range and limit
while (iter.hasNext())
{
CommonTree child = iter.next();
if (child.getType() == CliParser.NODE_KEY_RANGE)
{
if (child.getChildCount() > 0)
{
startKey = CliUtils.unescapeSQLString(child.getChild(0).getText());
if (child.getChildCount() > 1)
endKey = CliUtils.unescapeSQLString(child.getChild(1).getText());
}
}
else
{
if (child.getChildCount() != 1)
{
css_.out.println("Invalid limit clause");
return;
}
limitCount = Integer.parseInt(child.getChild(0).getText());
if (limitCount <= 0)
{
css_.out.println("Invalid limit " + limitCount);
return;
}
}
}
if (limitCount == Integer.MAX_VALUE)
{
limitCount = 100;
css_.out.println("Using default limit of 100");
}
CfDef columnFamilyDef = getCfDef(columnFamily);
// read all columns and superColumns
SlicePredicate predicate = new SlicePredicate();
SliceRange sliceRange = new SliceRange();
sliceRange.setStart(new byte[0]).setFinish(new byte[0]);
predicate.setSlice_range(sliceRange);
// set the key range
KeyRange range = new KeyRange(limitCount);
range.setStart_key(startKey.getBytes()).setEnd_key(endKey.getBytes());
ColumnParent columnParent = new ColumnParent(columnFamily);
List<KeySlice> keySlices = thriftClient_.get_range_slices(columnParent, predicate, range, ConsistencyLevel.ONE);
int toIndex = keySlices.size();
if (limitCount < keySlices.size())
{
// limitCount could be Integer.MAX_VALUE
toIndex = limitCount;
}
printSliceList(columnFamilyDef, keySlices.subList(0, toIndex));
}
private void executeShowVersion() throws TException
{
if (!CliMain.isConnected())
return;
css_.out.println(thriftClient_.describe_version());
}
// process "show tables" statement
private void executeShowTables() throws TException, InvalidRequestException
{
if (!CliMain.isConnected())
return;
List<KsDef> tables = thriftClient_.describe_keyspaces();
for (KsDef t : tables) {
describeTableInternal(t.name, t);
}
}
private boolean hasKeySpace()
{
if (keySpace == null)
{
css_.out.println("Not authenticated to a working keyspace.");
return false;
}
return true;
}
public String getKeySpace()
{
return keySpace == null ? "unknown" : keySpace;
}
public void setKeyspace(String keySpace) throws NotFoundException, InvalidRequestException, TException
{
this.keySpace = keySpace;
//We do nothing with the return value, but it hits a cache and
// the tab-completer.
getKSMetaData(keySpace);
}
public String getUsername()
{
return username == null ? "default" : username;
}
public void setUsername(String username)
{
this.username = username;
}
private void executeUseTable(CommonTree ast) throws TException
{
if (!CliMain.isConnected())
return;
int childCount = ast.getChildCount();
String tableName, username = null, password = null;
assert(childCount > 0);
// Get table name
tableName = ast.getChild(0).getText();
if (childCount == 3) {
username = ast.getChild(1).getText();
password = ast.getChild(2).getText();
}
if( tableName == null )
{
css_.out.println("Keyspace argument required");
return;
}
try
{
AuthenticationRequest authRequest;
Map<String, String> credentials = new HashMap<String, String>();
thriftClient_.set_keyspace(tableName);
if (username != null && password != null)
{
/* remove quotes */
password = password.replace("\'", "");
credentials.put(SimpleAuthenticator.USERNAME_KEY, username);
credentials.put(SimpleAuthenticator.PASSWORD_KEY, password);
authRequest = new AuthenticationRequest(credentials);
thriftClient_.login(authRequest);
}
keySpace = tableName;
this.username = username != null ? username : "default";
if (!(keyspacesMap.containsKey(keySpace)))
{
keyspacesMap.put(keySpace, thriftClient_.describe_keyspace(keySpace));
}
Set<String> cfnames = new HashSet<String>();
KsDef ksd = keyspacesMap.get(keySpace);
for (CfDef cfd : ksd.cf_defs) {
cfnames.add(cfd.name);
}
CliMain.updateCompletor(cfnames);
css_.out.println("Authenticated to keyspace: " + keySpace);
}
catch (AuthenticationException e)
{
css_.err.println("Exception during authentication to the cassandra node: " +
"verify keyspace exists, and you are using correct credentials.");
}
catch (AuthorizationException e)
{
css_.err.println("You are not authorized to use keyspace: " + tableName);
}
catch (InvalidRequestException e)
{
css_.err.println(tableName + " does not exist.");
}
catch (NotFoundException e)
{
css_.err.println(tableName + " does not exist.");
}
catch (TException e)
{
if (css_.debug)
e.printStackTrace();
css_.err.println("Login failure. Did you specify 'keyspace', 'username' and 'password'?");
}
}
private void describeTableInternal(String tableName, KsDef metadata) throws TException {
// Describe and display
css_.out.println("Keyspace: " + tableName + ":");
try
{
KsDef ks_def;
ks_def = metadata == null ? thriftClient_.describe_keyspace(tableName) : metadata;
css_.out.println(" Replication Factor: " + ks_def.replication_factor);
css_.out.println(" Column Families:");
for (CfDef cf_def : ks_def.cf_defs)
{
css_.out.printf(" ColumnFamily: %s%s\n", cf_def.name, cf_def.column_type.equals("Super") ? " (Super)" : "");
if (cf_def.comment != null && !cf_def.comment.isEmpty())
css_.out.printf(" \"%s\"\n", cf_def.comment);
css_.out.printf(" Columns sorted by: %s%s\n", cf_def.comparator_type, cf_def.column_type.equals("Super") ? "/" + cf_def.subcomparator_type : "");
if (cf_def.subcomparator_type != null)
css_.out.println(" Subcolumns sorted by: " + cf_def.comparator_type);
css_.out.printf(" Row cache size / save period: %s/%s\n", cf_def.row_cache_size, cf_def.row_cache_save_period_in_seconds);
css_.out.printf(" Key cache size / save period: %s/%s\n", cf_def.key_cache_size, cf_def.key_cache_save_period_in_seconds);
css_.out.printf(" Memtable thresholds: %s/%s/%s\n",
cf_def.memtable_operations_in_millions, cf_def.memtable_throughput_in_mb, cf_def.memtable_flush_after_mins);
css_.out.printf(" GC grace seconds: %s\n", cf_def.gc_grace_seconds);
css_.out.printf(" Compaction min/max thresholds: %s/%s\n", cf_def.min_compaction_threshold, cf_def.max_compaction_threshold);
if (cf_def.getColumn_metadataSize() != 0)
{
String leftSpace = " ";
String columnLeftSpace = leftSpace + " ";
AbstractType columnNameValidator = getFormatTypeForColumn(cf_def.comparator_type);
css_.out.println(leftSpace + "Column Metadata:");
for (ColumnDef columnDef : cf_def.getColumn_metadata())
{
String columnName = columnNameValidator.getString(columnDef.name);
css_.out.println(leftSpace + " Column Name: " + columnName);
css_.out.println(columnLeftSpace + "Validation Class: " + columnDef.getValidation_class());
if (columnDef.isSetIndex_name())
{
css_.out.println(columnLeftSpace + "Index Name: " + columnDef.getIndex_name());
}
if (columnDef.isSetIndex_type())
{
css_.out.println(columnLeftSpace + "Index Type: " + columnDef.getIndex_type().name());
}
}
}
}
}
catch (InvalidRequestException e)
{
css_.out.println("Invalid request: " + e);
}
catch (NotFoundException e)
{
css_.out.println("Keyspace " + tableName + " could not be found.");
}
}
// process a statement of the form: describe table <tablename>
private void executeDescribeTable(CommonTree ast) throws TException
{
if (!CliMain.isConnected())
return;
// Get table name
int childCount = ast.getChildCount();
assert(childCount == 1);
String tableName = ast.getChild(0).getText();
if( tableName == null ) {
css_.out.println("Keyspace argument required");
return;
}
describeTableInternal(tableName, null);
}
// process a statement of the form: connect hostname/port
private void executeConnect(CommonTree ast)
{
int portNumber = Integer.parseInt(ast.getChild(1).getText());
Tree idList = ast.getChild(0);
StringBuilder hostName = new StringBuilder();
int idCount = idList.getChildCount();
for (int idx = 0; idx < idCount; idx++)
{
hostName.append(idList.getChild(idx).getText());
}
// disconnect current connection, if any.
// This is a no-op, if you aren't currently connected.
CliMain.disconnect();
// now, connect to the newly specified host name and port
css_.hostName = hostName.toString();
css_.thriftPort = portNumber;
CliMain.connect(css_.hostName, css_.thriftPort);
}
/**
* To get Column Family Definition object from specified keyspace
* @param keySpaceName key space name to search for specific column family
* @param columnFamilyName column family name
* @return CfDef - Column family definition object
*/
private CfDef getCfDef(String keySpaceName, String columnFamilyName)
{
KsDef keySpaceDefinition = keyspacesMap.get(keySpaceName);
for (CfDef columnFamilyDef : keySpaceDefinition.cf_defs)
{
if (columnFamilyDef.name.equals(columnFamilyName))
{
return columnFamilyDef;
}
}
throw new RuntimeException("No such column family: " + columnFamilyName);
}
/**
* Uses getCfDef(keySpaceName, columnFamilyName) with current keyspace
* @param columnFamilyName column family name to find in specified keyspace
* @return CfDef - Column family definition object
*/
private CfDef getCfDef(String columnFamilyName)
{
return getCfDef(this.keySpace, columnFamilyName);
}
/**
* Used to parse meta tree and compile meta attributes into List<ColumnDef>
* @param meta (Tree representing Array of the hashes with metadata attributes)
* @return List<ColumnDef> List of the ColumnDef's
*
* meta is in following format - ^(ARRAY ^(HASH ^(PAIR .. ..) ^(PAIR .. ..)) ^(HASH ...))
*/
private List<ColumnDef> getCFColumnMetaFromTree(Tree meta)
{
// this list will be returned
List<ColumnDef> columnDefinitions = new ArrayList<ColumnDef>();
// each child node is a ^(HASH ...)
for (int i = 0; i < meta.getChildCount(); i++)
{
Tree metaHash = meta.getChild(i);
ColumnDef columnDefinition = new ColumnDef();
// each child node is ^(PAIR $key $value)
for (int j = 0; j < metaHash.getChildCount(); j++)
{
Tree metaPair = metaHash.getChild(j);
// current $key
String metaKey = CliUtils.unescapeSQLString(metaPair.getChild(0).getText());
// current $value
String metaVal = CliUtils.unescapeSQLString(metaPair.getChild(1).getText());
if (metaKey.equals("column_name"))
{
columnDefinition.setName(metaVal.getBytes(Charsets.UTF_8));
}
else if (metaKey.equals("validation_class"))
{
columnDefinition.setValidation_class(metaVal);
}
else if (metaKey.equals("index_type"))
{
columnDefinition.setIndex_type(getIndexTypeFromString(metaVal));
}
else if (metaKey.equals("index_name"))
{
columnDefinition.setIndex_name(metaVal);
}
}
// validating columnDef structure, 'name' and 'validation_class' must be set
try
{
columnDefinition.validate();
}
catch (TException e)
{
throw new RuntimeException(e.getMessage(), e);
}
columnDefinitions.add(columnDefinition);
}
return columnDefinitions;
}
/**
* Getting IndexType object from indexType string
* @param indexTypeAsString - string return by parser corresponding to IndexType
* @return IndexType - an IndexType object
*/
private IndexType getIndexTypeFromString(String indexTypeAsString)
{
Integer indexTypeId;
IndexType indexType;
try {
indexTypeId = new Integer(indexTypeAsString);
}
catch (NumberFormatException e) {
throw new RuntimeException("Could not convert " + indexTypeAsString + " into Integer.");
}
indexType = IndexType.findByValue(indexTypeId);
if (indexType == null) {
throw new RuntimeException(indexTypeAsString + " is unsupported.");
}
return indexType;
}
/**
* Converts object represented as string into byte[] according to comparator
* @param object - object to covert into byte array
* @param comparator - comparator used to convert object
* @return byte[] - object in the byte array representation
*/
private ByteBuffer getBytesAccordingToType(String object, AbstractType comparator)
{
if (comparator instanceof LongType)
{
long longType;
try
{
longType = Long.valueOf(object);
}
catch (Exception e)
{
throw new RuntimeException("'" + object + "' could not be translated into a LongType.");
}
return FBUtilities.toByteBuffer(longType);
}
else if (comparator instanceof LexicalUUIDType || comparator instanceof TimeUUIDType)
{
UUID uuid = UUID.fromString(object);
if (comparator instanceof TimeUUIDType && uuid.version() != 1)
throw new IllegalArgumentException("TimeUUID supports only version 1 UUIDs");
return ByteBuffer.wrap(UUIDGen.decompose(uuid));
}
else if (comparator instanceof IntegerType)
{
BigInteger integerType;
try
{
integerType = new BigInteger(object);
}
catch (Exception e)
{
throw new RuntimeException("'" + object + "' could not be translated into an IntegerType.");
}
return ByteBuffer.wrap(integerType.toByteArray());
}
else if (comparator instanceof AsciiType)
{
return ByteBuffer.wrap(object.getBytes(Charsets.US_ASCII));
}
else
{
return ByteBuffer.wrap(object.getBytes(Charsets.UTF_8));
}
}
/**
* Converts column name into byte[] according to comparator type
* @param column - column name from parser
* @param columnFamily - column family name from parser
* @return byte[] - array of bytes in which column name was converted according to comparator type
* @throws NoSuchFieldException - raised from getFormatTypeForColumn call
* @throws InstantiationException - raised from getFormatTypeForColumn call
* @throws IllegalAccessException - raised from getFormatTypeForColumn call
*/
private ByteBuffer columnNameAsBytes(String column, String columnFamily) throws NoSuchFieldException, InstantiationException, IllegalAccessException
{
CfDef columnFamilyDef = getCfDef(columnFamily);
String comparatorClass = columnFamilyDef.comparator_type;
return getBytesAccordingToType(column, getFormatTypeForColumn(comparatorClass));
}
/**
* Converts column value into byte[] according to validation class
* @param columnName - column name to which value belongs
* @param columnFamilyName - column family name
* @param columnValue - actual column value
* @return byte[] - value in byte array representation
*/
private ByteBuffer columnValueAsBytes(ByteBuffer columnName, String columnFamilyName, String columnValue)
{
CfDef columnFamilyDef = getCfDef(columnFamilyName);
for (ColumnDef columnDefinition : columnFamilyDef.getColumn_metadata())
{
byte[] currentColumnName = columnDefinition.getName();
if (ByteBufferUtil.compare(currentColumnName,columnName)==0)
{
try
{
String validationClass = columnDefinition.getValidation_class();
return getBytesAccordingToType(columnValue, getFormatTypeForColumn(validationClass));
}
catch (Exception e)
{
throw new RuntimeException(e.getMessage(), e);
}
}
}
// if no validation were set returning simple .getBytes()
return ByteBuffer.wrap(columnValue.getBytes());
}
/**
* Get validator for specific column value
* @param ColumnFamilyDef - CfDef object representing column family with metadata
* @param columnNameInBytes - column name as byte array
* @return AbstractType - validator for column value
*/
private AbstractType getValidatorForValue(CfDef ColumnFamilyDef, byte[] columnNameInBytes)
{
String defaultValidator = ColumnFamilyDef.default_validation_class;
for (ColumnDef columnDefinition : ColumnFamilyDef.getColumn_metadata())
{
byte[] nameInBytes = columnDefinition.getName();
if (nameInBytes.equals(columnNameInBytes))
{
return getFormatTypeForColumn(columnDefinition.getValidation_class());
}
}
if (defaultValidator != null && !defaultValidator.isEmpty())
{
return getFormatTypeForColumn(defaultValidator);
}
return null;
}
/**
* Used to get Map of the provided options by create/update keyspace commands
* @param options - tree representing options
* @return Map - strategy_options map
*/
private Map<String, String> getStrategyOptionsFromTree(Tree options)
{
// this map will be returned
Map<String, String> strategyOptions = new HashMap<String, String>();
// each child node is a ^(HASH ...)
for (int i = 0; i < options.getChildCount(); i++)
{
Tree optionsHash = options.getChild(i);
// each child node is ^(PAIR $key $value)
for (int j = 0; j < optionsHash.getChildCount(); j++)
{
Tree optionPair = optionsHash.getChild(j);
// current $key
String key = CliUtils.unescapeSQLString(optionPair.getChild(0).getText());
// current $value
String val = CliUtils.unescapeSQLString(optionPair.getChild(1).getText());
strategyOptions.put(key, val);
}
}
return strategyOptions;
}
/**
* Used to convert value (function argument, string) into byte[]
* calls convertValueByFunction method with "withUpdate" set to false
* @param functionCall - tree representing function call ^(FUNCTION_CALL function_name value)
* @param columnFamily - column family definition (CfDef)
* @param columnName - also updates column family metadata for given column
* @return byte[] - string value as byte[]
*/
private ByteBuffer convertValueByFunction(Tree functionCall, CfDef columnFamily, ByteBuffer columnName)
{
return convertValueByFunction(functionCall, columnFamily, columnName, false);
}
/**
* Used to convert value (function argument, string) into byte[]
* @param functionCall - tree representing function call ^(FUNCTION_CALL function_name value)
* @param columnFamily - column family definition (CfDef)
* @param columnName - column name as byte[] (used to update CfDef)
* @param withUpdate - also updates column family metadata for given column
* @return byte[] - string value as byte[]
*/
private ByteBuffer convertValueByFunction(Tree functionCall, CfDef columnFamily, ByteBuffer columnName, boolean withUpdate)
{
String functionName = functionCall.getChild(0).getText();
String functionArg = CliUtils.unescapeSQLString(functionCall.getChild(1).getText());
Function function;
try
{
function = Function.valueOf(functionName.toUpperCase());
}
catch (IllegalArgumentException e)
{
StringBuilder errorMessage = new StringBuilder("Function '" + functionName + "' not found. ");
errorMessage.append("Available functions: ");
throw new RuntimeException(errorMessage.append(Function.getFunctionNames()).toString());
}
try
{
AbstractType validator = function.getValidator();
ByteBuffer value = getBytesAccordingToType(functionArg, validator);
// performing ColumnDef local validator update
if (withUpdate)
{
updateColumnMetaData(columnFamily, columnName, validator.getClass().getName());
}
return value;
}
catch (Exception e)
{
throw new RuntimeException(e.getMessage());
}
}
/**
* Used to locally update column family definition with new column metadata
* @param columnFamily - CfDef record
* @param columnName - column name represented as byte[]
* @param validationClass - value validation class
*/
private void updateColumnMetaData(CfDef columnFamily, ByteBuffer columnName, String validationClass)
{
List<ColumnDef> columnMetaData = columnFamily.getColumn_metadata();
ColumnDef column = getColumnDefByName(columnFamily, columnName);
if (column != null)
{
// if validation class is the same - no need to modify it
if (column.getValidation_class().equals(validationClass))
return;
// updating column definition with new validation_class
column.setValidation_class(validationClass);
}
else
{
columnMetaData.add(new ColumnDef(columnName, validationClass));
}
}
/**
* Get specific ColumnDef in column family meta data by column name
* @param columnFamily - CfDef record
* @param columnName - column name represented as byte[]
* @return ColumnDef - found column definition
*/
private ColumnDef getColumnDefByName(CfDef columnFamily, ByteBuffer columnName)
{
for (ColumnDef columnDef : columnFamily.getColumn_metadata())
{
byte[] currName = columnDef.getName();
if (ByteBufferUtil.compare(currName, columnName) == 0)
{
return columnDef;
}
}
return null;
}
/**
* Prints out KeySlice list
* @param columnFamilyDef - column family definition
* @param slices - list of the KeySlice's to print out
* @throws NotFoundException - column not found
* @throws TException - transfer is broken
* @throws IllegalAccessException - can't do operation
* @throws InstantiationException - can't instantiate a class
* @throws NoSuchFieldException - column not found
*/
private void printSliceList(CfDef columnFamilyDef, List<KeySlice> slices)
throws NotFoundException, TException, IllegalAccessException, InstantiationException, NoSuchFieldException
{
AbstractType validator;
String columnFamilyName = columnFamilyDef.getName();
for (KeySlice ks : slices)
{
css_.out.printf("-------------------\n");
css_.out.printf("RowKey: %s\n", new String(ks.key.array(),ks.key.position(),ks.key.remaining(), Charsets.UTF_8));
Iterator<ColumnOrSuperColumn> iterator = ks.getColumnsIterator();
while (iterator.hasNext())
{
ColumnOrSuperColumn columnOrSuperColumn = iterator.next();
if (columnOrSuperColumn.column != null)
{
Column col = columnOrSuperColumn.column;
validator = getValidatorForValue(columnFamilyDef, col.getName());
css_.out.printf("=> (column=%s, value=%s, timestamp=%d)\n",
formatColumnName(keySpace, columnFamilyName, col), validator.getString(col.value), col.timestamp);
}
else if (columnOrSuperColumn.super_column != null)
{
SuperColumn superCol = columnOrSuperColumn.super_column;
css_.out.printf("=> (super_column=%s,", formatSuperColumnName(keySpace, columnFamilyName, superCol));
for (Column col : superCol.columns)
{
validator = getValidatorForValue(columnFamilyDef, col.getName());
css_.out.printf("\n (column=%s, value=%s, timestamp=%d)",
formatSubcolumnName(keySpace, columnFamilyName, col), validator.getString(col.value), col.timestamp);
}
css_.out.println(")");
}
}
}
css_.out.printf("\n%d Row%s Returned.\n", slices.size(), (slices.size() > 1 ? "s" : ""));
}
}