cqlsh: Fix handling of CAS statement results

Patch by Tyler Hobbs; reviewed by Aleksey Yeschenko for CASSANDRA-7671
This commit is contained in:
Tyler Hobbs 2014-08-21 11:54:47 -05:00
parent f36d1b51b7
commit d586ea8980
4 changed files with 33 additions and 19 deletions

View File

@ -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)

View File

@ -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:

View File

@ -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> "]" )?
;

View File

@ -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'):