mirror of https://github.com/apache/cassandra
277 lines
9.4 KiB
Python
Executable File
277 lines
9.4 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
# 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.
|
|
|
|
from optparse import OptionParser
|
|
from StringIO import StringIO
|
|
|
|
import cmd
|
|
import sys
|
|
import readline
|
|
import os
|
|
import re
|
|
import time
|
|
|
|
try:
|
|
import cql
|
|
except ImportError:
|
|
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
|
|
import cql
|
|
from cql.cursor import _COUNT_DESCRIPTION
|
|
|
|
HISTORY = os.path.join(os.path.expanduser('~'), '.cqlsh')
|
|
CQLTYPES = ("bytes", "ascii", "utf8", "timeuuid", "uuid", "long", "int")
|
|
|
|
RED = "\033[1;31m%s\033[0m"
|
|
GREEN = "\033[1;32m%s\033[0m"
|
|
BLUE = "\033[1;34m%s\033[0m"
|
|
YELLOW = "\033[1;33m%s\033[0m"
|
|
CYAN = "\033[1;36m%s\033[0m"
|
|
MAGENTA = "\033[1;35m%s\033[0m"
|
|
|
|
def startswith(words, text):
|
|
return [i for i in words if i.startswith(text)]
|
|
|
|
class Shell(cmd.Cmd):
|
|
default_prompt = "cqlsh> "
|
|
continue_prompt = " ... "
|
|
|
|
def __init__(self, hostname, port, color=False, username=None,
|
|
password=None):
|
|
cmd.Cmd.__init__(self)
|
|
self.conn = cql.connect(hostname, port, user=username, password=password)
|
|
self.cursor = self.conn.cursor()
|
|
|
|
if os.path.exists(HISTORY):
|
|
readline.read_history_file(HISTORY)
|
|
|
|
if sys.stdin.isatty():
|
|
self.prompt = Shell.default_prompt
|
|
else:
|
|
self.prompt = ""
|
|
|
|
self.statement = StringIO()
|
|
self.color = color
|
|
self.in_comment = False
|
|
self.in_batch = False
|
|
|
|
def reset_statement(self):
|
|
self.set_prompt(Shell.default_prompt)
|
|
self.statement.truncate(0)
|
|
|
|
def get_statement(self, line):
|
|
if self.in_comment:
|
|
if "*/" in line:
|
|
fragment = line[line.index("*/")+2:]
|
|
if fragment.strip():
|
|
line = fragment
|
|
self.in_comment = False
|
|
else:
|
|
self.in_comment = False
|
|
self.set_prompt(Shell.default_prompt)
|
|
return None
|
|
else:
|
|
return None
|
|
|
|
if "/*" in line and (not self.in_comment):
|
|
self.in_comment = True
|
|
self.set_prompt(Shell.continue_prompt)
|
|
if line.lstrip().index("/*") != 0:
|
|
self.statement.write(line[:line.lstrip().index("/*")])
|
|
return None
|
|
|
|
if ["BEGIN", "BATCH"] == line.upper().split()[:2]:
|
|
self.set_prompt(Shell.continue_prompt)
|
|
self.in_batch = True
|
|
self.statement.write("%s\n" % line)
|
|
return None
|
|
elif (["APPLY", "BATCH"] == line.upper().split()) and self.in_batch:
|
|
self.in_batch = False
|
|
self.statement.write("%s\n" % line)
|
|
try:
|
|
return self.statement.getvalue()
|
|
finally:
|
|
self.reset_statement()
|
|
|
|
self.statement.write("%s\n" % line)
|
|
|
|
# The end of a statement
|
|
if not line.endswith(";") or self.in_batch:
|
|
self.set_prompt(Shell.continue_prompt)
|
|
return None
|
|
|
|
try:
|
|
return self.statement.getvalue()
|
|
finally:
|
|
self.reset_statement()
|
|
|
|
def default(self, arg):
|
|
def scrub_oneline_comments(s):
|
|
res = re.sub(r'\/\*.*\*\/', '', s)
|
|
res = re.sub(r'--.*$', '', res)
|
|
return res
|
|
|
|
input = scrub_oneline_comments(arg)
|
|
if not input.strip(): return
|
|
statement = self.get_statement(input)
|
|
if not statement: return
|
|
|
|
for i in range(1,4):
|
|
try:
|
|
self.cursor.execute(statement)
|
|
break
|
|
except cql.IntegrityError, err:
|
|
self.printerr("Attempt #%d: %s" % (i, str(err)), color=RED)
|
|
time.sleep(1*i)
|
|
|
|
if self.cursor.description is _COUNT_DESCRIPTION:
|
|
if self.cursor.result: print self.cursor.result[0]
|
|
else:
|
|
for x in range(self.cursor.rowcount):
|
|
row = self.cursor.fetchone()
|
|
self.printout(repr(row[0]), BLUE, False)
|
|
for (i, value) in enumerate(row[1:]):
|
|
name = self.cursor.description[i+1][0]
|
|
self.printout(" | ", newline=False)
|
|
self.printout(repr(name), MAGENTA, False)
|
|
self.printout(",", newline=False)
|
|
self.printout(repr(value), YELLOW, False)
|
|
self.printout("")
|
|
|
|
def emptyline(self):
|
|
pass
|
|
|
|
def complete_select(self, text, line, begidx, endidx):
|
|
keywords = ('FIRST', 'REVERSED', 'FROM', 'WHERE', 'KEY')
|
|
return startswith(keywords, text.upper())
|
|
complete_SELECT = complete_select
|
|
|
|
def complete_update(self, text, line, begidx, endidx):
|
|
keywords = ('WHERE', 'KEY', 'SET')
|
|
return startswith(keywords, text.upper())
|
|
complete_UPDATE = complete_update
|
|
|
|
def complete_create(self, text, line, begidx, endidx):
|
|
words = line.split()
|
|
if len(words) < 3:
|
|
return startswith(['COLUMNFAMILY', 'KEYSPACE'], text.upper())
|
|
|
|
common = ['WITH', 'AND']
|
|
|
|
if words[1].upper() == 'COLUMNFAMILY':
|
|
types = startswith(CQLTYPES, text)
|
|
keywords = startswith(('KEY', 'PRIMARY'), text.upper())
|
|
props = startswith(("comparator",
|
|
"comment",
|
|
"row_cache_size",
|
|
"key_cache_size",
|
|
"read_repair_chance",
|
|
"gc_grace_seconds",
|
|
"default_validation",
|
|
"min_compaction_threshold",
|
|
"max_compaction_threshold",
|
|
"row_cache_save_period_in_seconds",
|
|
"key_cache_save_period_in_seconds",
|
|
"memtable_flush_after_mins",
|
|
"memtable_throughput_in_mb",
|
|
"memtable_operations_in_millions",
|
|
"replicate_on_write"), text)
|
|
return startswith(common, text.upper()) + types + keywords + props
|
|
|
|
if words[1].upper() == 'KEYSPACE':
|
|
props = ("replication_factor", "strategy_options", "strategy_class")
|
|
return startswith(common, text.upper()) + startswith(props, text)
|
|
complete_CREATE = complete_create
|
|
|
|
def complete_drop(self, text, line, begidx, endidx):
|
|
words = line.split()
|
|
if len(words) < 3:
|
|
return startswith(['COLUMNFAMILY', 'KEYSPACE'], text.upper())
|
|
complete_DROP = complete_drop
|
|
|
|
def completenames(self, text, *ignored):
|
|
cmds = startswith(('USE', 'SELECT', 'UPDATE', 'DELETE', 'CREATE', 'DROP'),
|
|
text.upper())
|
|
return cmd.Cmd.completenames(self, text, ignored) + cmds
|
|
|
|
def set_prompt(self, prompt):
|
|
if sys.stdin.isatty():
|
|
self.prompt = prompt
|
|
|
|
def do_EOF(self, arg):
|
|
if sys.stdin.isatty(): print
|
|
self.do_exit(None)
|
|
|
|
def do_exit(self, arg):
|
|
sys.exit()
|
|
do_quit = do_exit
|
|
|
|
def printout(self, text, color=None, newline=True, out=sys.stdout):
|
|
if not color or not self.color:
|
|
out.write(text)
|
|
else:
|
|
out.write(color % text)
|
|
|
|
if newline:
|
|
out.write("\n");
|
|
|
|
def printerr(self, text, color=None, newline=True):
|
|
self.printout(text, color, newline, sys.stderr)
|
|
|
|
if __name__ == '__main__':
|
|
parser = OptionParser(usage = "Usage: %prog [host [port]]")
|
|
parser.add_option("-C",
|
|
"--color",
|
|
action="store_true",
|
|
help="Enable color output.")
|
|
parser.add_option("-u", "--username", help="Authenticate as user.")
|
|
parser.add_option("-p", "--password", help="Authenticate using password.")
|
|
(options, arguments) = parser.parse_args()
|
|
|
|
hostname = len(arguments) > 0 and arguments[0] or "localhost"
|
|
|
|
if len(arguments) > 1:
|
|
try:
|
|
port = int(arguments[1])
|
|
except ValueError:
|
|
print >>sys.stderr, "%s is not a valid port number" % arguments[1]
|
|
parser.print_help(file=sys.stderr)
|
|
sys.exit(1)
|
|
else:
|
|
port = 9160
|
|
|
|
|
|
shell = Shell(hostname,
|
|
port,
|
|
color=options.color,
|
|
username=options.username,
|
|
password=options.password)
|
|
while(True):
|
|
try:
|
|
shell.cmdloop()
|
|
except SystemExit:
|
|
readline.write_history_file(HISTORY)
|
|
break
|
|
except cql.Error, cqlerr:
|
|
shell.printerr(str(cqlerr), color=RED)
|
|
except KeyboardInterrupt:
|
|
shell.reset_statement()
|
|
print
|
|
except Exception, err:
|
|
shell.printerr("Exception: %s" % err, color=RED)
|
|
|