mirror of https://github.com/apache/cassandra
Added tab-ahead support for new built-in functions
patch by Brad Schoening, Naren Sreedhara; reviewed by Stefan Miklosovic, Bernardo Botella Corbi for CASSANDRA-19631
This commit is contained in:
parent
6111a60ad6
commit
91bc34fbf8
|
|
@ -1,4 +1,5 @@
|
|||
5.1
|
||||
* Add autocompletion in CQLSH for built-in functions (CASSANDRA-19631)
|
||||
* Introduce metadata serialization version V4 (CASSANDRA-19970)
|
||||
* Allow CMS reconfiguration to work around DOWN nodes (CASSANDRA-19943)
|
||||
* Make TableParams.Serializer set allowAutoSnapshots and incrementalBackups (CASSANDRA-19954)
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
SELECT id, to_date(create_ts) FROM myTable
|
||||
|
|
@ -184,12 +184,12 @@ time where the function is invoked:
|
|||
|===
|
||||
|Function name |Output type
|
||||
|
||||
| `current_timestamp` | `timestamp`
|
||||
|
||||
| `current_date` | `date`
|
||||
|
||||
| `current_time` | `time`
|
||||
|
||||
| `current_timestamp` | `timestamp`
|
||||
|
||||
| `current_timeuuid` | `timeUUID`
|
||||
|===
|
||||
|
||||
|
|
@ -223,6 +223,13 @@ A number of functions are provided to convert a `timeuuid`, a `timestamp` or a `
|
|||
| `to_unix_timestamp` | `date` | Converts the `date` argument into a `bigInt` raw value
|
||||
|===
|
||||
|
||||
For example, a timestamp can be converted to a date with the following:
|
||||
|
||||
[source,cql]
|
||||
----
|
||||
include::cassandra:example$CQL/to_date.cql[]
|
||||
----
|
||||
|
||||
==== Blob conversion functions
|
||||
|
||||
A number of functions are provided to convert the native types into
|
||||
|
|
|
|||
|
|
@ -602,7 +602,7 @@ def cf_prop_val_mapender_completer(ctxt, cass):
|
|||
|
||||
@completer_for('tokenDefinition', 'token')
|
||||
def token_word_completer(ctxt, cass):
|
||||
return ['token(']
|
||||
return ['TOKEN']
|
||||
|
||||
|
||||
@completer_for('simpleStorageType', 'typename')
|
||||
|
|
@ -741,12 +741,13 @@ syntax_rules += r'''
|
|||
;
|
||||
<whereClause> ::= <relation> ( "AND" <relation> )*
|
||||
;
|
||||
<relation> ::= [rel_lhs]=<cident> ( "[" <term> "]" )? ( "=" | "<" | ">" | "<=" | ">=" | "!=" | ( "NOT" )? "CONTAINS" ( "KEY" )? ) <term>
|
||||
<relation> ::= [rel_lhs]=<cident> ( "[" <term> "]" )? ( "=" | "<" | ">" | "<=" | ">=" | "!=" | ( "NOT" )? "CONTAINS" ( "KEY" )? ) (<term> | <operandFunctions>)
|
||||
| token="TOKEN" "(" [rel_tokname]=<cident>
|
||||
( "," [rel_tokname]=<cident> )*
|
||||
")" ("=" | "<" | ">" | "<=" | ">=") <tokenDefinition>
|
||||
| [rel_lhs]=<cident> (( "NOT" )? "IN" ) "(" <term> ( "," <term> )* ")"
|
||||
| [rel_lhs]=<cident> "BETWEEN" <term> "AND" <term>
|
||||
| <operandFunctions>
|
||||
;
|
||||
<selectClause> ::= "DISTINCT"? <selector> ("AS" <cident>)? ("," <selector> ("AS" <cident>)?)*
|
||||
| "*"
|
||||
|
|
@ -755,14 +756,20 @@ syntax_rules += r'''
|
|||
;
|
||||
<selector> ::= [colname]=<cident> ( "[" ( <term> ( ".." <term> "]" )? | <term> ".." ) )?
|
||||
| <udtSubfieldSelection>
|
||||
| "WRITETIME" "(" [colname]=<cident> ")"
|
||||
| "MAXWRITETIME" "(" [colname]=<cident> ")"
|
||||
| "TTL" "(" [colname]=<cident> ")"
|
||||
| "COUNT" "(" star=( "*" | "1" ) ")"
|
||||
| "CAST" "(" <selector> "AS" <storageType> ")"
|
||||
| "TTL" "(" [colname]=<cident> ")"
|
||||
| "TOKEN" "(" [colname]=<cident> ")"
|
||||
| <aggregateMathFunctions>
|
||||
| <scalarMathFunctions>
|
||||
| <collectionFunctions>
|
||||
| <currentTimeFunctions>
|
||||
| <maskFunctions>
|
||||
| <timeConversionFunctions>
|
||||
| <writetimeFunctions>
|
||||
| <functionName> <selectionFunctionArguments>
|
||||
| <term>
|
||||
;
|
||||
|
||||
<selectionFunctionArguments> ::= "(" ( <selector> ( "," <selector> )* )? ")"
|
||||
;
|
||||
<orderByClause> ::= [ordercol]=<cident> ( "ASC" | "DESC" )?
|
||||
|
|
@ -775,6 +782,60 @@ syntax_rules += r'''
|
|||
<groupByFunctionArgument> ::= [groupcol]=<cident>
|
||||
| <term>
|
||||
;
|
||||
|
||||
<aggregateMathFunctions> ::= "COUNT" "(" star=( "*" | "1" ) ")"
|
||||
| "AVG" "(" [colname]=<cident> ")"
|
||||
| "MIN" "(" [colname]=<cident> ")"
|
||||
| "MAX" "(" [colname]=<cident> ")"
|
||||
| "SUM" "(" [colname]=<cident> ")"
|
||||
;
|
||||
|
||||
<scalarMathFunctions> ::= "ABS" "(" [colname]=<cident> ")"
|
||||
| "EXP" "(" [colname]=<cident> ")"
|
||||
| "LOG" "(" [colname]=<cident> ")"
|
||||
| "LOG10" "(" [colname]=<cident> ")"
|
||||
| "ROUND" "(" [colname]=<cident> ")"
|
||||
;
|
||||
|
||||
<collectionFunctions> ::= "MAP_KEYS" "(" [colname]=<cident> ")"
|
||||
| "MAP_VALUES" "(" [colname]=<cident> ")"
|
||||
| "COLLECTION_AVG" "(" [colname]=<cident> ")"
|
||||
| "COLLECTION_COUNT" "(" [colname]=<cident> ")"
|
||||
| "COLLECTION_MIN" "(" [colname]=<cident> ")"
|
||||
| "COLLECTION_MAX" "(" [colname]=<cident> ")"
|
||||
| "COLLECTION_SUM" "(" [colname]=<cident> ")"
|
||||
;
|
||||
|
||||
<currentTimeFunctions> ::= "CURRENT_DATE()"
|
||||
| "CURRENT_TIME()"
|
||||
| "CURRENT_TIMESTAMP()"
|
||||
| "CURRENT_TIMEUUID()"
|
||||
;
|
||||
|
||||
<maskFunctions> ::= "MASK_DEFAULT" "(" [colname]=<cident> ")"
|
||||
| "MASK_HASH" "(" [colname]=<cident> ")"
|
||||
| "MASK_INNER" "(" [colname]=<cident> "," <wholenumber> "," <wholenumber> ")"
|
||||
| "MASK_NULL" "(" [colname]=<cident> ")"
|
||||
| "MASK_REPLACE" "(" [colname]=<cident> "," <propertyValue> ")"
|
||||
| "MASK_OUTER" "(" [colname]=<cident> "," <wholenumber> "," <wholenumber> ")"
|
||||
;
|
||||
|
||||
<timeConversionFunctions> ::= "TO_DATE" "(" [colname]=<cident> ")"
|
||||
| "TO_TIMESTAMP" "(" [colname]=<cident> ")"
|
||||
| "TO_UNIX_TIMESTAMP" "(" [colname]=<cident> ")"
|
||||
;
|
||||
|
||||
<timeuuidFunctions> ::= "MAX_TIMEUUID" "(" [colname]=<cident> ")"
|
||||
| "MIN_TIMEUUID" "(" [colname]=<cident> ")"
|
||||
;
|
||||
|
||||
<writetimeFunctions> ::= "MAX_WRITETIME" "(" [colname]=<cident> ")"
|
||||
| "MIN_WRITETIME" "(" [colname]=<cident> ")"
|
||||
| "WRITETIME" "(" [colname]=<cident> ")"
|
||||
;
|
||||
<operandFunctions> ::= <currentTimeFunctions> | <timeuuidFunctions>
|
||||
;
|
||||
|
||||
'''
|
||||
|
||||
|
||||
|
|
@ -867,7 +928,7 @@ def select_group_column_completer(ctxt, cass):
|
|||
|
||||
@completer_for('relation', 'token')
|
||||
def relation_token_word_completer(ctxt, cass):
|
||||
return ['TOKEN(']
|
||||
return ['TOKEN']
|
||||
|
||||
|
||||
@completer_for('relation', 'rel_tokname')
|
||||
|
|
@ -1001,7 +1062,7 @@ syntax_rules += r'''
|
|||
|
||||
@completer_for('updateStatement', 'updateopt')
|
||||
def update_option_completer(ctxt, cass):
|
||||
opts = set('TIMESTAMP TTL'.split())
|
||||
opts = {'TIMESTAMP', 'TTL'}
|
||||
for opt in ctxt.get_binding('updateopt', ()):
|
||||
opts.discard(opt.split()[0])
|
||||
return opts
|
||||
|
|
|
|||
|
|
@ -383,7 +383,7 @@ class Shell(cmd.Cmd):
|
|||
baseversion = baseversion[0:extra]
|
||||
if baseversion != build_version:
|
||||
print("WARNING: cqlsh was built against {}, but this server is {}. All features may not work!"
|
||||
.format(build_version, baseversion)) # ToDo: use file=sys.stderr)
|
||||
.format(build_version, baseversion), file=sys.stderr)
|
||||
|
||||
@property
|
||||
def batch_mode(self):
|
||||
|
|
|
|||
|
|
@ -114,7 +114,8 @@ class CqlshCompletionCase(BaseTestCase):
|
|||
|
||||
def _trycompletions_inner(self, inputstring, immediate='', choices=(),
|
||||
other_choices_ok=False,
|
||||
split_completed_lines=True):
|
||||
split_completed_lines=True,
|
||||
ignore_system_keyspaces=False):
|
||||
"""
|
||||
Test tab completion in cqlsh. Enters in the text in inputstring, then
|
||||
simulates a tab keypress to see what is immediately completed (this
|
||||
|
|
@ -132,17 +133,22 @@ class CqlshCompletionCase(BaseTestCase):
|
|||
self.assertEqual(completed, immediate, msg=msg)
|
||||
return
|
||||
|
||||
if ignore_system_keyspaces:
|
||||
completed = list(filter(lambda s: not s.startswith('system'), completed))
|
||||
|
||||
if other_choices_ok:
|
||||
self.assertEqual(set(choices), completed.intersection(choices))
|
||||
else:
|
||||
self.assertEqual(set(choices), set(completed))
|
||||
|
||||
def trycompletions(self, inputstring, immediate='', choices=(),
|
||||
other_choices_ok=False, split_completed_lines=True):
|
||||
other_choices_ok=False, split_completed_lines=True,
|
||||
ignore_system_keyspaces=False):
|
||||
try:
|
||||
self._trycompletions_inner(inputstring, immediate, choices,
|
||||
other_choices_ok=other_choices_ok,
|
||||
split_completed_lines=split_completed_lines)
|
||||
split_completed_lines=split_completed_lines,
|
||||
ignore_system_keyspaces=ignore_system_keyspaces)
|
||||
finally:
|
||||
try:
|
||||
self.cqlsh.send(CTRL_C) # cancel any current line
|
||||
|
|
@ -175,7 +181,43 @@ class TestCqlshCompletion(CqlshCompletionCase):
|
|||
pass
|
||||
|
||||
def test_complete_in_select(self):
|
||||
pass
|
||||
self.trycompletions('SELECT ',
|
||||
choices=('*', '<colname>',
|
||||
'-', '<blobLiteral>', '<float>', '<wholenumber>', '<uuid>',
|
||||
'<identifier>', '<pgStringLiteral>', '<quotedStringLiteral>',
|
||||
'ABS', 'AVG', 'CAST', 'COUNT', 'DISTINCT',
|
||||
'EXP', 'JSON', 'LOG', 'LOG10',
|
||||
'MAP_KEYS', 'MAP_VALUES',
|
||||
'MIN', 'MAX',
|
||||
'MIN_WRITETIME', 'MAX_WRITETIME',
|
||||
'ROUND', 'SUM', 'TOKEN',
|
||||
'TO_DATE', 'TO_TIMESTAMP', 'TO_UNIX_TIMESTAMP',
|
||||
'TTL', 'WRITETIME',
|
||||
'COLLECTION_AVG', 'COLLECTION_COUNT', 'COLLECTION_MAX',
|
||||
'COLLECTION_MIN', 'COLLECTION_SUM',
|
||||
'MASK_DEFAULT', 'MASK_HASH', 'MASK_INNER', 'MASK_NULL',
|
||||
'MASK_OUTER', 'MASK_REPLACE',
|
||||
'[', '{', 'false', 'true', 'NULL'
|
||||
),
|
||||
other_choices_ok=True
|
||||
)
|
||||
|
||||
def test_complete_in_select_where(self):
|
||||
self.trycompletions('SELECT * FROM system.peers WHERE ',
|
||||
choices=('<identifier>', '<quotedName>', 'peer', 'CURRENT_DATE()', 'CURRENT_TIME()',
|
||||
'CURRENT_TIMEUUID()', 'CURRENT_TIMESTAMP()', 'TOKEN',
|
||||
'MIN_TIMEUUID', 'MAX_TIMEUUID')
|
||||
)
|
||||
|
||||
def test_complete_in_select_where_equal(self):
|
||||
self.trycompletions('SELECT * FROM system.peers WHERE rack = ',
|
||||
choices=('-', '<blobLiteral>', '<float>', '<wholenumber>', '<uuid>',
|
||||
'<identifier>', '<pgStringLiteral>', '<quotedStringLiteral>',
|
||||
'[', '{', 'false', 'true', 'NULL',
|
||||
'TOKEN', 'MIN_TIMEUUID', 'MAX_TIMEUUID',
|
||||
'CURRENT_DATE()', 'CURRENT_TIME()', 'CURRENT_TIMEUUID()', 'CURRENT_TIMESTAMP()'
|
||||
)
|
||||
)
|
||||
|
||||
def test_complete_in_insert(self):
|
||||
self.trycompletions('INSERT INTO ',
|
||||
|
|
@ -376,7 +418,8 @@ class TestCqlshCompletion(CqlshCompletionCase):
|
|||
self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs'",
|
||||
choices=[',', 'WHERE'])
|
||||
self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE ",
|
||||
choices=['TOKEN(', 'lonelykey'])
|
||||
choices=['CURRENT_DATE()', 'CURRENT_TIME()', 'CURRENT_TIMESTAMP()',
|
||||
'CURRENT_TIMEUUID()', 'TOKEN', 'MIN_TIMEUUID', 'MAX_TIMEUUID', 'lonelykey'])
|
||||
|
||||
self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE lonel",
|
||||
immediate='ykey ')
|
||||
|
|
@ -385,7 +428,8 @@ class TestCqlshCompletion(CqlshCompletionCase):
|
|||
self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE lonelykey = 0.0 ",
|
||||
choices=['AND', 'IF', ';'])
|
||||
self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE lonelykey = 0.0 AND ",
|
||||
choices=['TOKEN(', 'lonelykey'])
|
||||
choices=['CURRENT_DATE()', 'CURRENT_TIME()', 'CURRENT_TIMESTAMP()',
|
||||
'CURRENT_TIMEUUID()', 'TOKEN', 'MIN_TIMEUUID', 'MAX_TIMEUUID', 'lonelykey'])
|
||||
|
||||
self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE TOKEN(lonelykey ",
|
||||
choices=[',', ')'])
|
||||
|
|
@ -397,7 +441,7 @@ class TestCqlshCompletion(CqlshCompletionCase):
|
|||
choices=['EXISTS', '<quotedName>', '<identifier>'])
|
||||
|
||||
self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE TOKEN(lonelykey) <= TOKEN(13) IF EXISTS ",
|
||||
choices=['>=', '!=', '<=', 'IN','[', ';', '=', '<', '>', '.', 'CONTAINS'])
|
||||
choices=['>=', '!=', '<=', 'IN', '[', ';', '=', '<', '>', '.', 'CONTAINS'])
|
||||
|
||||
self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE TOKEN(lonelykey) <= TOKEN(13) IF lonelykey ",
|
||||
choices=['>=', '!=', '<=', 'IN', '=', '<', '>', 'CONTAINS'])
|
||||
|
|
@ -461,10 +505,11 @@ class TestCqlshCompletion(CqlshCompletionCase):
|
|||
self.trycompletions('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 ',
|
||||
immediate='WHERE ')
|
||||
self.trycompletions('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE ',
|
||||
choices=['a', 'b', 'TOKEN('])
|
||||
choices=['a', 'b', 'CURRENT_DATE()', 'CURRENT_TIME()', 'CURRENT_TIMESTAMP()',
|
||||
'CURRENT_TIMEUUID()', 'MAX_TIMEUUID', 'MIN_TIMEUUID', 'TOKEN'])
|
||||
|
||||
self.trycompletions('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE a ',
|
||||
choices=['<=', '>=', 'BETWEEN', 'CONTAINS', 'IN', 'NOT' , '[', '=', '<', '>', '!='])
|
||||
choices=['<=', '>=', 'BETWEEN', 'CONTAINS', 'IN', 'NOT', '[', '=', '<', '>', '!='])
|
||||
|
||||
self.trycompletions('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE TOKEN(',
|
||||
immediate='a ')
|
||||
|
|
@ -476,7 +521,7 @@ class TestCqlshCompletion(CqlshCompletionCase):
|
|||
choices=['>=', '<=', '=', '<', '>'])
|
||||
self.trycompletions('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE TOKEN(a) >= ',
|
||||
choices=['false', 'true', '<pgStringLiteral>',
|
||||
'token(', '-', '<float>', 'TOKEN',
|
||||
'-', '<float>', 'TOKEN',
|
||||
'<identifier>', '<uuid>', '{', '[', 'NULL',
|
||||
'<quotedStringLiteral>', '<blobLiteral>',
|
||||
'<wholenumber>'])
|
||||
|
|
|
|||
Loading…
Reference in New Issue