This is an automated email from the ASF dual-hosted git repository.

brads pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git

commit 91bc34fbf86f65d8872c9610fdb7b63561190894
Author: Naren Sreedhara <naren.mys...@gmail.com>
AuthorDate: Thu Aug 1 10:43:46 2024 -0400

    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
---
 CHANGES.txt                                        |  1 +
 doc/modules/cassandra/examples/CQL/to_date.cql     |  1 +
 .../cassandra/pages/developing/cql/functions.adoc  | 11 +++-
 pylib/cqlshlib/cql3handling.py                     | 77 +++++++++++++++++++---
 pylib/cqlshlib/cqlshmain.py                        |  2 +-
 pylib/cqlshlib/test/test_cqlsh_completion.py       | 65 +++++++++++++++---
 6 files changed, 136 insertions(+), 21 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 5a60651cfc..764246421a 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -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)
diff --git a/doc/modules/cassandra/examples/CQL/to_date.cql 
b/doc/modules/cassandra/examples/CQL/to_date.cql
new file mode 100644
index 0000000000..160dcaab67
--- /dev/null
+++ b/doc/modules/cassandra/examples/CQL/to_date.cql
@@ -0,0 +1 @@
+SELECT id, to_date(create_ts) FROM myTable
diff --git a/doc/modules/cassandra/pages/developing/cql/functions.adoc 
b/doc/modules/cassandra/pages/developing/cql/functions.adoc
index 75786de271..d74beb1446 100644
--- a/doc/modules/cassandra/pages/developing/cql/functions.adoc
+++ b/doc/modules/cassandra/pages/developing/cql/functions.adoc
@@ -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
diff --git a/pylib/cqlshlib/cql3handling.py b/pylib/cqlshlib/cql3handling.py
index 457582f3d3..101c39ca6c 100644
--- a/pylib/cqlshlib/cql3handling.py
+++ b/pylib/cqlshlib/cql3handling.py
@@ -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
diff --git a/pylib/cqlshlib/cqlshmain.py b/pylib/cqlshlib/cqlshmain.py
index 2cac58ef22..2bc79f3c37 100755
--- a/pylib/cqlshlib/cqlshmain.py
+++ b/pylib/cqlshlib/cqlshmain.py
@@ -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):
diff --git a/pylib/cqlshlib/test/test_cqlsh_completion.py 
b/pylib/cqlshlib/test/test_cqlsh_completion.py
index 112474e7c7..53ab1908ed 100644
--- a/pylib/cqlshlib/test/test_cqlsh_completion.py
+++ b/pylib/cqlshlib/test/test_cqlsh_completion.py
@@ -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>'])


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org
For additional commands, e-mail: commits-h...@cassandra.apache.org

Reply via email to