cqlsh: in cql3 mode, use cql3 quoting when outputting cql. Patch by paul cannon, reviewed by brandonwilliams for CASSANDRA-4173
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/f1ee34f0 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/f1ee34f0 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/f1ee34f0 Branch: refs/heads/cassandra-1.1 Commit: f1ee34f08fdde93f4a79227a5d6d3987fe007d77 Parents: c5ad288 Author: Brandon Williams <[email protected]> Authored: Tue May 1 09:48:27 2012 -0500 Committer: Brandon Williams <[email protected]> Committed: Tue May 1 09:48:27 2012 -0500 ---------------------------------------------------------------------- bin/cqlsh | 120 +++++++++++++++++++--------------- pylib/cqlshlib/cql3handling.py | 13 ++++- 2 files changed, 79 insertions(+), 54 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/f1ee34f0/bin/cqlsh ---------------------------------------------------------------------- diff --git a/bin/cqlsh b/bin/cqlsh index 00c6c0d..b1833fd 100755 --- a/bin/cqlsh +++ b/bin/cqlsh @@ -59,10 +59,7 @@ if os.path.isdir(cqlshlibdir): sys.path.insert(0, cqlshlibdir) from cqlshlib import cqlhandling, cql3handling, pylexotron, wcwidth -from cqlshlib.cqlhandling import (token_dequote, cql_dequote, cql_escape, - maybe_cql_escape, cql_typename) -from cqlshlib.cql3handling import (CqlTableDef, maybe_cql3_escape_name, - cql3_escape_value) +from cqlshlib.cqlhandling import cql_typename try: import readline @@ -235,32 +232,32 @@ def complete_help(ctxt, cqlsh): @cqlhandling.cql_add_completer('describeCommand', 'ksname') def complete_describe_ks(ctxt, cqlsh): - return map(maybe_cql_escape, cqlsh.get_keyspace_names()) + return map(cqlsh.cql_protect_name, cqlsh.get_keyspace_names()) @cqlhandling.cql_add_completer('describeCommand', 'cfname') def complete_describe_cf(ctxt, cqlsh): - return map(maybe_cql_escape, cqlsh.get_columnfamily_names()) + return map(cqlsh.cql_protect_name, cqlsh.get_columnfamily_names()) @cqlhandling.cql_add_completer('assumeCommand', 'ks') def complete_assume_ks(ctxt, cqlsh): - return [maybe_cql_escape(ks) + '.' for ks in cqlsh.get_keyspace_names()] + return [cqlsh.cql_protect_name(ks) + '.' for ks in cqlsh.get_keyspace_names()] @cqlhandling.cql_add_completer('assumeCommand', 'cf') def complete_assume_cf(ctxt, cqlsh): ks = ctxt.get_binding('ks', None) if ks is not None: - ks = cql_dequote(ks) - return map(maybe_cql_escape, cqlsh.get_columnfamily_names(ks)) + ks = cqlsh.cql_unprotect_name(ks) + return map(cqlsh.cql_protect_name, cqlsh.get_columnfamily_names(ks)) @cqlhandling.cql_add_completer('assumeTypeDef', 'colname') def complete_assume_col(ctxt, cqlsh): ks = ctxt.get_binding('ks', None) - ks = cql_dequote(ks) if ks is not None else None - cf = cql_dequote(ctxt.get_binding('cf')) + ks = cqlsh.cql_unprotect_name(ks) if ks is not None else None + cf = cqlsh.cql_unprotect_name(ctxt.get_binding('cf')) cfdef = cqlsh.get_columnfamily(cf, ksname=ks) cols = [cm.name for cm in cfdef.column_metadata] cols.append(cfdef.key_alias or 'KEY') - return map(maybe_cql_escape, cols) + return map(cqlsh.cql_protect_name, cols) def complete_source_quoted_filename(ctxt, cqlsh): partial = ctxt.get_binding('partial', '') @@ -576,11 +573,11 @@ class Shell(cmd.Cmd): print 'No overrides.' return for keyspace, ksoverrides in groupby(all_overrides, key=lambda x:x[0][0]): - keyspace = maybe_cql_escape(keyspace) + keyspace = self.cql_protect_name(keyspace) print 'USE %s;' % keyspace print for (ks, cf), override in ksoverrides: - cf = maybe_cql_escape(cf) + cf = self.cql_protect_name(cf) if override.default_name_type: print 'ASSUME %s NAMES ARE %s;' \ % (cf, cql_typename(override.default_name_type)) @@ -588,7 +585,7 @@ class Shell(cmd.Cmd): print 'ASSUME %s VALUES ARE %s;' \ % (cf, cql_typename(override.default_value_type)) for colname, vtype in override.value_types.items(): - colname = maybe_cql_escape(colname) + colname = self.cql_protect_name(colname) print 'ASSUME %s(%s) VALUES ARE %s;' \ % (cf, colname, cql_typename(vtype)) print @@ -715,7 +712,7 @@ class Shell(cmd.Cmd): where "keyspace"=:ks and "columnfamily"=:cf""", {'ks': ksname, 'cf': cfname}) cols = self.fetchdict_all() - return CqlTableDef.from_layout(layout, cols) + return cql3handling.CqlTableDef.from_layout(layout, cols) # ===== end cql3-dependent parts ===== @@ -873,7 +870,7 @@ class Shell(cmd.Cmd): """ ksname = parsed.get_binding('ksname') if self.perform_statement(parsed.extract_orig()): - self.current_keyspace = cql_dequote(ksname) + self.current_keyspace = self.cql_unprotect_name(ksname) def do_select(self, parsed): """ @@ -897,8 +894,8 @@ class Shell(cmd.Cmd): """ ksname = parsed.get_binding('selectks') if ksname is not None: - ksname = cql_dequote(ksname) - cfname = cql_dequote(parsed.get_binding('selectsource')) + ksname = self.cql_unprotect_name(ksname) + cfname = self.cql_unprotect_name(parsed.get_binding('selectsource')) decoder = self.determine_decoder_for(cfname, ksname=ksname) self.perform_statement(parsed.extract_orig(), decoder=decoder) @@ -1062,13 +1059,30 @@ class Shell(cmd.Cmd): if self.prompt != '': self.prompt = prompt + def cql_protect_name(self, name): + if self.cqlver_atleast(3): + return cql3handling.maybe_cql3_escape_name(name) + else: + return cqlhandling.maybe_cql_escape(name) + + def cql_protect_value(self, value): + return cqlhandling.cql_escape(value) + + def cql_unprotect_name(self, namestr): + if namestr is not None: + return cql3handling.cql3_dequote_name(namestr) + + def cql_unprotect_value(self, valstr): + if valstr is not None: + return cqlhandling.cql_dequote(valstr) + def print_recreate_keyspace(self, ksdef, out): stratclass = trim_if_present(ksdef.strategy_class, 'org.apache.cassandra.locator.') - ksname = maybe_cql_escape(ksdef.name) + ksname = self.cql_protect_name(ksdef.name) out.write("CREATE KEYSPACE %s WITH strategy_class = %s" - % (ksname, cql_escape(stratclass))) + % (ksname, self.cql_protect_value(stratclass))) for opname, opval in ksdef.strategy_options.iteritems(): - out.write("\n AND strategy_options:%s = %s" % (opname, cql_escape(opval))) + out.write("\n AND strategy_options:%s = %s" % (opname, self.cql_protect_value(opval))) out.write(';\n') if ksdef.cf_defs: @@ -1111,41 +1125,41 @@ class Shell(cmd.Cmd): return self.print_recreate_columnfamily_from_cfdef(cfdef, out) def print_recreate_columnfamily_from_cfdef(self, cfdef, out): - cfname = maybe_cql_escape(cfdef.name) + cfname = self.cql_protect_name(cfdef.name) out.write("CREATE COLUMNFAMILY %s (\n" % cfname) - alias = maybe_cql_escape(cfdef.key_alias) if cfdef.key_alias else 'KEY' + alias = self.cql_protect_name(cfdef.key_alias) if cfdef.key_alias else 'KEY' keytype = cql_typename(cfdef.key_validation_class) out.write(" %s %s PRIMARY KEY" % (alias, keytype)) indexed_columns = [] for col in cfdef.column_metadata: - colname = maybe_cql_escape(col.name) + colname = self.cql_protect_name(col.name) out.write(",\n %s %s" % (colname, cql_typename(col.validation_class))) if col.index_name is not None: indexed_columns.append(col) - notable_columns = [] + cf_opts = [] for (option, thriftname) in cqlhandling.columnfamily_options: optval = getattr(cfdef, thriftname or option, None) if optval is None: continue if option in ('comparator', 'default_validation'): optval = cql_typename(optval) - elif option == 'row_cache_provider': - optval = cql_escape(trim_if_present(optval, 'org.apache.cassandra.cache.')) - elif option == 'compaction_strategy_class': - optval = cql_escape(trim_if_present(optval, 'org.apache.cassandra.db.compaction.')) else: - optval = cql_escape(optval) - notable_columns.append((option, optval)) + if option == 'row_cache_provider': + optval = trim_if_present(optval, 'org.apache.cassandra.cache.') + elif option == 'compaction_strategy_class': + optval = trim_if_present(optval, 'org.apache.cassandra.db.compaction.') + optval = self.cql_protect_value(optval) + cf_opts.append((option, optval)) for option, thriftname, _ in cqlhandling.columnfamily_map_options: optmap = getattr(cfdef, thriftname or option, {}) for k, v in optmap.items(): if option == 'compression_parameters' and k == 'sstable_compression': v = trim_if_present(v, 'org.apache.cassandra.io.compress.') - notable_columns.append(('%s:%s' % (option, k), cql_escape(v))) + cf_opts.append(('%s:%s' % (option, k), self.cql_protect_value(v))) out.write('\n)') - if notable_columns: + if cf_opts: joiner = 'WITH' - for optname, optval in notable_columns: + for optname, optval in cf_opts: out.write(" %s\n %s=%s" % (joiner, optname, optval)) joiner = 'AND' out.write(";\n") @@ -1154,25 +1168,25 @@ class Shell(cmd.Cmd): out.write('\n') # guess CQL can't represent index_type or index_options out.write('CREATE INDEX %s ON %s (%s);\n' - % (col.index_name, cfname, maybe_cql_escape(col.name))) + % (col.index_name, cfname, self.cql_protect_name(col.name))) def print_recreate_columnfamily_from_layout(self, layout, out): - cfname = maybe_cql3_escape_name(layout.name) + cfname = self.cql_protect_name(layout.name) out.write("CREATE COLUMNFAMILY %s (\n" % cfname) keycol = layout.columns[0] - out.write(" %s %s" % (maybe_cql3_escape_name(keycol.name), keycol.cqltype)) + out.write(" %s %s" % (self.cql_protect_name(keycol.name), keycol.cqltype)) if len(layout.key_components) == 1: out.write(" PRIMARY KEY") indexed_columns = [] for col in layout.columns[1:]: - colname = maybe_cql3_escape_name(col.name) + colname = self.cql_protect_name(col.name) out.write(",\n %s %s" % (colname, col.cqltype)) if col.index_name is not None: indexed_columns.append(col) if len(layout.key_components) > 1: - out.write(",\n PRIMARY KEY (%s)" % ', '.join(map(maybe_cql3_escape_name, layout.key_components))) + out.write(",\n PRIMARY KEY (%s)" % ', '.join(map(self.cql_protect_name, layout.key_components))) out.write("\n)") joiner = 'WITH' @@ -1189,13 +1203,13 @@ class Shell(cmd.Cmd): optval = trim_if_present(optval, 'org.apache.cassandra.cache.') elif option == 'compaction_strategy_class': optval = trim_if_present(optval, 'org.apache.cassandra.db.compaction.') - cf_opts.append((option, cql3_escape_value(optval))) + cf_opts.append((option, self.cql_protect_value(optval))) for option, _ in cql3handling.columnfamily_map_options: optmap = getattr(layout, option, {}) for k, v in optmap.items(): if option == 'compression_parameters' and k == 'sstable_compression': v = trim_if_present(v, 'org.apache.cassandra.io.compress.') - cf_opts.append(('%s:%s' % (option, k.encode('ascii')), cql3_escape_value(v))) + cf_opts.append(('%s:%s' % (option, k.encode('ascii')), self.cql_protect_value(v))) if cf_opts: for optname, optval in cf_opts: out.write(" %s\n %s=%s" % (joiner, optname, optval)) @@ -1206,7 +1220,7 @@ class Shell(cmd.Cmd): out.write('\n') # guess CQL can't represent index_type or index_options out.write('CREATE INDEX %s ON %s (%s);\n' - % (col.index_name, cfname, maybe_cql3_escape_name(col.name))) + % (col.index_name, cfname, self.cql_protect_name(col.name))) def describe_keyspace(self, ksname): print @@ -1296,7 +1310,7 @@ class Shell(cmd.Cmd): what = parsed.matched[1][1].lower() if what == 'keyspace': - ksname = cql_dequote(parsed.get_binding('ksname', '')) + ksname = self.cql_unprotect_name(parsed.get_binding('ksname', '')) if not ksname: ksname = self.current_keyspace if ksname is None: @@ -1304,7 +1318,7 @@ class Shell(cmd.Cmd): return self.describe_keyspace(ksname) elif what == 'columnfamily': - cfname = cql_dequote(parsed.get_binding('cfname')) + cfname = self.cql_unprotect_name(parsed.get_binding('cfname')) self.describe_columnfamily(cfname) elif what == 'columnfamilies': self.describe_columnfamilies(self.current_keyspace) @@ -1387,12 +1401,12 @@ class Shell(cmd.Cmd): """ params = {} - for paramname in ('ks', 'cf', 'colname', 'names', 'values', 'colvalues'): + for paramname in ('ks', 'cf', 'colname'): val = parsed.get_binding(paramname, None) - if val is not None: - val = cql_dequote(val) - params[paramname] = val - + params[paramname] = self.cql_unprotect_name(val) + for paramname in ('names', 'values', 'colvalues'): + val = parsed.get_binding(paramname, None) + params[paramname] = self.cql_unprotect_value(val) if params['ks'] is None: if self.current_keyspace is None: self.printerr('Error: not in any keyspace.') @@ -1433,7 +1447,7 @@ class Shell(cmd.Cmd): """ fname = parsed.get_binding('fname') - fname = os.path.expanduser(cql_dequote(fname)) + fname = os.path.expanduser(cqlsh.cql_unprotect_value(fname)) try: f = open(fname, 'r') except IOError, e: @@ -1497,7 +1511,7 @@ class Shell(cmd.Cmd): ' to disable.' % (self.query_out.name,)) return - fname = os.path.expanduser(cql_dequote(fname)) + fname = os.path.expanduser(cqlsh.cql_unprotect_value(fname)) try: f = open(fname, 'a') except IOError, e: @@ -1543,7 +1557,7 @@ class Shell(cmd.Cmd): if not topics: return cmd.Cmd.do_help(self, '') for t in topics: - cmd.Cmd.do_help(self, cql_dequote(t).lower()) + cmd.Cmd.do_help(self, self.cql_unprotect_value(t).lower()) def help_types(self): print "\n CQL types recognized by this version of cqlsh:\n" http://git-wip-us.apache.org/repos/asf/cassandra/blob/f1ee34f0/pylib/cqlshlib/cql3handling.py ---------------------------------------------------------------------- diff --git a/pylib/cqlshlib/cql3handling.py b/pylib/cqlshlib/cql3handling.py index 9d27f44..a8828ab 100644 --- a/pylib/cqlshlib/cql3handling.py +++ b/pylib/cqlshlib/cql3handling.py @@ -16,7 +16,7 @@ import re from warnings import warn -from .cqlhandling import cql_typename, cql_escape +from .cqlhandling import cql_typename, cql_escape, cql_dequote try: import json @@ -66,6 +66,17 @@ def cql3_escape_value(value): def cql3_escape_name(name): return '"%s"' % name.replace('"', '""') +def cql3_dequote_value(value): + return cql_dequote(value) + +def cql3_dequote_name(name): + name = name.strip() + if name == '': + return name + if name[0] == '"': + name = name[1:-1].replace('""', '"') + return name + valid_cql3_word_re = re.compile(r'^[a-z][0-9a-z_]*$', re.I) def is_valid_cql3_name(s):
