Repository: cassandra Updated Branches: refs/heads/trunk f6ef8ef4d -> 9a4f6bdf0
cqlsh: Fix handling of CAS statement results Patch by Tyler Hobbs; reviewed by Aleksey Yeschenko for CASSANDRA-7671 Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/d586ea89 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/d586ea89 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/d586ea89 Branch: refs/heads/trunk Commit: d586ea89800ddbba38eea3bd08e2d107bf765344 Parents: f36d1b5 Author: Tyler Hobbs <ty...@datastax.com> Authored: Thu Aug 21 11:54:47 2014 -0500 Committer: Tyler Hobbs <ty...@datastax.com> Committed: Thu Aug 21 11:54:47 2014 -0500 ---------------------------------------------------------------------- CHANGES.txt | 1 + bin/cqlsh | 34 ++++++++++++++++++---------------- pylib/cqlshlib/cql3handling.py | 13 +++++++++++-- pylib/cqlshlib/pylexotron.py | 4 +++- 4 files changed, 33 insertions(+), 19 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/d586ea89/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index 2320653..11ec7ff 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 2.1.1 + * (cqlsh) Fix handling of CAS statement results (CASSANDRA-7671) * (cqlsh) COPY TO/FROM improvements (CASSANDRA-7405) * Support list index operations with conditions (CASSANDRA-7499) * Add max live/tombstoned cells to nodetool cfstats output (CASSANDRA-7731) http://git-wip-us.apache.org/repos/asf/cassandra/blob/d586ea89/bin/cqlsh ---------------------------------------------------------------------- diff --git a/bin/cqlsh b/bin/cqlsh index 340566d..b633e93 100755 --- a/bin/cqlsh +++ b/bin/cqlsh @@ -585,13 +585,13 @@ class Shell(cmd.Cmd): self.decoding_errors.append(err) return format_value(err, self.output_codec.name, addcolor=self.color) - def myformat_colname(self, name, cfMetaData = None): + def myformat_colname(self, name, table_meta=None): column_colors = COLUMN_NAME_COLORS.copy() # check column role and color appropriately - if cfMetaData: - if name in [col.name for col in cfMetaData.partition_key]: + if table_meta: + if name in [col.name for col in table_meta.partition_key]: column_colors.default_factory = lambda : RED - elif name in [col.name for col in cfMetaData.clustering_key]: + elif name in [col.name for col in table_meta.clustering_key]: column_colors.default_factory = lambda : CYAN return self.myformat_value(name, colormap=column_colors) @@ -913,6 +913,12 @@ class Shell(cmd.Cmd): return result + def parse_for_table_meta(self, query_string): + parsed = cqlruleset.cql_parse(query_string)[1] + 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) + def perform_simple_statement(self, statement, with_default_limit=False): if not statement: return False @@ -930,25 +936,21 @@ class Shell(cmd.Cmd): return False if statement.query_string[:6].lower() == 'select' or statement.query_string.lower().startswith("list"): - parsed = cqlruleset.cql_parse(statement.query_string)[1] - ks = self.cql_unprotect_name(parsed.get_binding('ksname', None)) - cf = self.cql_unprotect_name(parsed.get_binding('cfname')) - cfMetaData = self.get_table_meta(ks, cf) - self.print_result(rows, with_default_limit, cfMetaData) + self.print_result(rows, with_default_limit, self.parse_for_table_meta(statement.query_string)) elif rows: # CAS INSERT/UPDATE self.writeresult("") - self.print_static_result(rows) + self.print_static_result(rows, self.parse_for_table_meta(statement.query_string)) self.flush_output() return True - def print_result(self, rows, with_default_limit, cfMetaData): + def print_result(self, rows, with_default_limit, table_meta): self.decoding_errors = [] self.writeresult("") if rows: rows = list(rows) # this may be an iterator if the result is large enough to page - self.print_static_result(rows, cfMetaData) + self.print_static_result(rows, table_meta) self.writeresult("(%d rows)" % len(rows or [])) if self.decoding_errors: @@ -965,16 +967,16 @@ class Shell(cmd.Cmd): % DEFAULT_SELECT_LIMIT, color=RED) self.writeresult("") - def print_static_result(self, rows, cfMetaData): + def print_static_result(self, rows, table_meta): if not rows: # print header only - colnames = cfMetaData.columns.keys() # full header - formatted_names = [self.myformat_colname(name, cfMetaData) for name in colnames] + colnames = table_meta.columns.keys() # full header + formatted_names = [self.myformat_colname(name, table_meta) for name in colnames] self.print_formatted_result(formatted_names, None) return colnames = rows[0].keys() - formatted_names = [self.myformat_colname(name, cfMetaData) for name in colnames] + formatted_names = [self.myformat_colname(name, table_meta) for name in colnames] formatted_values = [map(self.myformat_value, row.values()) for row in rows] if self.expand_enabled: http://git-wip-us.apache.org/repos/asf/cassandra/blob/d586ea89/pylib/cqlshlib/cql3handling.py ---------------------------------------------------------------------- diff --git a/pylib/cqlshlib/cql3handling.py b/pylib/cqlshlib/cql3handling.py index 0888d64..e4ef67b 100644 --- a/pylib/cqlshlib/cql3handling.py +++ b/pylib/cqlshlib/cql3handling.py @@ -185,6 +185,7 @@ JUNK ::= /([ \t\r\f\v]+|(--|[/][/])[^\n\r]*([\n\r]|$)|[/][*].*?[*][/])/ ; | <blobLiteral> | <collectionLiteral> | <functionName> <functionArguments> + | "NULL" ; <functionArguments> ::= "(" ( <term> ( "," <term> )* )? ")" @@ -673,6 +674,7 @@ syntax_rules += r''' ( "," [colname]=<cident> )* ")" "VALUES" "(" [newval]=<term> valcomma="," [newval]=<term> ( valcomma="," [newval]=<term> )* valcomma=")" + ( "IF" "NOT" "EXISTS")? ( "USING" [insertopt]=<usingOption> ( "AND" [insertopt]=<usingOption> )* )? ; @@ -741,13 +743,19 @@ syntax_rules += r''' ( "AND" [updateopt]=<usingOption> )* )? "SET" <assignment> ( "," <assignment> )* "WHERE" <whereClause> + ( "IF" <conditions> )? ; <assignment> ::= updatecol=<cident> - ( "=" update_rhs=( <value> | <cident> ) + ( "=" update_rhs=( <term> | <cident> ) ( counterop=( "+" | "-" ) inc=<wholenumber> - | listadder="+" listcol=<cident> ) + | listadder="+" listcol=<cident> )? | indexbracket="[" <term> "]" "=" <term> ) ; +<conditions> ::= <condition> ( "AND" <condition> )* + ; +<condition> ::= <cident> ( "[" <term> "]" )? ( ( "=" | "<" | ">" | "<=" | ">=" | "!=" ) <term> + | "IN" "(" <term> ( "," <term> )* ")") + ; ''' @completer_for('updateStatement', 'updateopt') @@ -819,6 +827,7 @@ syntax_rules += r''' "FROM" cf=<columnFamilyName> ( "USING" [delopt]=<deleteOption> )? "WHERE" <whereClause> + ( "IF" ( "EXISTS" | <conditions> ) )? ; <deleteSelector> ::= delcol=<cident> ( memberbracket="[" memberselector=<term> "]" )? ; http://git-wip-us.apache.org/repos/asf/cassandra/blob/d586ea89/pylib/cqlshlib/pylexotron.py ---------------------------------------------------------------------- diff --git a/pylib/cqlshlib/pylexotron.py b/pylib/cqlshlib/pylexotron.py index ad283df..b4ac36f 100644 --- a/pylib/cqlshlib/pylexotron.py +++ b/pylib/cqlshlib/pylexotron.py @@ -14,7 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from functools import partial import re from .saferscanner import SaferScanner @@ -24,6 +23,9 @@ class LexingError(Exception): bad_char = len(rulestr) - len(unmatched) linenum = rulestr[:bad_char].count('\n') + 1 charnum = len(rulestr[:bad_char].rsplit('\n', 1)[-1]) + 1 + snippet_start = max(0, min(len(rulestr), bad_char - 10)) + snippet_end = max(0, min(len(rulestr), bad_char + 10)) + msg += " (Error at: '...%s...')" % (rulestr[snippet_start:snippet_end],) raise cls(linenum, charnum, msg) def __init__(self, linenum, charnum, msg='Lexing error'):