Merge branch 'cassandra-2.1' into trunk

Conflicts:
	bin/cqlsh
This commit is contained in:
Tyler Hobbs 2015-04-21 12:24:47 -05:00
commit 840d9a6632
1 changed files with 60 additions and 28 deletions

View File

@ -36,7 +36,7 @@ description = "CQL Shell for Apache Cassandra"
version = "5.0.1"
from StringIO import StringIO
from contextlib import contextmanager, closing
from contextlib import contextmanager
from glob import glob
import cmd
@ -79,6 +79,7 @@ if myplatform == 'Linux':
if os.environ.get('CQLSH_NO_BUNDLED', ''):
ZIPLIB_DIRS = ()
def find_zip(libprefix):
for ziplibdir in ZIPLIB_DIRS:
zips = glob(os.path.join(ziplibdir, libprefix + '*.zip'))
@ -111,7 +112,6 @@ from cassandra.cluster import Cluster, PagedResult
from cassandra.query import SimpleStatement, ordered_dict_factory
from cassandra.policies import WhiteListRoundRobinPolicy
from cassandra.protocol import QueryMessage, ResultMessage
from cassandra.marshal import int16_pack, int32_pack, uint64_pack
from cassandra.metadata import protect_name, protect_names, protect_value, KeyspaceMetadata, TableMetadata, ColumnMetadata
from cassandra.auth import PlainTextAuthProvider
@ -145,6 +145,9 @@ if readline is not None and readline.__doc__ is not None and 'libedit' in readli
else:
DEFAULT_COMPLETEKEY = 'tab'
cqldocs = None
cqlruleset = None
epilog = """Connects to %(DEFAULT_HOST)s:%(DEFAULT_PORT)d by default. These
defaults can be changed by setting $CQLSH_HOST and/or $CQLSH_PORT. When a
host (and optional port number) are given on the command line, they take
@ -227,13 +230,17 @@ my_commands_ending_with_newline = (
'quit'
)
cqlsh_syntax_completers = []
def cqlsh_syntax_completer(rulename, termname):
def registrator(f):
cqlsh_syntax_completers.append((rulename, termname, f))
return f
return registrator
cqlsh_extra_syntax_rules = r'''
<cqlshCommand> ::= <CQL_Statement>
| <specialCommand> ( ";" | "\n" )
@ -325,13 +332,15 @@ cqlsh_extra_syntax_rules = r'''
<qmark> ::= "?" ;
'''
@cqlsh_syntax_completer('helpCommand', 'topic')
def complete_help(ctxt, cqlsh):
return sorted([ t.upper() for t in cqldocs.get_help_topics() + cqlsh.get_help_topics() ])
return sorted([t.upper() for t in cqldocs.get_help_topics() + cqlsh.get_help_topics()])
def complete_source_quoted_filename(ctxt, cqlsh):
partial = ctxt.get_binding('partial', '')
head, tail = os.path.split(partial)
partial_path = ctxt.get_binding('partial', '')
head, tail = os.path.split(partial_path)
exhead = os.path.expanduser(head)
try:
contents = os.listdir(exhead or '.')
@ -346,21 +355,22 @@ def complete_source_quoted_filename(ctxt, cqlsh):
annotated.append(match)
return annotated
cqlsh_syntax_completer('sourceCommand', 'fname') \
(complete_source_quoted_filename)
cqlsh_syntax_completer('captureCommand', 'fname') \
(complete_source_quoted_filename)
cqlsh_syntax_completer('sourceCommand', 'fname')(complete_source_quoted_filename)
cqlsh_syntax_completer('captureCommand', 'fname')(complete_source_quoted_filename)
@cqlsh_syntax_completer('copyCommand', 'fname')
def copy_fname_completer(ctxt, cqlsh):
lasttype = ctxt.get_binding('*LASTTYPE*')
if lasttype == 'unclosedString':
return complete_source_quoted_filename(ctxt, cqlsh)
partial = ctxt.get_binding('partial')
if partial == '':
partial_path = ctxt.get_binding('partial')
if partial_path == '':
return ["'"]
return ()
@cqlsh_syntax_completer('copyCommand', 'colnames')
def complete_copy_column_names(ctxt, cqlsh):
existcols = map(cqlsh.cql_unprotect_name, ctxt.get_binding('colnames', ()))
@ -371,8 +381,10 @@ def complete_copy_column_names(ctxt, cqlsh):
return [colnames[0]]
return set(colnames[1:]) - set(existcols)
COPY_OPTIONS = ('DELIMITER', 'QUOTE', 'ESCAPE', 'HEADER', 'ENCODING', 'NULL')
@cqlsh_syntax_completer('copyOption', 'optnames')
def complete_copy_options(ctxt, cqlsh):
optnames = map(str.upper, ctxt.get_binding('optnames', ()))
@ -382,6 +394,7 @@ def complete_copy_options(ctxt, cqlsh):
opts -= ('ENCODING',)
return opts
@cqlsh_syntax_completer('copyOption', 'optvals')
def complete_copy_opt_values(ctxt, cqlsh):
optnames = ctxt.get_binding('optnames', ())
@ -390,21 +403,27 @@ def complete_copy_opt_values(ctxt, cqlsh):
return ['true', 'false']
return [cqlhandling.Hint('<single_character_string>')]
class NoKeyspaceError(Exception):
pass
class KeyspaceNotFound(Exception):
pass
class ColumnFamilyNotFound(Exception):
pass
class VersionNotSupported(Exception):
pass
class UserTypeNotFound(Exception):
pass
class DecodeError(Exception):
verb = 'decode'
@ -426,9 +445,11 @@ class DecodeError(Exception):
def __repr__(self):
return '<%s %s>' % (self.__class__.__name__, self.message())
class FormatError(DecodeError):
verb = 'format'
def full_cql_version(ver):
while ver.count('.') < 2:
ver += '.0'
@ -436,6 +457,7 @@ def full_cql_version(ver):
vertuple = tuple(map(int, ver_parts[0].split('.')) + [ver_parts[1]])
return ver, vertuple
def format_value(val, output_encoding, addcolor=False, date_time_format=None,
float_precision=None, colormap=None, nullval=None):
if isinstance(val, DecodeError):
@ -447,6 +469,7 @@ def format_value(val, output_encoding, addcolor=False, date_time_format=None,
addcolor=addcolor, nullval=nullval, date_time_format=date_time_format,
float_precision=float_precision)
def show_warning_without_quoting_line(message, category, filename, lineno, file=None, line=None):
if file is None:
file = sys.stderr
@ -457,6 +480,7 @@ def show_warning_without_quoting_line(message, category, filename, lineno, file=
warnings.showwarning = show_warning_without_quoting_line
warnings.filterwarnings('always', category=cql3handling.UnexpectedTableStructure)
def describe_interval(seconds):
desc = []
for length, unit in ((86400, 'day'), (3600, 'hour'), (60, 'minute')):
@ -477,6 +501,7 @@ def describe_interval(seconds):
def auto_format_udts():
# when we see a new user defined type, set up the shell formatting for it
udt_apply_params = cassandra.cqltypes.UserType.apply_parameters
def new_apply_params(cls, *args, **kwargs):
udt_class = udt_apply_params(*args, **kwargs)
formatter_for(udt_class.typename)(format_value_utype)
@ -485,6 +510,7 @@ def auto_format_udts():
cassandra.cqltypes.UserType.udt_apply_parameters = classmethod(new_apply_params)
make_udt_class = cassandra.cqltypes.UserType.make_udt_class
def new_make_udt_class(cls, *args, **kwargs):
udt_class = make_udt_class(*args, **kwargs)
formatter_for(udt_class.tuple_type.__name__)(format_value_utype)
@ -665,9 +691,9 @@ class Shell(cmd.Cmd):
# check column role and color appropriately
if table_meta:
if name in [col.name for col in table_meta.partition_key]:
column_colors.default_factory = lambda : RED
column_colors.default_factory = lambda: RED
elif name in [col.name for col in table_meta.clustering_key]:
column_colors.default_factory = lambda : CYAN
column_colors.default_factory = lambda: CYAN
return self.myformat_value(name, colormap=column_colors)
def report_connection(self):
@ -769,7 +795,7 @@ class Shell(cmd.Cmd):
ksmeta = self.get_keyspace_meta(ksname)
if tablename not in ksmeta.tables:
if ksname == 'system_auth' and tablename in ['roles','role_permissions']:
if ksname == 'system_auth' and tablename in ['roles', 'role_permissions']:
self.get_fake_auth_table_meta(ksname, tablename)
else:
raise ColumnFamilyNotFound("Column family %r not found" % tablename)
@ -793,7 +819,7 @@ class Shell(cmd.Cmd):
table_meta.columns['resource'] = ColumnMetadata(table_meta, 'resource', cassandra.cqltypes.UTF8Type)
table_meta.columns['permission'] = ColumnMetadata(table_meta, 'permission', cassandra.cqltypes.UTF8Type)
else:
raise ColumnFamilyNotFoundException("Column family %r not found" % tablename)
raise ColumnFamilyNotFound("Column family %r not found" % tablename)
def get_usertypes_meta(self):
data = self.session.execute("select * from system.schema_usertypes")
@ -813,7 +839,7 @@ class Shell(cmd.Cmd):
def reset_statement(self):
self.reset_prompt()
self.statement.truncate(0)
self.empty_lines = 0;
self.empty_lines = 0
def reset_prompt(self):
if self.current_keyspace is None:
@ -822,7 +848,7 @@ class Shell(cmd.Cmd):
self.set_prompt(self.keyspace_prompt % self.current_keyspace, True)
def set_continue_prompt(self):
if self.empty_lines >=3:
if self.empty_lines >= 3:
self.set_prompt("Statements are terminated with a ';'. You can press CTRL-C to cancel an incomplete statement.")
self.empty_lines = 0
return
@ -881,7 +907,6 @@ class Shell(cmd.Cmd):
return
yield newline
def cmdloop(self):
"""
Adapted from cmd.Cmd's version, because there is literally no way with
@ -1023,7 +1048,7 @@ class Shell(cmd.Cmd):
parsed = cqlruleset.cql_parse(query_string)[1]
except IndexError:
return None
ks = self.cql_unprotect_name(parsed.get_binding('ksname', None))
ks = self.cql_unprotect_name(parsed.get_binding('ksname', None))
cf = self.cql_unprotect_name(parsed.get_binding('cfname'))
return self.get_table_meta(ks, cf)
@ -1046,9 +1071,9 @@ class Shell(cmd.Cmd):
if statement.query_string[:6].lower() == 'select':
self.print_result(rows, self.parse_for_table_meta(statement.query_string))
elif statement.query_string.lower().startswith("list users") or statement.query_string.lower().startswith("list roles"):
self.print_result(rows, self.get_table_meta('system_auth','roles'))
self.print_result(rows, self.get_table_meta('system_auth', 'roles'))
elif statement.query_string.lower().startswith("list"):
self.print_result(rows, self.get_table_meta('system_auth','role_permissions'))
self.print_result(rows, self.get_table_meta('system_auth', 'role_permissions'))
elif rows:
# CAS INSERT/UPDATE
self.writeresult("")
@ -1126,7 +1151,7 @@ class Shell(cmd.Cmd):
# stop if there are no rows
if formatted_values is None:
self.writeresult("")
return;
return
# print row data
for row in formatted_values:
@ -1178,7 +1203,6 @@ class Shell(cmd.Cmd):
prevlines = self.statement.getvalue()
wholestmt = prevlines + curline
begidx = readline.get_begidx() + len(prevlines)
endidx = readline.get_endidx() + len(prevlines)
stuff_to_complete = wholestmt[:begidx]
return cqlruleset.cql_complete(stuff_to_complete, text, cassandra_conn=self,
debug=debug_completion, startsymbol='cqlshCommand')
@ -1412,7 +1436,7 @@ class Shell(cmd.Cmd):
fname = os.path.expanduser(self.cql_unprotect_value(fname))
copyoptnames = map(str.lower, parsed.get_binding('optnames', ()))
copyoptvals = map(self.cql_unprotect_value, parsed.get_binding('optvals', ()))
cleancopyoptvals = [optval.decode('string-escape') for optval in copyoptvals]
cleancopyoptvals = [optval.decode('string-escape') for optval in copyoptvals]
opts = dict(zip(copyoptnames, cleancopyoptvals))
timestart = time.time()
@ -1464,7 +1488,6 @@ class Shell(cmd.Cmd):
try:
if header:
linesource.next()
table_meta = self.get_table_meta(ks, cf)
reader = csv.reader(linesource, **dialect_options)
from multiprocessing import Process, Pipe, cpu_count
@ -1980,7 +2003,7 @@ class Shell(cmd.Cmd):
pdb.set_trace()
def get_help_topics(self):
topics = [ t[3:] for t in dir(self) if t.startswith('do_') and getattr(self, t, None).__doc__]
topics = [t[3:] for t in dir(self) if t.startswith('do_') and getattr(self, t, None).__doc__]
for hide_from_help in ('quit',):
topics.remove(hide_from_help)
return topics
@ -1998,9 +2021,9 @@ class Shell(cmd.Cmd):
"""
topics = parsed.get_binding('topic', ())
if not topics:
shell_topics = [ t.upper() for t in self.get_help_topics() ]
shell_topics = [t.upper() for t in self.get_help_topics()]
self.print_topics("\nDocumented shell commands:", shell_topics, 15, 80)
cql_topics = [ t.upper() for t in cqldocs.get_help_topics() ]
cql_topics = [t.upper() for t in cqldocs.get_help_topics()]
self.print_topics("CQL help topics:", cql_topics, 15, 80)
return
for t in topics:
@ -2095,6 +2118,7 @@ def option_with_default(cparser_getter, section, option, default=None):
except ConfigParser.Error:
return default
def raw_option_with_default(configs, section, option, default=None):
"""
Same (almost) as option_with_default() but won't do any string interpolation.
@ -2105,6 +2129,7 @@ def raw_option_with_default(configs, section, option, default=None):
except ConfigParser.Error:
return default
def should_use_color():
if not sys.stdout.isatty():
return False
@ -2122,6 +2147,7 @@ def should_use_color():
pass
return True
def read_options(cmdlineargs, environment):
configs = ConfigParser.SafeConfigParser()
configs.read(CONFIG_FILE)
@ -2201,6 +2227,7 @@ def read_options(cmdlineargs, environment):
return options, hostname, port
def setup_cqlruleset(cqlmodule):
global cqlruleset
cqlruleset = cqlmodule.CqlRuleSet
@ -2209,10 +2236,12 @@ def setup_cqlruleset(cqlmodule):
cqlruleset.completer_for(rulename, termname)(func)
cqlruleset.commands_end_with_newline.update(my_commands_ending_with_newline)
def setup_cqldocs(cqlmodule):
global cqldocs
cqldocs = cqlmodule.cqldocs
def init_history():
if readline is not None:
try:
@ -2224,6 +2253,7 @@ def init_history():
delims += '.'
readline.set_completer_delims(delims)
def save_history():
if readline is not None:
try:
@ -2231,6 +2261,7 @@ def save_history():
except IOError:
pass
def main(options, hostname, port):
setup_cqlruleset(options.cqlmodule)
setup_cqldocs(options.cqlmodule)
@ -2284,6 +2315,7 @@ def main(options, hostname, port):
if batch_mode and shell.statement_error:
sys.exit(2)
if __name__ == '__main__':
main(*read_options(sys.argv[1:], os.environ))