Преглед изворни кода

HUE-9370 [editor] Add details about UDF argument locations to the generic parser and its relatives

Johan Ahlen пре 5 година
родитељ
комит
4a3ca309d0
33 измењених фајлова са 4607 додато и 545 уклоњено
  1. 12 9
      desktop/core/src/desktop/js/parse/jison/sql/calcite/sql_main.jison
  2. 9 9
      desktop/core/src/desktop/js/parse/jison/sql/generic/sql_main.jison
  3. 85 18
      desktop/core/src/desktop/js/parse/jison/sql/generic/sql_udf.jison
  4. 99 17
      desktop/core/src/desktop/js/parse/jison/sql/generic/sql_valueExpression.jison
  5. 191 30
      desktop/core/src/desktop/js/parse/sql/calcite/calciteAutocompleteParser.js
  6. 191 30
      desktop/core/src/desktop/js/parse/sql/calcite/calciteSyntaxParser.js
  7. 24 0
      desktop/core/src/desktop/js/parse/sql/calcite/test/calciteAutocompleteParser.Locations.test.js
  8. 118 2
      desktop/core/src/desktop/js/parse/sql/calcite/test/calciteAutocompleteParser.Select.test.js
  9. 186 30
      desktop/core/src/desktop/js/parse/sql/druid/druidAutocompleteParser.js
  10. 186 30
      desktop/core/src/desktop/js/parse/sql/druid/druidSyntaxParser.js
  11. 24 0
      desktop/core/src/desktop/js/parse/sql/druid/test/druidAutocompleteParser.Locations.test.js
  12. 118 2
      desktop/core/src/desktop/js/parse/sql/druid/test/druidAutocompleteParser.Select.test.js
  13. 186 30
      desktop/core/src/desktop/js/parse/sql/elasticsearch/elasticsearchAutocompleteParser.js
  14. 186 30
      desktop/core/src/desktop/js/parse/sql/elasticsearch/elasticsearchSyntaxParser.js
  15. 24 0
      desktop/core/src/desktop/js/parse/sql/elasticsearch/test/elasticsearchAutocompleteParser.Locations.test.js
  16. 118 2
      desktop/core/src/desktop/js/parse/sql/elasticsearch/test/elasticsearchAutocompleteParser.Select.test.js
  17. 186 30
      desktop/core/src/desktop/js/parse/sql/flink/flinkAutocompleteParser.js
  18. 186 30
      desktop/core/src/desktop/js/parse/sql/flink/flinkSyntaxParser.js
  19. 24 0
      desktop/core/src/desktop/js/parse/sql/flink/test/flinkAutocompleteParser.Locations.test.js
  20. 118 2
      desktop/core/src/desktop/js/parse/sql/flink/test/flinkAutocompleteParser.Select.test.js
  21. 186 30
      desktop/core/src/desktop/js/parse/sql/generic/genericAutocompleteParser.js
  22. 186 30
      desktop/core/src/desktop/js/parse/sql/generic/genericSyntaxParser.js
  23. 24 0
      desktop/core/src/desktop/js/parse/sql/generic/test/genericAutocompleteParser.Locations.test.js
  24. 115 0
      desktop/core/src/desktop/js/parse/sql/generic/test/genericAutocompleteParser.Select.test.js
  25. 186 30
      desktop/core/src/desktop/js/parse/sql/ksql/ksqlAutocompleteParser.js
  26. 186 30
      desktop/core/src/desktop/js/parse/sql/ksql/ksqlSyntaxParser.js
  27. 24 0
      desktop/core/src/desktop/js/parse/sql/ksql/test/ksqlAutocompleteParser.Locations.test.js
  28. 822 42
      desktop/core/src/desktop/js/parse/sql/ksql/test/ksqlAutocompleteParser.Select.test.js
  29. 186 30
      desktop/core/src/desktop/js/parse/sql/phoenix/phoenixAutocompleteParser.js
  30. 186 30
      desktop/core/src/desktop/js/parse/sql/phoenix/phoenixSyntaxParser.js
  31. 24 0
      desktop/core/src/desktop/js/parse/sql/phoenix/test/phoenixAutocompleteParser.Locations.test.js
  32. 122 2
      desktop/core/src/desktop/js/parse/sql/phoenix/test/phoenixAutocompleteParser.Select.test.js
  33. 89 20
      desktop/core/src/desktop/js/parse/sql/sqlParseUtils.js

+ 12 - 9
desktop/core/src/desktop/js/parse/jison/sql/calcite/sql_main.jison

@@ -1410,6 +1410,9 @@ LimitClause
 
 LimitClause_EDIT
  : 'LIMIT' 'CURSOR'
+   {
+     parser.suggestKeywords([{ value: '10', weight: 10000 }, { value: '100', weight: 10000 }, { value: '1000', weight: 10000 }, { value: '5000', weight: 10000 }, { value: '10000', weight: 10000 }])
+   }
  ;
 
 SearchCondition
@@ -1509,7 +1512,7 @@ InValueList
 NonParenthesizedValueExpressionPrimary
  : UnsignedValueSpecification
  | ColumnOrArbitraryFunctionRef  -> { types: ['COLREF'], columnReference: $1.chain }
- | 'NULL'                        -> { types: [ 'NULL' ] }
+ | 'NULL'                        -> { types: [ 'NULL' ], text: $1 }
  ;
 
 NonParenthesizedValueExpressionPrimary_EDIT
@@ -1563,7 +1566,7 @@ UnsignedValueSpecification_EDIT
  ;
 
 UnsignedLiteral
- : UnsignedNumericLiteral  -> { types: [ 'NUMBER' ] }
+ : UnsignedNumericLiteral  -> { types: [ 'NUMBER' ], text: $1 }
  | GeneralLiteral
  ;
 
@@ -1584,9 +1587,9 @@ ExactNumericLiteral
  ;
 
 ApproximateNumericLiteral
- : UNSIGNED_INTEGER_E 'UNSIGNED_INTEGER'
- | '.' UNSIGNED_INTEGER_E 'UNSIGNED_INTEGER'
- | 'UNSIGNED_INTEGER' '.' UNSIGNED_INTEGER_E 'UNSIGNED_INTEGER'
+ : UNSIGNED_INTEGER_E 'UNSIGNED_INTEGER'                        -> $1 + $2
+ | '.' UNSIGNED_INTEGER_E 'UNSIGNED_INTEGER'                    -> $1 + $2 + $3
+ | 'UNSIGNED_INTEGER' '.' UNSIGNED_INTEGER_E 'UNSIGNED_INTEGER' -> $1 + $2 + $3 + $4
  ;
 
 GeneralLiteral
@@ -1594,7 +1597,7 @@ GeneralLiteral
    {
      if (/\$\{[^}]*\}/.test($1)) {
        parser.addVariableLocation(@1, $1);
-       $$ = { types: [ 'STRING' ], columnReference: [{ name: $1 }] }
+       $$ = { types: [ 'STRING' ], columnReference: [{ name: $1 }], text: "'" + $1 + "'" }
      } else {
        $$ = { types: [ 'STRING' ] }
      }
@@ -1603,12 +1606,12 @@ GeneralLiteral
    {
      if (/\$\{[^}]*\}/.test($1)) {
        parser.addVariableLocation(@1, $1);
-       $$ = { types: [ 'STRING' ], columnReference: [{ name: $1 }] }
+       $$ = { types: [ 'STRING' ], columnReference: [{ name: $1 }], text: '"' + $1 + '"' }
      } else {
-       $$ = { types: [ 'STRING' ] }
+       $$ = { types: [ 'STRING' ], text: "'" + $1 + "'" }
      }
    }
- | TruthValue         -> { types: [ 'BOOLEAN' ] }
+ | TruthValue         -> { types: [ 'BOOLEAN' ], text: $1 }
  ;
 
 GeneralLiteral_EDIT

+ 9 - 9
desktop/core/src/desktop/js/parse/jison/sql/generic/sql_main.jison

@@ -1509,7 +1509,7 @@ InValueList
 NonParenthesizedValueExpressionPrimary
  : UnsignedValueSpecification
  | ColumnOrArbitraryFunctionRef  -> { types: ['COLREF'], columnReference: $1.chain }
- | 'NULL'                        -> { types: [ 'NULL' ] }
+ | 'NULL'                        -> { types: [ 'NULL' ], text: $1 }
  ;
 
 NonParenthesizedValueExpressionPrimary_EDIT
@@ -1563,7 +1563,7 @@ UnsignedValueSpecification_EDIT
  ;
 
 UnsignedLiteral
- : UnsignedNumericLiteral  -> { types: [ 'NUMBER' ] }
+ : UnsignedNumericLiteral  -> { types: [ 'NUMBER' ], text: $1 }
  | GeneralLiteral
  ;
 
@@ -1584,9 +1584,9 @@ ExactNumericLiteral
  ;
 
 ApproximateNumericLiteral
- : UNSIGNED_INTEGER_E 'UNSIGNED_INTEGER'
- | '.' UNSIGNED_INTEGER_E 'UNSIGNED_INTEGER'
- | 'UNSIGNED_INTEGER' '.' UNSIGNED_INTEGER_E 'UNSIGNED_INTEGER'
+ : UNSIGNED_INTEGER_E 'UNSIGNED_INTEGER'                        -> $1 + $2
+ | '.' UNSIGNED_INTEGER_E 'UNSIGNED_INTEGER'                    -> $1 + $2 + $3
+ | 'UNSIGNED_INTEGER' '.' UNSIGNED_INTEGER_E 'UNSIGNED_INTEGER' -> $1 + $2 + $3 + $4
  ;
 
 GeneralLiteral
@@ -1594,7 +1594,7 @@ GeneralLiteral
    {
      if (/\$\{[^}]*\}/.test($1)) {
        parser.addVariableLocation(@1, $1);
-       $$ = { types: [ 'STRING' ], columnReference: [{ name: $1 }] }
+       $$ = { types: [ 'STRING' ], columnReference: [{ name: $1 }], text: "'" + $1 + "'" }
      } else {
        $$ = { types: [ 'STRING' ] }
      }
@@ -1603,12 +1603,12 @@ GeneralLiteral
    {
      if (/\$\{[^}]*\}/.test($1)) {
        parser.addVariableLocation(@1, $1);
-       $$ = { types: [ 'STRING' ], columnReference: [{ name: $1 }] }
+       $$ = { types: [ 'STRING' ], columnReference: [{ name: $1 }], text: '"' + $1 + '"' }
      } else {
-       $$ = { types: [ 'STRING' ] }
+       $$ = { types: [ 'STRING' ], text: '"' + $1 + '"' }
      }
    }
- | TruthValue         -> { types: [ 'BOOLEAN' ] }
+ | TruthValue         -> { types: [ 'BOOLEAN' ], text: $1 }
  ;
 
 GeneralLiteral_EDIT

+ 85 - 18
desktop/core/src/desktop/js/parse/jison/sql/generic/sql_udf.jison

@@ -20,6 +20,7 @@ NonParenthesizedValueExpressionPrimary
      // We need to handle arbitrary UDFs here instead of inside UserDefinedFunction or there will be a conflict
      // with columnReference for functions like: db.udf(foo)
      var fn = $1.chain[$1.chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $2.expressions, $1.chain);
      $1.lastLoc.type = 'function';
      $1.lastLoc.function = fn;
      $1.lastLoc.location = {
@@ -54,6 +55,7 @@ NonParenthesizedValueExpressionPrimary_EDIT
  : ColumnOrArbitraryFunctionRef ArbitraryFunctionRightPart_EDIT
    {
      var fn = $1.chain[$1.chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $2.expressions, $1.chain);
      $1.lastLoc.type = 'function';
      $1.lastLoc.function = fn;
      $1.lastLoc.location = {
@@ -75,6 +77,7 @@ NonParenthesizedValueExpressionPrimary_EDIT
  | ArbitraryFunctionName ArbitraryFunctionRightPart_EDIT
    {
      parser.addFunctionLocation(@1, $1);
+     parser.addFunctionArgumentLocations($1, $2.expressions);
      if ($2.activePosition) {
        parser.applyArgumentTypesToSuggestions($1, $2.activePosition);
      }
@@ -111,6 +114,7 @@ ArbitraryFunction
  : RegularIdentifier ArbitraryFunctionRightPart
    {
      parser.addFunctionLocation(@1, $1);
+     parser.addFunctionArgumentLocations($1, $2.expressions);
      if ($2.expressions && $2.expressions.length) {
        $$ = { function: $1, expression: $2.expressions[$2.expressions.length - 1].expression, types: ['UDFREF'] }
      } else {
@@ -120,6 +124,7 @@ ArbitraryFunction
  | ArbitraryFunctionName ArbitraryFunctionRightPart
    {
      parser.addFunctionLocation(@1, $1);
+     parser.addFunctionArgumentLocations($1, $2.expressions);
      if ($2.expressions && $2.expressions.length) {
        $$ = { function: $1, expression: $2.expressions[$2.expressions.length - 1].expression, types: ['UDFREF'] }
      } else {
@@ -132,6 +137,7 @@ ArbitraryFunction_EDIT
  : RegularIdentifier ArbitraryFunctionRightPart_EDIT
    {
      parser.addFunctionLocation(@1, $1);
+     parser.addFunctionArgumentLocations($1, $2.expressions);
      if ($2.activePosition) {
        parser.applyArgumentTypesToSuggestions($1, $2.activePosition);
      }
@@ -140,6 +146,7 @@ ArbitraryFunction_EDIT
  | ArbitraryFunctionName ArbitraryFunctionRightPart_EDIT
    {
      parser.addFunctionLocation(@1, $1);
+     parser.addFunctionArgumentLocations($1, $2.expressions);
      if ($2.activePosition) {
        parser.applyArgumentTypesToSuggestions($1, $2.activePosition);
      }
@@ -165,7 +172,7 @@ ArbitraryFunctionRightPart_EDIT
      parser.valueExpressionSuggest();
      $$ = {
        activePosition: 1,
-       expressions: [{ expression: undefined, location: @2 }]
+       expressions: [{ expression: { text: '' }, location: @2 }]
      }
    }
  | '(' ArgumentList 'CURSOR' RightParenthesisOrError
@@ -227,7 +234,7 @@ ArgumentList_EDIT
      parser.valueExpressionSuggest();
      $$ = {
        activePosition: $1.activePosition + 1,
-       expressions: $1.expressions.concat([{ expression: undefined, location: @3 }])
+       expressions: $1.expressions.concat([{ expression: { text: '' }, location: @3 }])
      }
    }
  | ArgumentList ',' AnyCursor ',' ArgumentList
@@ -235,7 +242,7 @@ ArgumentList_EDIT
      parser.valueExpressionSuggest();
      $$ = {
        activePosition: $1.activePosition + 1,
-       expressions: $1.expressions.concat([{ expression: undefined, location: @3 }]).concat($5.expressions)
+       expressions: $1.expressions.concat([{ expression: { text: '' }, location: @3 }]).concat($5.expressions)
      }
    }
  | ArgumentList 'CURSOR' ',' ArgumentList
@@ -252,7 +259,7 @@ ArgumentList_EDIT
      $$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: @1 }].concat($3.expressions)
+       expressions: [{ expression: { text: '' }, location: @1 }].concat($3.expressions)
      };
    }
  | AnyCursor ','
@@ -261,7 +268,7 @@ ArgumentList_EDIT
      $$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: @1 }, { expression: undefined, location: @2 }]
+       expressions: [{ expression: { text: '' }, location: @1 }, { expression: { text: '' }, location: @2 }]
      };
    }
  | ',' AnyCursor
@@ -269,7 +276,7 @@ ArgumentList_EDIT
      parser.valueExpressionSuggest();
      $$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: @1 }, { expression: undefined, location: @2 }]
+       expressions: [{ expression: { text: '' }, location: @1 }, { expression: { text: '' }, location: @2 }]
      };
    }
  | ',' AnyCursor ',' ArgumentList
@@ -277,7 +284,7 @@ ArgumentList_EDIT
      parser.valueExpressionSuggest();
      $$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: @1 }, { expression: undefined, location: @2 }].concat($4.expressions)
+       expressions: [{ expression: { text: '' }, location: @1 }, { expression: { text: '' }, location: @2 }].concat($4.expressions)
      };
    }
  ;
@@ -295,24 +302,42 @@ AggregateFunction_EDIT
  ;
 
 AnalyticFunction
- : 'ANALYTIC' '(' ')'               -> { function: $1, types: ['UDFREF'] }
- | 'ANALYTIC' '(' ArgumentList ')'  -> { function: $1, expression: $3.expressions[$3.expressions.length - 1].expression, types: ['UDFREF'] }
+ : 'ANALYTIC' '(' ')'
+  {
+    parser.addFunctionLocation(@1, $1);
+    $$ = { function: $1, types: ['UDFREF'] }
+  }
+ | 'ANALYTIC' '(' ArgumentList ')'
+   {
+     parser.addFunctionLocation(@1, $1);
+     parser.addFunctionArgumentLocations($1, $3.expressions);
+     $$ = {
+       function: $1,
+       expression: $3.expressions[$3.expressions.length - 1].expression,
+       types: ['UDFREF']
+     }
+   }
  ;
 
 AnalyticFunction_EDIT
  : 'ANALYTIC' '(' AnyCursor RightParenthesisOrError
    {
+     parser.addFunctionLocation(@1, $1);
      parser.valueExpressionSuggest();
      parser.applyArgumentTypesToSuggestions($1, 1);
      $$ = { function: $1, types: ['UDFREF'] };
    }
  | 'ANALYTIC' '(' ArgumentList 'CURSOR' RightParenthesisOrError
    {
+     parser.addFunctionLocation(@1, $1);
+     parser.addFunctionArgumentLocations($1, $3.expressions);
      parser.suggestValueExpressionKeywords($3.expressions[$3.expressions.length - 1].expression);
      $$ = { function: $1, types: ['UDFREF'] };
    }
  | 'ANALYTIC' '(' ArgumentList_EDIT RightParenthesisOrError
    {
+     parser.addFunctionLocation(@1, $1);
+     parser.addFunctionArgumentLocations($1, $3.expressions);
      parser.applyArgumentTypesToSuggestions($1, $3.activePosition);
      $$ = { function: $1, types: ['UDFREF'] };
    }
@@ -337,7 +362,21 @@ OverClause_EDIT
  ;
 
 CastFunction
- : 'CAST' '(' ValueExpression 'AS' PrimitiveType ')'  -> { types: [ $5.toUpperCase() ] }
+ : 'CAST' '(' ValueExpression 'AS' PrimitiveType ')'
+   {
+     var expression = $3;
+     parser.extractExpressionText(expression, $3, $4, $5);
+     parser.addFunctionArgumentLocations($1, [{
+       expression: expression,
+       location: {
+         first_line: @3.first_line,
+         last_line: @5.last_line,
+         first_column: @3.first_column,
+         last_column: @3.last_column
+       }
+     }]);
+     $$ = { types: [ $5.toUpperCase() ] }
+   }
  | 'CAST' '(' ')'                                      -> { types: [ 'T' ] }
  ;
 
@@ -359,7 +398,7 @@ CastFunction_EDIT
    }
  | 'CAST' '(' ValueExpression_EDIT 'AS' PrimitiveType RightParenthesisOrError  -> { types: [ $5.toUpperCase() ] }
  | 'CAST' '(' ValueExpression_EDIT 'AS' RightParenthesisOrError                -> { types: [ 'T' ] }
- | 'CAST' '(' ValueExpression_EDIT RightParenthesisOrError                      -> { types: [ 'T' ] }
+ | 'CAST' '(' ValueExpression_EDIT RightParenthesisOrError                     -> { types: [ 'T' ] }
  | 'CAST' '(' ValueExpression 'CURSOR' PrimitiveType RightParenthesisOrError
    {
      parser.suggestValueExpressionKeywords($3, [{ value: 'AS', weight: 2 }]);
@@ -383,9 +422,23 @@ CastFunction_EDIT
  ;
 
 CountFunction
- : 'COUNT' '(' '*' ')'                                 -> { function: $1, types: ['UDFREF'] }
- | 'COUNT' '(' ')'                                     -> { function: $1, types: ['UDFREF'] }
- | 'COUNT' '(' OptionalAllOrDistinct ArgumentList ')'  -> { function: $1, types: ['UDFREF'] }
+ : 'COUNT' '(' '*' ')'
+   {
+     parser.addFunctionArgumentLocations($1, [{
+       expression: { text: $3 },
+       location: @3
+     }]);
+     $$ = { function: $1, types: ['UDFREF'] }
+   }
+ | 'COUNT' '(' ')'
+   {
+     $$ = { function: $1, types: ['UDFREF'] }
+   }
+ | 'COUNT' '(' OptionalAllOrDistinct ArgumentList ')'
+   {
+     parser.addFunctionArgumentLocations($1, $4.expressions);
+     $$ = { function: $1, types: ['UDFREF'] }
+   }
  ;
 
 CountFunction_EDIT
@@ -421,8 +474,15 @@ CountFunction_EDIT
  ;
 
 OtherAggregateFunction
- : OtherAggregateFunction_Type '(' OptionalAllOrDistinct ')'               -> { function: $1, types: ['UDFREF'] }
- | OtherAggregateFunction_Type '(' OptionalAllOrDistinct ArgumentList ')'  -> { function: $1, types: ['UDFREF'] }
+ : OtherAggregateFunction_Type '(' OptionalAllOrDistinct ')'
+   {
+     $$ = { function: $1, types: ['UDFREF'] };
+   }
+ | OtherAggregateFunction_Type '(' OptionalAllOrDistinct ArgumentList ')'
+   {
+     parser.addFunctionArgumentLocations($1, $4.expressions);
+     $$ = { function: $1, types: ['UDFREF'] };
+   }
  ;
 
 OtherAggregateFunction_EDIT
@@ -484,8 +544,15 @@ OtherAggregateFunction_Type
  ;
 
 SumFunction
- : 'SUM' '(' OptionalAllOrDistinct ValueExpression ')'  -> { function: $1, types: ['UDFREF'] }
- | 'SUM' '(' ')'                                        -> { function: $1, types: ['UDFREF'] }
+ : 'SUM' '(' OptionalAllOrDistinct ValueExpression ')'
+   {
+     parser.addFunctionArgumentLocations($1, $4.expressions);
+     $$ = { function: $1, types: ['UDFREF'] };
+   }
+ | 'SUM' '(' ')'
+   {
+     $$ = { function: $1, types: ['UDFREF'] }
+   }
  ;
 
 SumFunction_EDIT

+ 99 - 17
desktop/core/src/desktop/js/parse/jison/sql/generic/sql_valueExpression.jison

@@ -19,23 +19,46 @@ ValueExpression
    {
      // verifyType($2, 'BOOLEAN');
      $$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText($$, $1, $2);
    }
  | '!' ValueExpression
    {
      // verifyType($2, 'BOOLEAN');
      $$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText($$, $1, $2);
+   }
+ | '~' ValueExpression
+   {
+     $$ = $2;
+     parser.extractExpressionText($$, $1, $2);
    }
- | '~' ValueExpression                                                 -> $2
  | '-' ValueExpression %prec NEGATION
    {
      // verifyType($2, 'NUMBER');
      $$ = $2;
      $2.types = ['NUMBER'];
+     parser.extractExpressionText($$, $1, $2);
+   }
+ | ValueExpression 'IS' OptionalNot 'NULL'
+   {
+     $$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText($$, $1, $2, $3, $4);
+   }
+ | ValueExpression 'IS' OptionalNot 'TRUE'
+   {
+     $$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText($$, $1, $2, $3, $4);
+   }
+ | ValueExpression 'IS' OptionalNot 'FALSE'
+   {
+     $$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText($$, $1, $2, $3, $4);
+   }
+ | ValueExpression 'IS' OptionalNot 'DISTINCT' 'FROM' ValueExpression
+   {
+     $$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText($$, $1, $2, $3, $4, $5, $6);
    }
- | ValueExpression 'IS' OptionalNot 'NULL'                             -> { types: [ 'BOOLEAN' ] }
- | ValueExpression 'IS' OptionalNot 'TRUE'                             -> { types: [ 'BOOLEAN' ] }
- | ValueExpression 'IS' OptionalNot 'FALSE'                            -> { types: [ 'BOOLEAN' ] }
- | ValueExpression 'IS' OptionalNot 'DISTINCT' 'FROM' ValueExpression  -> { types: [ 'BOOLEAN' ] }
  ;
 
 ValueExpression_EDIT
@@ -145,21 +168,25 @@ ValueExpression
    {
      parser.addColRefToVariableIfExists($1, $3);
      $$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText($$, $1, $2, $3);
    }
  | ValueExpression '<' ValueExpression
    {
      parser.addColRefToVariableIfExists($1, $3);
      $$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText($$, $1, $2, $3);
    }
  | ValueExpression '>' ValueExpression
    {
      parser.addColRefToVariableIfExists($1, $3);
      $$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText($$, $1, $2, $3);
    }
  | ValueExpression 'COMPARISON_OPERATOR' ValueExpression
    {
      parser.addColRefToVariableIfExists($1, $3);
      $$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText($$, $1, $2, $3);
    }
  ;
 
@@ -326,8 +353,16 @@ ValueExpressionInSecondPart_EDIT
 // ------------------  BETWEEN ------------------
 
 ValueExpression
- : ValueExpression 'NOT' 'BETWEEN' ValueExpression 'BETWEEN_AND' ValueExpression  -> { types: [ 'BOOLEAN' ] }
- | ValueExpression 'BETWEEN' ValueExpression 'BETWEEN_AND' ValueExpression        -> { types: [ 'BOOLEAN' ] }
+ : ValueExpression 'NOT' 'BETWEEN' ValueExpression 'BETWEEN_AND' ValueExpression
+   {
+     $$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText($$, $1, $2, $3, $4, $5, $6);
+   }
+ | ValueExpression 'BETWEEN' ValueExpression 'BETWEEN_AND' ValueExpression
+   {
+     $$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText($$, $1, $2, $3, $4, $5);
+   }
  ;
 
 ValueExpression_EDIT
@@ -415,12 +450,14 @@ ValueExpression
      // verifyType($1, 'BOOLEAN');
      // verifyType($3, 'BOOLEAN');
      $$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText($$, $1, $2, $3);
    }
  | ValueExpression 'AND' ValueExpression
    {
      // verifyType($1, 'BOOLEAN');
      // verifyType($3, 'BOOLEAN');
      $$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText($$, $1, $2, $3);
    }
  ;
 
@@ -475,18 +512,21 @@ ValueExpression
      // verifyType($1, 'NUMBER');
      // verifyType($3, 'NUMBER');
      $$ = { types: [ 'NUMBER' ] };
+     parser.extractExpressionText($$, $1, $2, $3);
    }
  | ValueExpression '*' ValueExpression
    {
      // verifyType($1, 'NUMBER');
      // verifyType($3, 'NUMBER');
      $$ = { types: [ 'NUMBER' ] };
+     parser.extractExpressionText($$, $1, $2, $3);
    }
  | ValueExpression 'ARITHMETIC_OPERATOR' ValueExpression
    {
      // verifyType($1, 'NUMBER');
      // verifyType($3, 'NUMBER');
      $$ = { types: [ 'NUMBER' ] };
+     parser.extractExpressionText($$, $1, $2, $3);
    }
  ;
 
@@ -574,14 +614,34 @@ ValueExpression_EDIT
 // ------------------  LIKE, RLIKE and REGEXP ------------------
 
 ValueExpression
- : ValueExpression LikeRightPart          -> { types: [ 'BOOLEAN' ] }
- | ValueExpression 'NOT' LikeRightPart    -> { types: [ 'BOOLEAN' ] }
+ : ValueExpression LikeRightPart
+   {
+     $$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText($$, $1, $2);
+   }
+ | ValueExpression 'NOT' LikeRightPart
+   {
+     $$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText($$, $1, $2, $3);
+   }
  ;
 
 LikeRightPart
- : 'LIKE' ValueExpression             -> { suggestKeywords: ['NOT'] }
- | 'RLIKE' ValueExpression            -> { suggestKeywords: ['NOT'] }
- | 'REGEXP' ValueExpression           -> { suggestKeywords: ['NOT'] }
+ : 'LIKE' ValueExpression
+   {
+     $$ = { suggestKeywords: ['NOT'] }
+     parser.extractExpressionText($$, $1, $2);
+   }
+ | 'RLIKE' ValueExpression
+   {
+     $$ = { suggestKeywords: ['NOT'] }
+     parser.extractExpressionText($$, $1, $2);
+   }
+ | 'REGEXP' ValueExpression
+   {
+     $$ = { suggestKeywords: ['NOT'] }
+     parser.extractExpressionText($$, $1, $2);
+   }
  ;
 
 LikeRightPart_EDIT
@@ -630,8 +690,16 @@ ValueExpression_EDIT
 // ------------------  CASE, WHEN, THEN ------------------
 
 ValueExpression
- : 'CASE' CaseRightPart                  -> $2
- | 'CASE' ValueExpression CaseRightPart  -> $3
+ : 'CASE' CaseRightPart
+   {
+     $$ = $2;
+     parser.extractExpressionText($$, $1, $2);
+   }
+ | 'CASE' ValueExpression CaseRightPart
+   {
+     $$ = $3;
+     parser.extractExpressionText($$, $1, $2, $3);
+   }
  ;
 
 ValueExpression_EDIT
@@ -658,11 +726,16 @@ ValueExpression_EDIT
  ;
 
 CaseRightPart
- : CaseWhenThenList 'END'                         -> parser.findCaseType($1)
+ : CaseWhenThenList 'END'
+   {
+     $$ = parser.findCaseType($1)
+     parser.extractExpressionText($$, $1, $2);
+   }
  | CaseWhenThenList 'ELSE' ValueExpression 'END'
    {
      $1.caseTypes.push($3);
      $$ = parser.findCaseType($1);
+     parser.extractExpressionText($$, $1, $2, $3, $4);
    }
  ;
 
@@ -734,11 +807,16 @@ EndOrError
  ;
 
 CaseWhenThenList
- : CaseWhenThenListPartTwo                   -> { caseTypes: [ $1 ], lastType: $1 }
+ : CaseWhenThenListPartTwo
+   {
+     $$ = { caseTypes: [ $1 ], lastType: $1 }
+     parser.extractExpressionText($$, $1);
+   }
  | CaseWhenThenList CaseWhenThenListPartTwo
    {
      $1.caseTypes.push($2);
      $$ = { caseTypes: $1.caseTypes, lastType: $2 };
+     parser.extractExpressionText($$, $1, $2);
    }
  ;
 
@@ -754,7 +832,11 @@ CaseWhenThenList_EDIT
  ;
 
 CaseWhenThenListPartTwo
- : 'WHEN' ValueExpression 'THEN' ValueExpression  -> $4
+ : 'WHEN' ValueExpression 'THEN' ValueExpression
+   {
+     $$ = $4
+     parser.extractExpressionText($$, $1, $2, $3, $4);
+   }
  ;
 
 CaseWhenThenListPartTwo_EDIT

+ 191 - 30
desktop/core/src/desktop/js/parse/sql/calcite/calciteAutocompleteParser.js

@@ -606,7 +606,7 @@ case 285:
    }
  
 break;
-case 291: case 292: case 293: case 648: case 676: case 697: case 710: case 714: case 738: case 763: case 764: case 845: case 847: case 911: case 921: case 928: case 940: case 1029: case 1202: case 1240: case 1241:
+case 291: case 292: case 293: case 648: case 676: case 697: case 710: case 714: case 845: case 847: case 911: case 921: case 928: case 940: case 1029: case 1202: case 1240:
 this.$ = $$[$0];
 break;
 case 294: case 295:
@@ -1222,11 +1222,23 @@ case 723:
 
     this.$ = { suggestKeywords: ['ASC', 'DESC'] };
   
+break;
+case 732:
+
+     parser.suggestKeywords([{ value: '10', weight: 10000 }, { value: '100', weight: 10000 }, { value: '1000', weight: 10000 }, { value: '5000', weight: 10000 }, { value: '10000', weight: 10000 }])
+   
 break;
 case 736: case 737:
 
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 738: case 763:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 739:
@@ -1234,10 +1246,20 @@ case 739:
      // verifyType($$[$0], 'NUMBER');
      this.$ = $$[$0];
      $$[$0].types = ['NUMBER'];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
-case 740: case 741: case 742: case 743: case 750: case 751: case 752: case 753: case 754: case 755: case 761: case 762: case 783: case 841: case 842: case 900:
-this.$ = { types: [ 'BOOLEAN' ] };
+case 740: case 741: case 742:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 743:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 744:
 
@@ -1250,6 +1272,22 @@ case 746: case 747: case 748: case 749:
 
      parser.addColRefToVariableIfExists($$[$0-2], $$[$0]);
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 750: case 751: case 752: case 753: case 783: case 841: case 842:
+this.$ = { types: [ 'BOOLEAN' ] };
+break;
+case 754:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 755:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 756: case 757:
@@ -1257,6 +1295,7 @@ case 756: case 757:
      // verifyType($$[$0-2], 'BOOLEAN');
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 758: case 759: case 760:
@@ -1264,6 +1303,25 @@ case 758: case 759: case 760:
      // verifyType($$[$0-2], 'NUMBER');
      // verifyType($$[$0], 'NUMBER');
      this.$ = { types: [ 'NUMBER' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 761:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 762:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 764:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 766:
@@ -1677,13 +1735,14 @@ case 868:
 this.$ = { types: ['COLREF'], columnReference: $$[$0].chain };
 break;
 case 869:
-this.$ = { types: [ 'NULL' ] };
+this.$ = { types: [ 'NULL' ], text: $$[$0] };
 break;
 case 870:
 
      // We need to handle arbitrary UDFs here instead of inside UserDefinedFunction or there will be a conflict
      // with columnReference for functions like: db.udf(foo)
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1725,6 +1784,7 @@ break;
 case 875:
 
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1747,6 +1807,7 @@ break;
 case 876: case 1097: case 1098:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].activePosition) {
        parser.applyArgumentTypesToSuggestions($$[$0-1], $$[$0].activePosition);
      }
@@ -1770,23 +1831,39 @@ case 885:
    
 break;
 case 886:
-this.$ = { types: [ 'NUMBER' ] };
+this.$ = { types: [ 'NUMBER' ], text: $$[$0] };
 break;
-case 892: case 894:
+case 892: case 894: case 895:
 this.$ = $$[$0-1] + $$[$0];
 break;
-case 893:
+case 893: case 896:
 this.$ = $$[$0-2] + $$[$0-1] + $$[$0];
 break;
-case 898: case 899:
+case 897:
+this.$ = $$[$0-3] + $$[$0-2] + $$[$0-1] + $$[$0];
+break;
+case 898:
 
      if (/\$\{[^}]*\}/.test($$[$0])) {
        parser.addVariableLocation(_$[$0], $$[$0]);
-       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }] }
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: "'" + $$[$0] + "'" }
      } else {
        this.$ = { types: [ 'STRING' ] }
      }
    
+break;
+case 899:
+
+     if (/\$\{[^}]*\}/.test($$[$0])) {
+       parser.addVariableLocation(_$[$0], $$[$0]);
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: '"' + $$[$0] + '"' }
+     } else {
+       this.$ = { types: [ 'STRING' ], text: "'" + $$[$0] + "'" }
+     }
+   
+break;
+case 900:
+this.$ = { types: [ 'BOOLEAN' ], text: $$[$0] };
 break;
 case 901:
 
@@ -2258,6 +2335,7 @@ break;
 case 1095: case 1096:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].expressions && $$[$0].expressions.length) {
        this.$ = { function: $$[$0-1], expression: $$[$0].expressions[$$[$0].expressions.length - 1].expression, types: ['UDFREF'] }
      } else {
@@ -2270,7 +2348,7 @@ case 1105:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }]
      }
    
 break;
@@ -2317,7 +2395,7 @@ case 1114:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-2].activePosition + 1,
-       expressions: $$[$0-2].expressions.concat([{ expression: undefined, location: _$[$0] }])
+       expressions: $$[$0-2].expressions.concat([{ expression: { text: '' }, location: _$[$0] }])
      }
    
 break;
@@ -2326,7 +2404,7 @@ case 1115:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-4].activePosition + 1,
-       expressions: $$[$0-4].expressions.concat([{ expression: undefined, location: _$[$0-2] }]).concat($$[$0].expressions)
+       expressions: $$[$0-4].expressions.concat([{ expression: { text: '' }, location: _$[$0-2] }]).concat($$[$0].expressions)
      }
    
 break;
@@ -2345,7 +2423,7 @@ case 1117:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
@@ -2355,7 +2433,7 @@ case 1118:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2364,7 +2442,7 @@ case 1119:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2373,18 +2451,30 @@ case 1120:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-3] }, { expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-3] }, { expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
-case 1127: case 1151: case 1170:
-this.$ = { function: $$[$0-2], types: ['UDFREF'] };
+case 1127:
+
+    parser.addFunctionLocation(_$[$0-2], $$[$0-2]);
+    this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+  
 break;
 case 1128:
-this.$ = { function: $$[$0-3], expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression, types: ['UDFREF'] };
+
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
+     this.$ = {
+       function: $$[$0-3],
+       expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression,
+       types: ['UDFREF']
+     }
+   
 break;
 case 1129:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
      parser.valueExpressionSuggest();
      parser.applyArgumentTypesToSuggestions($$[$0-3], 1);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
@@ -2392,18 +2482,35 @@ case 1129:
 break;
 case 1130:
 
+     parser.addFunctionLocation(_$[$0-4], $$[$0-4]);
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-2].expressions);
      parser.suggestValueExpressionKeywords($$[$0-2].expressions[$$[$0-2].expressions.length - 1].expression);
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
 break;
 case 1131:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
      parser.applyArgumentTypesToSuggestions($$[$0-3], $$[$0-1].activePosition);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
    
 break;
-case 1138: case 1143:
-this.$ = { types: [ $$[$0-1].toUpperCase() ] };
+case 1138:
+
+     var expression = $$[$0-3];
+     parser.extractExpressionText(expression, $$[$0-3], $$[$0-2], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-5], [{
+       expression: expression,
+       location: {
+         first_line: _$[$0-3].first_line,
+         last_line: _$[$0-1].last_line,
+         first_column: _$[$0-3].first_column,
+         last_column: _$[$0-3].last_column
+       }
+     }]);
+     this.$ = { types: [ $$[$0-1].toUpperCase() ] }
+   
 break;
 case 1140:
 
@@ -2416,6 +2523,9 @@ case 1141: case 1142:
      parser.valueExpressionSuggest();
      this.$ = { types: [ 'T' ] };
    
+break;
+case 1143:
+this.$ = { types: [ $$[$0-1].toUpperCase() ] };
 break;
 case 1146:
 
@@ -2435,11 +2545,25 @@ case 1148: case 1149:
      this.$ = { types: [ 'T' ] };
    
 break;
-case 1150: case 1156:
-this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+case 1150:
+
+     parser.addFunctionArgumentLocations($$[$0-3], [{
+       expression: { text: $$[$0-1] },
+       location: _$[$0-1]
+     }]);
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] }
+   
 break;
-case 1152: case 1157: case 1169:
-this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+case 1151: case 1170:
+
+     this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+   
+break;
+case 1152:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] }
+   
 break;
 case 1153:
 
@@ -2472,6 +2596,17 @@ case 1155:
      }
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
+break;
+case 1156:
+
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+   
+break;
+case 1157: case 1169:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+   
 break;
 case 1158:
 
@@ -2572,7 +2707,10 @@ case 1209:
 this.$ = { inValueEdit: true, cursorAtStart: true };
 break;
 case 1210: case 1211: case 1212:
-this.$ = { suggestKeywords: ['NOT'] };
+
+     this.$ = { suggestKeywords: ['NOT'] }
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
 case 1216: case 1217: case 1218:
 
@@ -2581,14 +2719,21 @@ case 1216: case 1217: case 1218:
      this.$ = { types: ['BOOLEAN'] }
    
 break;
-case 1219: case 1221:
-this.$ = parser.findCaseType($$[$0-1]);
+case 1219:
+
+     this.$ = parser.findCaseType($$[$0-1])
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
-case 1220: case 1223:
+case 1220:
 
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
+break;
+case 1221:
+this.$ = parser.findCaseType($$[$0-1]);
 break;
 case 1222:
 
@@ -2596,6 +2741,12 @@ case 1222:
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
    
+break;
+case 1223:
+
+     $$[$0-3].caseTypes.push($$[$0-1]);
+     this.$ = parser.findCaseType($$[$0-3]);
+   
 break;
 case 1224:
 this.$ = parser.findCaseType($$[$0-2]);
@@ -2654,18 +2805,28 @@ case 1231:
    
 break;
 case 1234:
-this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] };
+
+     this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] }
+     parser.extractExpressionText(this.$, $$[$0]);
+   
 break;
 case 1235:
 
      $$[$0-1].caseTypes.push($$[$0]);
      this.$ = { caseTypes: $$[$0-1].caseTypes, lastType: $$[$0] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 1239:
 
      parser.suggestValueExpressionKeywords($$[$0-2], ['WHEN']);
    
+break;
+case 1241:
+
+     this.$ = $$[$0]
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 1242:
 this.$ = { caseTypes: [{ types: ['T'] }], suggestFilters: $$[$0].suggestFilters };

+ 191 - 30
desktop/core/src/desktop/js/parse/sql/calcite/calciteSyntaxParser.js

@@ -811,7 +811,7 @@ case 582:
    }
  
 break;
-case 583: case 621: case 642: case 655: case 659: case 683: case 708: case 709: case 790: case 792: case 856: case 866: case 873: case 885: case 988: case 1163: case 1201: case 1202:
+case 583: case 621: case 642: case 655: case 659: case 790: case 792: case 856: case 866: case 873: case 885: case 988: case 1163: case 1201:
 this.$ = $$[$0];
 break;
 case 586:
@@ -1172,11 +1172,23 @@ case 668:
 
     this.$ = { suggestKeywords: ['ASC', 'DESC'] };
   
+break;
+case 677:
+
+     parser.suggestKeywords([{ value: '10', weight: 10000 }, { value: '100', weight: 10000 }, { value: '1000', weight: 10000 }, { value: '5000', weight: 10000 }, { value: '10000', weight: 10000 }])
+   
 break;
 case 681: case 682:
 
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 683: case 708:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 684:
@@ -1184,10 +1196,20 @@ case 684:
      // verifyType($$[$0], 'NUMBER');
      this.$ = $$[$0];
      $$[$0].types = ['NUMBER'];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
-case 685: case 686: case 687: case 688: case 695: case 696: case 697: case 698: case 699: case 700: case 706: case 707: case 728: case 786: case 787: case 845:
-this.$ = { types: [ 'BOOLEAN' ] };
+case 685: case 686: case 687:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 688:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 689:
 
@@ -1200,6 +1222,22 @@ case 691: case 692: case 693: case 694:
 
      parser.addColRefToVariableIfExists($$[$0-2], $$[$0]);
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 695: case 696: case 697: case 698: case 728: case 786: case 787:
+this.$ = { types: [ 'BOOLEAN' ] };
+break;
+case 699:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 700:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 701: case 702:
@@ -1207,6 +1245,7 @@ case 701: case 702:
      // verifyType($$[$0-2], 'BOOLEAN');
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 703: case 704: case 705:
@@ -1214,6 +1253,25 @@ case 703: case 704: case 705:
      // verifyType($$[$0-2], 'NUMBER');
      // verifyType($$[$0], 'NUMBER');
      this.$ = { types: [ 'NUMBER' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 706:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 707:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 709:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 711:
@@ -1627,13 +1685,14 @@ case 813:
 this.$ = { types: ['COLREF'], columnReference: $$[$0].chain };
 break;
 case 814:
-this.$ = { types: [ 'NULL' ] };
+this.$ = { types: [ 'NULL' ], text: $$[$0] };
 break;
 case 815:
 
      // We need to handle arbitrary UDFs here instead of inside UserDefinedFunction or there will be a conflict
      // with columnReference for functions like: db.udf(foo)
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1675,6 +1734,7 @@ break;
 case 820:
 
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1697,6 +1757,7 @@ break;
 case 821: case 1058: case 1059:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].activePosition) {
        parser.applyArgumentTypesToSuggestions($$[$0-1], $$[$0].activePosition);
      }
@@ -1720,23 +1781,39 @@ case 830:
    
 break;
 case 831:
-this.$ = { types: [ 'NUMBER' ] };
+this.$ = { types: [ 'NUMBER' ], text: $$[$0] };
 break;
-case 837: case 839:
+case 837: case 839: case 840:
 this.$ = $$[$0-1] + $$[$0];
 break;
-case 838:
+case 838: case 841:
 this.$ = $$[$0-2] + $$[$0-1] + $$[$0];
 break;
-case 843: case 844:
+case 842:
+this.$ = $$[$0-3] + $$[$0-2] + $$[$0-1] + $$[$0];
+break;
+case 843:
 
      if (/\$\{[^}]*\}/.test($$[$0])) {
        parser.addVariableLocation(_$[$0], $$[$0]);
-       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }] }
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: "'" + $$[$0] + "'" }
      } else {
        this.$ = { types: [ 'STRING' ] }
      }
    
+break;
+case 844:
+
+     if (/\$\{[^}]*\}/.test($$[$0])) {
+       parser.addVariableLocation(_$[$0], $$[$0]);
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: '"' + $$[$0] + '"' }
+     } else {
+       this.$ = { types: [ 'STRING' ], text: "'" + $$[$0] + "'" }
+     }
+   
+break;
+case 845:
+this.$ = { types: [ 'BOOLEAN' ], text: $$[$0] };
 break;
 case 846:
 
@@ -2219,6 +2296,7 @@ break;
 case 1056: case 1057:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].expressions && $$[$0].expressions.length) {
        this.$ = { function: $$[$0-1], expression: $$[$0].expressions[$$[$0].expressions.length - 1].expression, types: ['UDFREF'] }
      } else {
@@ -2231,7 +2309,7 @@ case 1066:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }]
      }
    
 break;
@@ -2278,7 +2356,7 @@ case 1075:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-2].activePosition + 1,
-       expressions: $$[$0-2].expressions.concat([{ expression: undefined, location: _$[$0] }])
+       expressions: $$[$0-2].expressions.concat([{ expression: { text: '' }, location: _$[$0] }])
      }
    
 break;
@@ -2287,7 +2365,7 @@ case 1076:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-4].activePosition + 1,
-       expressions: $$[$0-4].expressions.concat([{ expression: undefined, location: _$[$0-2] }]).concat($$[$0].expressions)
+       expressions: $$[$0-4].expressions.concat([{ expression: { text: '' }, location: _$[$0-2] }]).concat($$[$0].expressions)
      }
    
 break;
@@ -2306,7 +2384,7 @@ case 1078:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
@@ -2316,7 +2394,7 @@ case 1079:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2325,7 +2403,7 @@ case 1080:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2334,18 +2412,30 @@ case 1081:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-3] }, { expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-3] }, { expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
-case 1088: case 1112: case 1131:
-this.$ = { function: $$[$0-2], types: ['UDFREF'] };
+case 1088:
+
+    parser.addFunctionLocation(_$[$0-2], $$[$0-2]);
+    this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+  
 break;
 case 1089:
-this.$ = { function: $$[$0-3], expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression, types: ['UDFREF'] };
+
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
+     this.$ = {
+       function: $$[$0-3],
+       expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression,
+       types: ['UDFREF']
+     }
+   
 break;
 case 1090:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
      parser.valueExpressionSuggest();
      parser.applyArgumentTypesToSuggestions($$[$0-3], 1);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
@@ -2353,18 +2443,35 @@ case 1090:
 break;
 case 1091:
 
+     parser.addFunctionLocation(_$[$0-4], $$[$0-4]);
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-2].expressions);
      parser.suggestValueExpressionKeywords($$[$0-2].expressions[$$[$0-2].expressions.length - 1].expression);
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
 break;
 case 1092:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
      parser.applyArgumentTypesToSuggestions($$[$0-3], $$[$0-1].activePosition);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
    
 break;
-case 1099: case 1104:
-this.$ = { types: [ $$[$0-1].toUpperCase() ] };
+case 1099:
+
+     var expression = $$[$0-3];
+     parser.extractExpressionText(expression, $$[$0-3], $$[$0-2], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-5], [{
+       expression: expression,
+       location: {
+         first_line: _$[$0-3].first_line,
+         last_line: _$[$0-1].last_line,
+         first_column: _$[$0-3].first_column,
+         last_column: _$[$0-3].last_column
+       }
+     }]);
+     this.$ = { types: [ $$[$0-1].toUpperCase() ] }
+   
 break;
 case 1101:
 
@@ -2377,6 +2484,9 @@ case 1102: case 1103:
      parser.valueExpressionSuggest();
      this.$ = { types: [ 'T' ] };
    
+break;
+case 1104:
+this.$ = { types: [ $$[$0-1].toUpperCase() ] };
 break;
 case 1107:
 
@@ -2396,11 +2506,25 @@ case 1109: case 1110:
      this.$ = { types: [ 'T' ] };
    
 break;
-case 1111: case 1117:
-this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+case 1111:
+
+     parser.addFunctionArgumentLocations($$[$0-3], [{
+       expression: { text: $$[$0-1] },
+       location: _$[$0-1]
+     }]);
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] }
+   
 break;
-case 1113: case 1118: case 1130:
-this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+case 1112: case 1131:
+
+     this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+   
+break;
+case 1113:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] }
+   
 break;
 case 1114:
 
@@ -2433,6 +2557,17 @@ case 1116:
      }
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
+break;
+case 1117:
+
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+   
+break;
+case 1118: case 1130:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+   
 break;
 case 1119:
 
@@ -2533,7 +2668,10 @@ case 1170:
 this.$ = { inValueEdit: true, cursorAtStart: true };
 break;
 case 1171: case 1172: case 1173:
-this.$ = { suggestKeywords: ['NOT'] };
+
+     this.$ = { suggestKeywords: ['NOT'] }
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
 case 1177: case 1178: case 1179:
 
@@ -2542,14 +2680,21 @@ case 1177: case 1178: case 1179:
      this.$ = { types: ['BOOLEAN'] }
    
 break;
-case 1180: case 1182:
-this.$ = parser.findCaseType($$[$0-1]);
+case 1180:
+
+     this.$ = parser.findCaseType($$[$0-1])
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
-case 1181: case 1184:
+case 1181:
 
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
+break;
+case 1182:
+this.$ = parser.findCaseType($$[$0-1]);
 break;
 case 1183:
 
@@ -2557,6 +2702,12 @@ case 1183:
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
    
+break;
+case 1184:
+
+     $$[$0-3].caseTypes.push($$[$0-1]);
+     this.$ = parser.findCaseType($$[$0-3]);
+   
 break;
 case 1185:
 this.$ = parser.findCaseType($$[$0-2]);
@@ -2615,18 +2766,28 @@ case 1192:
    
 break;
 case 1195:
-this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] };
+
+     this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] }
+     parser.extractExpressionText(this.$, $$[$0]);
+   
 break;
 case 1196:
 
      $$[$0-1].caseTypes.push($$[$0]);
      this.$ = { caseTypes: $$[$0-1].caseTypes, lastType: $$[$0] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 1200:
 
      parser.suggestValueExpressionKeywords($$[$0-2], ['WHEN']);
    
+break;
+case 1202:
+
+     this.$ = $$[$0]
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 1203:
 this.$ = { caseTypes: [{ types: ['T'] }], suggestFilters: $$[$0].suggestFilters };

+ 24 - 0
desktop/core/src/desktop/js/parse/sql/calcite/test/calciteAutocompleteParser.Locations.test.js

@@ -64,6 +64,14 @@ describe('calciteAutocompleteParser.js locations', () => {
           location: { first_line: 1, last_line: 1, first_column: 8, last_column: 10 },
           function: 'cos'
         },
+        {
+          type: 'functionArgument',
+          location: { first_line: 1, last_line: 1, first_column: 12, last_column: 13 },
+          function: 'cos',
+          argumentPosition: 0,
+          identifierChain: [{ name: 'cos' }],
+          expression: { types: ['NUMBER'], text: '1' }
+        },
         {
           type: 'alias',
           source: 'column',
@@ -1038,6 +1046,14 @@ describe('calciteAutocompleteParser.js locations', () => {
             location: { first_line: 1, last_line: 1, first_column: 13, last_column: 15 },
             function: 'cos'
           },
+          {
+            type: 'functionArgument',
+            location: { first_line: 1, last_line: 1, first_column: 17, last_column: 22 },
+            function: 'cos',
+            argumentPosition: 0,
+            identifierChain: [{ name: 'cos' }],
+            expression: { types: ['COLREF'], columnReference: [{ name: 'boo' }, { name: 'a' }] }
+          },
           {
             type: 'table',
             location: { first_line: 1, last_line: 1, first_column: 17, last_column: 20 },
@@ -1280,6 +1296,14 @@ describe('calciteAutocompleteParser.js locations', () => {
           location: { first_line: 1, last_line: 1, first_column: 8, last_column: 12 },
           function: 'count'
         },
+        {
+          type: 'functionArgument',
+          location: { first_line: 1, last_line: 1, first_column: 14, last_column: 15 },
+          function: 'count',
+          argumentPosition: 0,
+          identifierChain: [{ name: 'count' }],
+          expression: { text: '*' }
+        },
         {
           type: 'table',
           location: { first_line: 1, last_line: 1, first_column: 22, last_column: 31 },

+ 118 - 2
desktop/core/src/desktop/js/parse/sql/calcite/test/calciteAutocompleteParser.Select.test.js

@@ -371,6 +371,97 @@ describe('calciteAutocompleteParser.js SELECT statements', () => {
   });
 
   describe('Select List Completion', () => {
+    it('should handle "select testUdf(not id), cos(-1), sin(1+id) from autocomp_test;"', () => {
+      assertAutoComplete({
+        beforeCursor: 'select testUdf(not id), cos(-1), sin(1+id) from autocomp_test;',
+        afterCursor: '',
+        noErrors: true,
+        containsKeywords: ['SELECT'],
+        expectedResult: {
+          locations: [
+            {
+              type: 'statement',
+              location: { first_line: 1, last_line: 1, first_column: 1, last_column: 62 }
+            },
+            {
+              type: 'selectList',
+              missing: false,
+              location: { first_line: 1, last_line: 1, first_column: 8, last_column: 43 }
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 8, last_column: 14 },
+              function: 'testudf'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 16, last_column: 22 },
+              function: 'testudf',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'testUdf' }],
+              expression: { types: ['BOOLEAN'], text: 'not id' }
+            },
+            {
+              type: 'column',
+              location: { first_line: 1, last_line: 1, first_column: 20, last_column: 22 },
+              identifierChain: [{ name: 'id' }],
+              qualified: false,
+              tables: [{ identifierChain: [{ name: 'autocomp_test' }] }]
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 25, last_column: 27 },
+              function: 'cos'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 29, last_column: 31 },
+              function: 'cos',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'cos' }],
+              expression: { types: ['NUMBER'], text: '- 1' }
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
+              function: 'sin'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 38, last_column: 42 },
+              function: 'sin',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'sin' }],
+              expression: { types: ['NUMBER'], text: '1 + id' }
+            },
+            {
+              type: 'column',
+              location: { first_line: 1, last_line: 1, first_column: 40, last_column: 42 },
+              identifierChain: [{ name: 'id' }],
+              qualified: false,
+              tables: [{ identifierChain: [{ name: 'autocomp_test' }] }]
+            },
+            {
+              type: 'table',
+              location: { first_line: 1, last_line: 1, first_column: 49, last_column: 62 },
+              identifierChain: [{ name: 'autocomp_test' }]
+            },
+            {
+              type: 'whereClause',
+              missing: true,
+              location: { first_line: 1, last_line: 1, first_column: 62, last_column: 62 }
+            },
+            {
+              type: 'limitClause',
+              missing: true,
+              location: { first_line: 1, last_line: 1, first_column: 62, last_column: 62 }
+            }
+          ],
+          lowerCase: true
+        }
+      });
+    });
+
     it('should handle "select count(*), tst.count, avg (id), avg from autocomp_test tst;"', () => {
       assertAutoComplete({
         beforeCursor: 'select count(*), tst.count, avg (id), avg from autocomp_test tst;',
@@ -393,6 +484,14 @@ describe('calciteAutocompleteParser.js SELECT statements', () => {
               location: { first_line: 1, last_line: 1, first_column: 8, last_column: 12 },
               function: 'count'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 14, last_column: 15 },
+              function: 'count',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'count' }],
+              expression: { text: '*' }
+            },
             {
               type: 'table',
               location: { first_line: 1, last_line: 1, first_column: 18, last_column: 21 },
@@ -410,6 +509,14 @@ describe('calciteAutocompleteParser.js SELECT statements', () => {
               location: { first_line: 1, last_line: 1, first_column: 29, last_column: 32 },
               function: 'avg'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
+              function: 'avg',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'avg' }],
+              expression: { types: ['COLREF'], columnReference: [{ name: 'id' }] }
+            },
             {
               type: 'column',
               location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
@@ -2192,6 +2299,14 @@ describe('calciteAutocompleteParser.js SELECT statements', () => {
               identifierChain: [{ name: 'db' }, { name: 'customUdf' }],
               function: 'customudf'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 21, last_column: 24 },
+              function: 'customudf',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'db' }, { name: 'customUdf' }],
+              expression: { types: ['COLREF'], columnReference: [{ name: 'col' }] }
+            },
             {
               type: 'column',
               location: { first_line: 1, last_line: 1, first_column: 21, last_column: 24 },
@@ -4638,13 +4753,14 @@ describe('calciteAutocompleteParser.js SELECT statements', () => {
   });
 
   describe('LIMIT clause', () => {
-    it('should not suggest anything for "SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT |"', () => {
+    it('should suggest values for "SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT |"', () => {
       assertAutoComplete({
         beforeCursor: 'SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT ',
         afterCursor: '',
         noErrors: true,
         expectedResult: {
-          lowerCase: false
+          lowerCase: false,
+          suggestKeywords: ['10', '100', '1000', '10000', '5000']
         }
       });
     });

+ 186 - 30
desktop/core/src/desktop/js/parse/sql/druid/druidAutocompleteParser.js

@@ -596,7 +596,7 @@ case 283:
      parser.suggestDatabases({ prependFrom: true, appendDot: true });
    
 break;
-case 289: case 290: case 291: case 646: case 674: case 695: case 708: case 712: case 736: case 761: case 762: case 843: case 845: case 909: case 919: case 926: case 938: case 1027: case 1200: case 1238: case 1239:
+case 289: case 290: case 291: case 646: case 674: case 695: case 708: case 712: case 843: case 845: case 909: case 919: case 926: case 938: case 1027: case 1200: case 1238:
 this.$ = $$[$0];
 break;
 case 292: case 293:
@@ -1222,6 +1222,13 @@ case 734: case 735:
 
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 736: case 761:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 737:
@@ -1229,10 +1236,20 @@ case 737:
      // verifyType($$[$0], 'NUMBER');
      this.$ = $$[$0];
      $$[$0].types = ['NUMBER'];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
-case 738: case 739: case 740: case 741: case 748: case 749: case 750: case 751: case 752: case 753: case 759: case 760: case 781: case 839: case 840: case 898:
-this.$ = { types: [ 'BOOLEAN' ] };
+case 738: case 739: case 740:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 741:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 742:
 
@@ -1245,6 +1262,22 @@ case 744: case 745: case 746: case 747:
 
      parser.addColRefToVariableIfExists($$[$0-2], $$[$0]);
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 748: case 749: case 750: case 751: case 781: case 839: case 840:
+this.$ = { types: [ 'BOOLEAN' ] };
+break;
+case 752:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 753:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 754: case 755:
@@ -1252,6 +1285,7 @@ case 754: case 755:
      // verifyType($$[$0-2], 'BOOLEAN');
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 756: case 757: case 758:
@@ -1259,6 +1293,25 @@ case 756: case 757: case 758:
      // verifyType($$[$0-2], 'NUMBER');
      // verifyType($$[$0], 'NUMBER');
      this.$ = { types: [ 'NUMBER' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 759:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 760:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 762:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 764:
@@ -1672,13 +1725,14 @@ case 866:
 this.$ = { types: ['COLREF'], columnReference: $$[$0].chain };
 break;
 case 867:
-this.$ = { types: [ 'NULL' ] };
+this.$ = { types: [ 'NULL' ], text: $$[$0] };
 break;
 case 868:
 
      // We need to handle arbitrary UDFs here instead of inside UserDefinedFunction or there will be a conflict
      // with columnReference for functions like: db.udf(foo)
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1720,6 +1774,7 @@ break;
 case 873:
 
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1742,6 +1797,7 @@ break;
 case 874: case 1095: case 1096:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].activePosition) {
        parser.applyArgumentTypesToSuggestions($$[$0-1], $$[$0].activePosition);
      }
@@ -1765,23 +1821,39 @@ case 883:
    
 break;
 case 884:
-this.$ = { types: [ 'NUMBER' ] };
+this.$ = { types: [ 'NUMBER' ], text: $$[$0] };
 break;
-case 890: case 892:
+case 890: case 892: case 893:
 this.$ = $$[$0-1] + $$[$0];
 break;
-case 891:
+case 891: case 894:
 this.$ = $$[$0-2] + $$[$0-1] + $$[$0];
 break;
-case 896: case 897:
+case 895:
+this.$ = $$[$0-3] + $$[$0-2] + $$[$0-1] + $$[$0];
+break;
+case 896:
 
      if (/\$\{[^}]*\}/.test($$[$0])) {
        parser.addVariableLocation(_$[$0], $$[$0]);
-       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }] }
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: "'" + $$[$0] + "'" }
      } else {
        this.$ = { types: [ 'STRING' ] }
      }
    
+break;
+case 897:
+
+     if (/\$\{[^}]*\}/.test($$[$0])) {
+       parser.addVariableLocation(_$[$0], $$[$0]);
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: '"' + $$[$0] + '"' }
+     } else {
+       this.$ = { types: [ 'STRING' ], text: '"' + $$[$0] + '"' }
+     }
+   
+break;
+case 898:
+this.$ = { types: [ 'BOOLEAN' ], text: $$[$0] };
 break;
 case 899:
 
@@ -2253,6 +2325,7 @@ break;
 case 1093: case 1094:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].expressions && $$[$0].expressions.length) {
        this.$ = { function: $$[$0-1], expression: $$[$0].expressions[$$[$0].expressions.length - 1].expression, types: ['UDFREF'] }
      } else {
@@ -2265,7 +2338,7 @@ case 1103:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }]
      }
    
 break;
@@ -2312,7 +2385,7 @@ case 1112:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-2].activePosition + 1,
-       expressions: $$[$0-2].expressions.concat([{ expression: undefined, location: _$[$0] }])
+       expressions: $$[$0-2].expressions.concat([{ expression: { text: '' }, location: _$[$0] }])
      }
    
 break;
@@ -2321,7 +2394,7 @@ case 1113:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-4].activePosition + 1,
-       expressions: $$[$0-4].expressions.concat([{ expression: undefined, location: _$[$0-2] }]).concat($$[$0].expressions)
+       expressions: $$[$0-4].expressions.concat([{ expression: { text: '' }, location: _$[$0-2] }]).concat($$[$0].expressions)
      }
    
 break;
@@ -2340,7 +2413,7 @@ case 1115:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
@@ -2350,7 +2423,7 @@ case 1116:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2359,7 +2432,7 @@ case 1117:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2368,18 +2441,30 @@ case 1118:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-3] }, { expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-3] }, { expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
-case 1125: case 1149: case 1168:
-this.$ = { function: $$[$0-2], types: ['UDFREF'] };
+case 1125:
+
+    parser.addFunctionLocation(_$[$0-2], $$[$0-2]);
+    this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+  
 break;
 case 1126:
-this.$ = { function: $$[$0-3], expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression, types: ['UDFREF'] };
+
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
+     this.$ = {
+       function: $$[$0-3],
+       expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression,
+       types: ['UDFREF']
+     }
+   
 break;
 case 1127:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
      parser.valueExpressionSuggest();
      parser.applyArgumentTypesToSuggestions($$[$0-3], 1);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
@@ -2387,18 +2472,35 @@ case 1127:
 break;
 case 1128:
 
+     parser.addFunctionLocation(_$[$0-4], $$[$0-4]);
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-2].expressions);
      parser.suggestValueExpressionKeywords($$[$0-2].expressions[$$[$0-2].expressions.length - 1].expression);
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
 break;
 case 1129:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
      parser.applyArgumentTypesToSuggestions($$[$0-3], $$[$0-1].activePosition);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
    
 break;
-case 1136: case 1141:
-this.$ = { types: [ $$[$0-1].toUpperCase() ] };
+case 1136:
+
+     var expression = $$[$0-3];
+     parser.extractExpressionText(expression, $$[$0-3], $$[$0-2], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-5], [{
+       expression: expression,
+       location: {
+         first_line: _$[$0-3].first_line,
+         last_line: _$[$0-1].last_line,
+         first_column: _$[$0-3].first_column,
+         last_column: _$[$0-3].last_column
+       }
+     }]);
+     this.$ = { types: [ $$[$0-1].toUpperCase() ] }
+   
 break;
 case 1138:
 
@@ -2411,6 +2513,9 @@ case 1139: case 1140:
      parser.valueExpressionSuggest();
      this.$ = { types: [ 'T' ] };
    
+break;
+case 1141:
+this.$ = { types: [ $$[$0-1].toUpperCase() ] };
 break;
 case 1144:
 
@@ -2430,11 +2535,25 @@ case 1146: case 1147:
      this.$ = { types: [ 'T' ] };
    
 break;
-case 1148: case 1154:
-this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+case 1148:
+
+     parser.addFunctionArgumentLocations($$[$0-3], [{
+       expression: { text: $$[$0-1] },
+       location: _$[$0-1]
+     }]);
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] }
+   
 break;
-case 1150: case 1155: case 1167:
-this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+case 1149: case 1168:
+
+     this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+   
+break;
+case 1150:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] }
+   
 break;
 case 1151:
 
@@ -2467,6 +2586,17 @@ case 1153:
      }
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
+break;
+case 1154:
+
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+   
+break;
+case 1155: case 1167:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+   
 break;
 case 1156:
 
@@ -2567,7 +2697,10 @@ case 1207:
 this.$ = { inValueEdit: true, cursorAtStart: true };
 break;
 case 1208: case 1209: case 1210:
-this.$ = { suggestKeywords: ['NOT'] };
+
+     this.$ = { suggestKeywords: ['NOT'] }
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
 case 1214: case 1215: case 1216:
 
@@ -2576,14 +2709,21 @@ case 1214: case 1215: case 1216:
      this.$ = { types: ['BOOLEAN'] }
    
 break;
-case 1217: case 1219:
-this.$ = parser.findCaseType($$[$0-1]);
+case 1217:
+
+     this.$ = parser.findCaseType($$[$0-1])
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
-case 1218: case 1221:
+case 1218:
 
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
+break;
+case 1219:
+this.$ = parser.findCaseType($$[$0-1]);
 break;
 case 1220:
 
@@ -2591,6 +2731,12 @@ case 1220:
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
    
+break;
+case 1221:
+
+     $$[$0-3].caseTypes.push($$[$0-1]);
+     this.$ = parser.findCaseType($$[$0-3]);
+   
 break;
 case 1222:
 this.$ = parser.findCaseType($$[$0-2]);
@@ -2649,18 +2795,28 @@ case 1229:
    
 break;
 case 1232:
-this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] };
+
+     this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] }
+     parser.extractExpressionText(this.$, $$[$0]);
+   
 break;
 case 1233:
 
      $$[$0-1].caseTypes.push($$[$0]);
      this.$ = { caseTypes: $$[$0-1].caseTypes, lastType: $$[$0] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 1237:
 
      parser.suggestValueExpressionKeywords($$[$0-2], ['WHEN']);
    
+break;
+case 1239:
+
+     this.$ = $$[$0]
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 1240:
 this.$ = { caseTypes: [{ types: ['T'] }], suggestFilters: $$[$0].suggestFilters };

+ 186 - 30
desktop/core/src/desktop/js/parse/sql/druid/druidSyntaxParser.js

@@ -801,7 +801,7 @@ case 580:
      parser.suggestDatabases({ prependFrom: true, appendDot: true });
    
 break;
-case 581: case 619: case 640: case 653: case 657: case 681: case 706: case 707: case 788: case 790: case 854: case 864: case 871: case 883: case 986: case 1161: case 1199: case 1200:
+case 581: case 619: case 640: case 653: case 657: case 788: case 790: case 854: case 864: case 871: case 883: case 986: case 1161: case 1199:
 this.$ = $$[$0];
 break;
 case 584:
@@ -1172,6 +1172,13 @@ case 679: case 680:
 
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 681: case 706:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 682:
@@ -1179,10 +1186,20 @@ case 682:
      // verifyType($$[$0], 'NUMBER');
      this.$ = $$[$0];
      $$[$0].types = ['NUMBER'];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
-case 683: case 684: case 685: case 686: case 693: case 694: case 695: case 696: case 697: case 698: case 704: case 705: case 726: case 784: case 785: case 843:
-this.$ = { types: [ 'BOOLEAN' ] };
+case 683: case 684: case 685:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 686:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 687:
 
@@ -1195,6 +1212,22 @@ case 689: case 690: case 691: case 692:
 
      parser.addColRefToVariableIfExists($$[$0-2], $$[$0]);
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 693: case 694: case 695: case 696: case 726: case 784: case 785:
+this.$ = { types: [ 'BOOLEAN' ] };
+break;
+case 697:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 698:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 699: case 700:
@@ -1202,6 +1235,7 @@ case 699: case 700:
      // verifyType($$[$0-2], 'BOOLEAN');
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 701: case 702: case 703:
@@ -1209,6 +1243,25 @@ case 701: case 702: case 703:
      // verifyType($$[$0-2], 'NUMBER');
      // verifyType($$[$0], 'NUMBER');
      this.$ = { types: [ 'NUMBER' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 704:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 705:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 707:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 709:
@@ -1622,13 +1675,14 @@ case 811:
 this.$ = { types: ['COLREF'], columnReference: $$[$0].chain };
 break;
 case 812:
-this.$ = { types: [ 'NULL' ] };
+this.$ = { types: [ 'NULL' ], text: $$[$0] };
 break;
 case 813:
 
      // We need to handle arbitrary UDFs here instead of inside UserDefinedFunction or there will be a conflict
      // with columnReference for functions like: db.udf(foo)
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1670,6 +1724,7 @@ break;
 case 818:
 
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1692,6 +1747,7 @@ break;
 case 819: case 1056: case 1057:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].activePosition) {
        parser.applyArgumentTypesToSuggestions($$[$0-1], $$[$0].activePosition);
      }
@@ -1715,23 +1771,39 @@ case 828:
    
 break;
 case 829:
-this.$ = { types: [ 'NUMBER' ] };
+this.$ = { types: [ 'NUMBER' ], text: $$[$0] };
 break;
-case 835: case 837:
+case 835: case 837: case 838:
 this.$ = $$[$0-1] + $$[$0];
 break;
-case 836:
+case 836: case 839:
 this.$ = $$[$0-2] + $$[$0-1] + $$[$0];
 break;
-case 841: case 842:
+case 840:
+this.$ = $$[$0-3] + $$[$0-2] + $$[$0-1] + $$[$0];
+break;
+case 841:
 
      if (/\$\{[^}]*\}/.test($$[$0])) {
        parser.addVariableLocation(_$[$0], $$[$0]);
-       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }] }
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: "'" + $$[$0] + "'" }
      } else {
        this.$ = { types: [ 'STRING' ] }
      }
    
+break;
+case 842:
+
+     if (/\$\{[^}]*\}/.test($$[$0])) {
+       parser.addVariableLocation(_$[$0], $$[$0]);
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: '"' + $$[$0] + '"' }
+     } else {
+       this.$ = { types: [ 'STRING' ], text: '"' + $$[$0] + '"' }
+     }
+   
+break;
+case 843:
+this.$ = { types: [ 'BOOLEAN' ], text: $$[$0] };
 break;
 case 844:
 
@@ -2214,6 +2286,7 @@ break;
 case 1054: case 1055:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].expressions && $$[$0].expressions.length) {
        this.$ = { function: $$[$0-1], expression: $$[$0].expressions[$$[$0].expressions.length - 1].expression, types: ['UDFREF'] }
      } else {
@@ -2226,7 +2299,7 @@ case 1064:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }]
      }
    
 break;
@@ -2273,7 +2346,7 @@ case 1073:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-2].activePosition + 1,
-       expressions: $$[$0-2].expressions.concat([{ expression: undefined, location: _$[$0] }])
+       expressions: $$[$0-2].expressions.concat([{ expression: { text: '' }, location: _$[$0] }])
      }
    
 break;
@@ -2282,7 +2355,7 @@ case 1074:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-4].activePosition + 1,
-       expressions: $$[$0-4].expressions.concat([{ expression: undefined, location: _$[$0-2] }]).concat($$[$0].expressions)
+       expressions: $$[$0-4].expressions.concat([{ expression: { text: '' }, location: _$[$0-2] }]).concat($$[$0].expressions)
      }
    
 break;
@@ -2301,7 +2374,7 @@ case 1076:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
@@ -2311,7 +2384,7 @@ case 1077:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2320,7 +2393,7 @@ case 1078:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2329,18 +2402,30 @@ case 1079:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-3] }, { expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-3] }, { expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
-case 1086: case 1110: case 1129:
-this.$ = { function: $$[$0-2], types: ['UDFREF'] };
+case 1086:
+
+    parser.addFunctionLocation(_$[$0-2], $$[$0-2]);
+    this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+  
 break;
 case 1087:
-this.$ = { function: $$[$0-3], expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression, types: ['UDFREF'] };
+
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
+     this.$ = {
+       function: $$[$0-3],
+       expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression,
+       types: ['UDFREF']
+     }
+   
 break;
 case 1088:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
      parser.valueExpressionSuggest();
      parser.applyArgumentTypesToSuggestions($$[$0-3], 1);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
@@ -2348,18 +2433,35 @@ case 1088:
 break;
 case 1089:
 
+     parser.addFunctionLocation(_$[$0-4], $$[$0-4]);
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-2].expressions);
      parser.suggestValueExpressionKeywords($$[$0-2].expressions[$$[$0-2].expressions.length - 1].expression);
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
 break;
 case 1090:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
      parser.applyArgumentTypesToSuggestions($$[$0-3], $$[$0-1].activePosition);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
    
 break;
-case 1097: case 1102:
-this.$ = { types: [ $$[$0-1].toUpperCase() ] };
+case 1097:
+
+     var expression = $$[$0-3];
+     parser.extractExpressionText(expression, $$[$0-3], $$[$0-2], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-5], [{
+       expression: expression,
+       location: {
+         first_line: _$[$0-3].first_line,
+         last_line: _$[$0-1].last_line,
+         first_column: _$[$0-3].first_column,
+         last_column: _$[$0-3].last_column
+       }
+     }]);
+     this.$ = { types: [ $$[$0-1].toUpperCase() ] }
+   
 break;
 case 1099:
 
@@ -2372,6 +2474,9 @@ case 1100: case 1101:
      parser.valueExpressionSuggest();
      this.$ = { types: [ 'T' ] };
    
+break;
+case 1102:
+this.$ = { types: [ $$[$0-1].toUpperCase() ] };
 break;
 case 1105:
 
@@ -2391,11 +2496,25 @@ case 1107: case 1108:
      this.$ = { types: [ 'T' ] };
    
 break;
-case 1109: case 1115:
-this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+case 1109:
+
+     parser.addFunctionArgumentLocations($$[$0-3], [{
+       expression: { text: $$[$0-1] },
+       location: _$[$0-1]
+     }]);
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] }
+   
 break;
-case 1111: case 1116: case 1128:
-this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+case 1110: case 1129:
+
+     this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+   
+break;
+case 1111:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] }
+   
 break;
 case 1112:
 
@@ -2428,6 +2547,17 @@ case 1114:
      }
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
+break;
+case 1115:
+
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+   
+break;
+case 1116: case 1128:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+   
 break;
 case 1117:
 
@@ -2528,7 +2658,10 @@ case 1168:
 this.$ = { inValueEdit: true, cursorAtStart: true };
 break;
 case 1169: case 1170: case 1171:
-this.$ = { suggestKeywords: ['NOT'] };
+
+     this.$ = { suggestKeywords: ['NOT'] }
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
 case 1175: case 1176: case 1177:
 
@@ -2537,14 +2670,21 @@ case 1175: case 1176: case 1177:
      this.$ = { types: ['BOOLEAN'] }
    
 break;
-case 1178: case 1180:
-this.$ = parser.findCaseType($$[$0-1]);
+case 1178:
+
+     this.$ = parser.findCaseType($$[$0-1])
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
-case 1179: case 1182:
+case 1179:
 
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
+break;
+case 1180:
+this.$ = parser.findCaseType($$[$0-1]);
 break;
 case 1181:
 
@@ -2552,6 +2692,12 @@ case 1181:
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
    
+break;
+case 1182:
+
+     $$[$0-3].caseTypes.push($$[$0-1]);
+     this.$ = parser.findCaseType($$[$0-3]);
+   
 break;
 case 1183:
 this.$ = parser.findCaseType($$[$0-2]);
@@ -2610,18 +2756,28 @@ case 1190:
    
 break;
 case 1193:
-this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] };
+
+     this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] }
+     parser.extractExpressionText(this.$, $$[$0]);
+   
 break;
 case 1194:
 
      $$[$0-1].caseTypes.push($$[$0]);
      this.$ = { caseTypes: $$[$0-1].caseTypes, lastType: $$[$0] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 1198:
 
      parser.suggestValueExpressionKeywords($$[$0-2], ['WHEN']);
    
+break;
+case 1200:
+
+     this.$ = $$[$0]
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 1201:
 this.$ = { caseTypes: [{ types: ['T'] }], suggestFilters: $$[$0].suggestFilters };

+ 24 - 0
desktop/core/src/desktop/js/parse/sql/druid/test/druidAutocompleteParser.Locations.test.js

@@ -64,6 +64,14 @@ describe('druidAutocompleteParser.js locations', () => {
           location: { first_line: 1, last_line: 1, first_column: 8, last_column: 10 },
           function: 'cos'
         },
+        {
+          type: 'functionArgument',
+          location: { first_line: 1, last_line: 1, first_column: 12, last_column: 13 },
+          function: 'cos',
+          argumentPosition: 0,
+          identifierChain: [{ name: 'cos' }],
+          expression: { types: ['NUMBER'], text: '1' }
+        },
         {
           type: 'alias',
           source: 'column',
@@ -1038,6 +1046,14 @@ describe('druidAutocompleteParser.js locations', () => {
             location: { first_line: 1, last_line: 1, first_column: 13, last_column: 15 },
             function: 'cos'
           },
+          {
+            type: 'functionArgument',
+            location: { first_line: 1, last_line: 1, first_column: 17, last_column: 22 },
+            function: 'cos',
+            argumentPosition: 0,
+            identifierChain: [{ name: 'cos' }],
+            expression: { types: ['COLREF'], columnReference: [{ name: 'boo' }, { name: 'a' }] }
+          },
           {
             type: 'table',
             location: { first_line: 1, last_line: 1, first_column: 17, last_column: 20 },
@@ -1280,6 +1296,14 @@ describe('druidAutocompleteParser.js locations', () => {
           location: { first_line: 1, last_line: 1, first_column: 8, last_column: 12 },
           function: 'count'
         },
+        {
+          type: 'functionArgument',
+          location: { first_line: 1, last_line: 1, first_column: 14, last_column: 15 },
+          function: 'count',
+          argumentPosition: 0,
+          identifierChain: [{ name: 'count' }],
+          expression: { text: '*' }
+        },
         {
           type: 'table',
           location: { first_line: 1, last_line: 1, first_column: 22, last_column: 31 },

+ 118 - 2
desktop/core/src/desktop/js/parse/sql/druid/test/druidAutocompleteParser.Select.test.js

@@ -371,6 +371,97 @@ describe('druidAutocompleteParser.js SELECT statements', () => {
   });
 
   describe('Select List Completion', () => {
+    it('should handle "select testUdf(not id), cos(-1), sin(1+id) from autocomp_test;"', () => {
+      assertAutoComplete({
+        beforeCursor: 'select testUdf(not id), cos(-1), sin(1+id) from autocomp_test;',
+        afterCursor: '',
+        noErrors: true,
+        containsKeywords: ['SELECT'],
+        expectedResult: {
+          locations: [
+            {
+              type: 'statement',
+              location: { first_line: 1, last_line: 1, first_column: 1, last_column: 62 }
+            },
+            {
+              type: 'selectList',
+              missing: false,
+              location: { first_line: 1, last_line: 1, first_column: 8, last_column: 43 }
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 8, last_column: 14 },
+              function: 'testudf'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 16, last_column: 22 },
+              function: 'testudf',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'testUdf' }],
+              expression: { types: ['BOOLEAN'], text: 'not id' }
+            },
+            {
+              type: 'column',
+              location: { first_line: 1, last_line: 1, first_column: 20, last_column: 22 },
+              identifierChain: [{ name: 'id' }],
+              qualified: false,
+              tables: [{ identifierChain: [{ name: 'autocomp_test' }] }]
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 25, last_column: 27 },
+              function: 'cos'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 29, last_column: 31 },
+              function: 'cos',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'cos' }],
+              expression: { types: ['NUMBER'], text: '- 1' }
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
+              function: 'sin'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 38, last_column: 42 },
+              function: 'sin',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'sin' }],
+              expression: { types: ['NUMBER'], text: '1 + id' }
+            },
+            {
+              type: 'column',
+              location: { first_line: 1, last_line: 1, first_column: 40, last_column: 42 },
+              identifierChain: [{ name: 'id' }],
+              qualified: false,
+              tables: [{ identifierChain: [{ name: 'autocomp_test' }] }]
+            },
+            {
+              type: 'table',
+              location: { first_line: 1, last_line: 1, first_column: 49, last_column: 62 },
+              identifierChain: [{ name: 'autocomp_test' }]
+            },
+            {
+              type: 'whereClause',
+              missing: true,
+              location: { first_line: 1, last_line: 1, first_column: 62, last_column: 62 }
+            },
+            {
+              type: 'limitClause',
+              missing: true,
+              location: { first_line: 1, last_line: 1, first_column: 62, last_column: 62 }
+            }
+          ],
+          lowerCase: true
+        }
+      });
+    });
+
     it('should handle "select count(*), tst.count, avg (id), avg from autocomp_test tst;"', () => {
       assertAutoComplete({
         beforeCursor: 'select count(*), tst.count, avg (id), avg from autocomp_test tst;',
@@ -393,6 +484,14 @@ describe('druidAutocompleteParser.js SELECT statements', () => {
               location: { first_line: 1, last_line: 1, first_column: 8, last_column: 12 },
               function: 'count'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 14, last_column: 15 },
+              function: 'count',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'count' }],
+              expression: { text: '*' }
+            },
             {
               type: 'table',
               location: { first_line: 1, last_line: 1, first_column: 18, last_column: 21 },
@@ -410,6 +509,14 @@ describe('druidAutocompleteParser.js SELECT statements', () => {
               location: { first_line: 1, last_line: 1, first_column: 29, last_column: 32 },
               function: 'avg'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
+              function: 'avg',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'avg' }],
+              expression: { types: ['COLREF'], columnReference: [{ name: 'id' }] }
+            },
             {
               type: 'column',
               location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
@@ -2192,6 +2299,14 @@ describe('druidAutocompleteParser.js SELECT statements', () => {
               identifierChain: [{ name: 'db' }, { name: 'customUdf' }],
               function: 'customudf'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 21, last_column: 24 },
+              function: 'customudf',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'db' }, { name: 'customUdf' }],
+              expression: { types: ['COLREF'], columnReference: [{ name: 'col' }] }
+            },
             {
               type: 'column',
               location: { first_line: 1, last_line: 1, first_column: 21, last_column: 24 },
@@ -4638,13 +4753,14 @@ describe('druidAutocompleteParser.js SELECT statements', () => {
   });
 
   describe('LIMIT clause', () => {
-    it('should not suggest anything for "SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT |"', () => {
+    it('should suggest values for "SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT |"', () => {
       assertAutoComplete({
         beforeCursor: 'SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT ',
         afterCursor: '',
         noErrors: true,
         expectedResult: {
-          lowerCase: false
+          lowerCase: false,
+          suggestKeywords: ['10', '100', '1000', '10000', '5000']
         }
       });
     });

+ 186 - 30
desktop/core/src/desktop/js/parse/sql/elasticsearch/elasticsearchAutocompleteParser.js

@@ -596,7 +596,7 @@ case 283:
      parser.suggestDatabases({ prependFrom: true, appendDot: true });
    
 break;
-case 289: case 290: case 291: case 646: case 674: case 695: case 708: case 712: case 736: case 761: case 762: case 843: case 845: case 909: case 919: case 926: case 938: case 1027: case 1200: case 1238: case 1239:
+case 289: case 290: case 291: case 646: case 674: case 695: case 708: case 712: case 843: case 845: case 909: case 919: case 926: case 938: case 1027: case 1200: case 1238:
 this.$ = $$[$0];
 break;
 case 292: case 293:
@@ -1222,6 +1222,13 @@ case 734: case 735:
 
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 736: case 761:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 737:
@@ -1229,10 +1236,20 @@ case 737:
      // verifyType($$[$0], 'NUMBER');
      this.$ = $$[$0];
      $$[$0].types = ['NUMBER'];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
-case 738: case 739: case 740: case 741: case 748: case 749: case 750: case 751: case 752: case 753: case 759: case 760: case 781: case 839: case 840: case 898:
-this.$ = { types: [ 'BOOLEAN' ] };
+case 738: case 739: case 740:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 741:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 742:
 
@@ -1245,6 +1262,22 @@ case 744: case 745: case 746: case 747:
 
      parser.addColRefToVariableIfExists($$[$0-2], $$[$0]);
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 748: case 749: case 750: case 751: case 781: case 839: case 840:
+this.$ = { types: [ 'BOOLEAN' ] };
+break;
+case 752:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 753:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 754: case 755:
@@ -1252,6 +1285,7 @@ case 754: case 755:
      // verifyType($$[$0-2], 'BOOLEAN');
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 756: case 757: case 758:
@@ -1259,6 +1293,25 @@ case 756: case 757: case 758:
      // verifyType($$[$0-2], 'NUMBER');
      // verifyType($$[$0], 'NUMBER');
      this.$ = { types: [ 'NUMBER' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 759:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 760:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 762:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 764:
@@ -1672,13 +1725,14 @@ case 866:
 this.$ = { types: ['COLREF'], columnReference: $$[$0].chain };
 break;
 case 867:
-this.$ = { types: [ 'NULL' ] };
+this.$ = { types: [ 'NULL' ], text: $$[$0] };
 break;
 case 868:
 
      // We need to handle arbitrary UDFs here instead of inside UserDefinedFunction or there will be a conflict
      // with columnReference for functions like: db.udf(foo)
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1720,6 +1774,7 @@ break;
 case 873:
 
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1742,6 +1797,7 @@ break;
 case 874: case 1095: case 1096:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].activePosition) {
        parser.applyArgumentTypesToSuggestions($$[$0-1], $$[$0].activePosition);
      }
@@ -1765,23 +1821,39 @@ case 883:
    
 break;
 case 884:
-this.$ = { types: [ 'NUMBER' ] };
+this.$ = { types: [ 'NUMBER' ], text: $$[$0] };
 break;
-case 890: case 892:
+case 890: case 892: case 893:
 this.$ = $$[$0-1] + $$[$0];
 break;
-case 891:
+case 891: case 894:
 this.$ = $$[$0-2] + $$[$0-1] + $$[$0];
 break;
-case 896: case 897:
+case 895:
+this.$ = $$[$0-3] + $$[$0-2] + $$[$0-1] + $$[$0];
+break;
+case 896:
 
      if (/\$\{[^}]*\}/.test($$[$0])) {
        parser.addVariableLocation(_$[$0], $$[$0]);
-       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }] }
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: "'" + $$[$0] + "'" }
      } else {
        this.$ = { types: [ 'STRING' ] }
      }
    
+break;
+case 897:
+
+     if (/\$\{[^}]*\}/.test($$[$0])) {
+       parser.addVariableLocation(_$[$0], $$[$0]);
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: '"' + $$[$0] + '"' }
+     } else {
+       this.$ = { types: [ 'STRING' ], text: '"' + $$[$0] + '"' }
+     }
+   
+break;
+case 898:
+this.$ = { types: [ 'BOOLEAN' ], text: $$[$0] };
 break;
 case 899:
 
@@ -2253,6 +2325,7 @@ break;
 case 1093: case 1094:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].expressions && $$[$0].expressions.length) {
        this.$ = { function: $$[$0-1], expression: $$[$0].expressions[$$[$0].expressions.length - 1].expression, types: ['UDFREF'] }
      } else {
@@ -2265,7 +2338,7 @@ case 1103:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }]
      }
    
 break;
@@ -2312,7 +2385,7 @@ case 1112:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-2].activePosition + 1,
-       expressions: $$[$0-2].expressions.concat([{ expression: undefined, location: _$[$0] }])
+       expressions: $$[$0-2].expressions.concat([{ expression: { text: '' }, location: _$[$0] }])
      }
    
 break;
@@ -2321,7 +2394,7 @@ case 1113:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-4].activePosition + 1,
-       expressions: $$[$0-4].expressions.concat([{ expression: undefined, location: _$[$0-2] }]).concat($$[$0].expressions)
+       expressions: $$[$0-4].expressions.concat([{ expression: { text: '' }, location: _$[$0-2] }]).concat($$[$0].expressions)
      }
    
 break;
@@ -2340,7 +2413,7 @@ case 1115:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
@@ -2350,7 +2423,7 @@ case 1116:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2359,7 +2432,7 @@ case 1117:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2368,18 +2441,30 @@ case 1118:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-3] }, { expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-3] }, { expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
-case 1125: case 1149: case 1168:
-this.$ = { function: $$[$0-2], types: ['UDFREF'] };
+case 1125:
+
+    parser.addFunctionLocation(_$[$0-2], $$[$0-2]);
+    this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+  
 break;
 case 1126:
-this.$ = { function: $$[$0-3], expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression, types: ['UDFREF'] };
+
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
+     this.$ = {
+       function: $$[$0-3],
+       expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression,
+       types: ['UDFREF']
+     }
+   
 break;
 case 1127:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
      parser.valueExpressionSuggest();
      parser.applyArgumentTypesToSuggestions($$[$0-3], 1);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
@@ -2387,18 +2472,35 @@ case 1127:
 break;
 case 1128:
 
+     parser.addFunctionLocation(_$[$0-4], $$[$0-4]);
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-2].expressions);
      parser.suggestValueExpressionKeywords($$[$0-2].expressions[$$[$0-2].expressions.length - 1].expression);
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
 break;
 case 1129:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
      parser.applyArgumentTypesToSuggestions($$[$0-3], $$[$0-1].activePosition);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
    
 break;
-case 1136: case 1141:
-this.$ = { types: [ $$[$0-1].toUpperCase() ] };
+case 1136:
+
+     var expression = $$[$0-3];
+     parser.extractExpressionText(expression, $$[$0-3], $$[$0-2], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-5], [{
+       expression: expression,
+       location: {
+         first_line: _$[$0-3].first_line,
+         last_line: _$[$0-1].last_line,
+         first_column: _$[$0-3].first_column,
+         last_column: _$[$0-3].last_column
+       }
+     }]);
+     this.$ = { types: [ $$[$0-1].toUpperCase() ] }
+   
 break;
 case 1138:
 
@@ -2411,6 +2513,9 @@ case 1139: case 1140:
      parser.valueExpressionSuggest();
      this.$ = { types: [ 'T' ] };
    
+break;
+case 1141:
+this.$ = { types: [ $$[$0-1].toUpperCase() ] };
 break;
 case 1144:
 
@@ -2430,11 +2535,25 @@ case 1146: case 1147:
      this.$ = { types: [ 'T' ] };
    
 break;
-case 1148: case 1154:
-this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+case 1148:
+
+     parser.addFunctionArgumentLocations($$[$0-3], [{
+       expression: { text: $$[$0-1] },
+       location: _$[$0-1]
+     }]);
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] }
+   
 break;
-case 1150: case 1155: case 1167:
-this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+case 1149: case 1168:
+
+     this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+   
+break;
+case 1150:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] }
+   
 break;
 case 1151:
 
@@ -2467,6 +2586,17 @@ case 1153:
      }
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
+break;
+case 1154:
+
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+   
+break;
+case 1155: case 1167:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+   
 break;
 case 1156:
 
@@ -2567,7 +2697,10 @@ case 1207:
 this.$ = { inValueEdit: true, cursorAtStart: true };
 break;
 case 1208: case 1209: case 1210:
-this.$ = { suggestKeywords: ['NOT'] };
+
+     this.$ = { suggestKeywords: ['NOT'] }
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
 case 1214: case 1215: case 1216:
 
@@ -2576,14 +2709,21 @@ case 1214: case 1215: case 1216:
      this.$ = { types: ['BOOLEAN'] }
    
 break;
-case 1217: case 1219:
-this.$ = parser.findCaseType($$[$0-1]);
+case 1217:
+
+     this.$ = parser.findCaseType($$[$0-1])
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
-case 1218: case 1221:
+case 1218:
 
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
+break;
+case 1219:
+this.$ = parser.findCaseType($$[$0-1]);
 break;
 case 1220:
 
@@ -2591,6 +2731,12 @@ case 1220:
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
    
+break;
+case 1221:
+
+     $$[$0-3].caseTypes.push($$[$0-1]);
+     this.$ = parser.findCaseType($$[$0-3]);
+   
 break;
 case 1222:
 this.$ = parser.findCaseType($$[$0-2]);
@@ -2649,18 +2795,28 @@ case 1229:
    
 break;
 case 1232:
-this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] };
+
+     this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] }
+     parser.extractExpressionText(this.$, $$[$0]);
+   
 break;
 case 1233:
 
      $$[$0-1].caseTypes.push($$[$0]);
      this.$ = { caseTypes: $$[$0-1].caseTypes, lastType: $$[$0] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 1237:
 
      parser.suggestValueExpressionKeywords($$[$0-2], ['WHEN']);
    
+break;
+case 1239:
+
+     this.$ = $$[$0]
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 1240:
 this.$ = { caseTypes: [{ types: ['T'] }], suggestFilters: $$[$0].suggestFilters };

+ 186 - 30
desktop/core/src/desktop/js/parse/sql/elasticsearch/elasticsearchSyntaxParser.js

@@ -801,7 +801,7 @@ case 580:
      parser.suggestDatabases({ prependFrom: true, appendDot: true });
    
 break;
-case 581: case 619: case 640: case 653: case 657: case 681: case 706: case 707: case 788: case 790: case 854: case 864: case 871: case 883: case 986: case 1161: case 1199: case 1200:
+case 581: case 619: case 640: case 653: case 657: case 788: case 790: case 854: case 864: case 871: case 883: case 986: case 1161: case 1199:
 this.$ = $$[$0];
 break;
 case 584:
@@ -1172,6 +1172,13 @@ case 679: case 680:
 
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 681: case 706:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 682:
@@ -1179,10 +1186,20 @@ case 682:
      // verifyType($$[$0], 'NUMBER');
      this.$ = $$[$0];
      $$[$0].types = ['NUMBER'];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
-case 683: case 684: case 685: case 686: case 693: case 694: case 695: case 696: case 697: case 698: case 704: case 705: case 726: case 784: case 785: case 843:
-this.$ = { types: [ 'BOOLEAN' ] };
+case 683: case 684: case 685:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 686:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 687:
 
@@ -1195,6 +1212,22 @@ case 689: case 690: case 691: case 692:
 
      parser.addColRefToVariableIfExists($$[$0-2], $$[$0]);
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 693: case 694: case 695: case 696: case 726: case 784: case 785:
+this.$ = { types: [ 'BOOLEAN' ] };
+break;
+case 697:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 698:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 699: case 700:
@@ -1202,6 +1235,7 @@ case 699: case 700:
      // verifyType($$[$0-2], 'BOOLEAN');
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 701: case 702: case 703:
@@ -1209,6 +1243,25 @@ case 701: case 702: case 703:
      // verifyType($$[$0-2], 'NUMBER');
      // verifyType($$[$0], 'NUMBER');
      this.$ = { types: [ 'NUMBER' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 704:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 705:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 707:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 709:
@@ -1622,13 +1675,14 @@ case 811:
 this.$ = { types: ['COLREF'], columnReference: $$[$0].chain };
 break;
 case 812:
-this.$ = { types: [ 'NULL' ] };
+this.$ = { types: [ 'NULL' ], text: $$[$0] };
 break;
 case 813:
 
      // We need to handle arbitrary UDFs here instead of inside UserDefinedFunction or there will be a conflict
      // with columnReference for functions like: db.udf(foo)
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1670,6 +1724,7 @@ break;
 case 818:
 
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1692,6 +1747,7 @@ break;
 case 819: case 1056: case 1057:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].activePosition) {
        parser.applyArgumentTypesToSuggestions($$[$0-1], $$[$0].activePosition);
      }
@@ -1715,23 +1771,39 @@ case 828:
    
 break;
 case 829:
-this.$ = { types: [ 'NUMBER' ] };
+this.$ = { types: [ 'NUMBER' ], text: $$[$0] };
 break;
-case 835: case 837:
+case 835: case 837: case 838:
 this.$ = $$[$0-1] + $$[$0];
 break;
-case 836:
+case 836: case 839:
 this.$ = $$[$0-2] + $$[$0-1] + $$[$0];
 break;
-case 841: case 842:
+case 840:
+this.$ = $$[$0-3] + $$[$0-2] + $$[$0-1] + $$[$0];
+break;
+case 841:
 
      if (/\$\{[^}]*\}/.test($$[$0])) {
        parser.addVariableLocation(_$[$0], $$[$0]);
-       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }] }
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: "'" + $$[$0] + "'" }
      } else {
        this.$ = { types: [ 'STRING' ] }
      }
    
+break;
+case 842:
+
+     if (/\$\{[^}]*\}/.test($$[$0])) {
+       parser.addVariableLocation(_$[$0], $$[$0]);
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: '"' + $$[$0] + '"' }
+     } else {
+       this.$ = { types: [ 'STRING' ], text: '"' + $$[$0] + '"' }
+     }
+   
+break;
+case 843:
+this.$ = { types: [ 'BOOLEAN' ], text: $$[$0] };
 break;
 case 844:
 
@@ -2214,6 +2286,7 @@ break;
 case 1054: case 1055:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].expressions && $$[$0].expressions.length) {
        this.$ = { function: $$[$0-1], expression: $$[$0].expressions[$$[$0].expressions.length - 1].expression, types: ['UDFREF'] }
      } else {
@@ -2226,7 +2299,7 @@ case 1064:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }]
      }
    
 break;
@@ -2273,7 +2346,7 @@ case 1073:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-2].activePosition + 1,
-       expressions: $$[$0-2].expressions.concat([{ expression: undefined, location: _$[$0] }])
+       expressions: $$[$0-2].expressions.concat([{ expression: { text: '' }, location: _$[$0] }])
      }
    
 break;
@@ -2282,7 +2355,7 @@ case 1074:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-4].activePosition + 1,
-       expressions: $$[$0-4].expressions.concat([{ expression: undefined, location: _$[$0-2] }]).concat($$[$0].expressions)
+       expressions: $$[$0-4].expressions.concat([{ expression: { text: '' }, location: _$[$0-2] }]).concat($$[$0].expressions)
      }
    
 break;
@@ -2301,7 +2374,7 @@ case 1076:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
@@ -2311,7 +2384,7 @@ case 1077:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2320,7 +2393,7 @@ case 1078:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2329,18 +2402,30 @@ case 1079:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-3] }, { expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-3] }, { expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
-case 1086: case 1110: case 1129:
-this.$ = { function: $$[$0-2], types: ['UDFREF'] };
+case 1086:
+
+    parser.addFunctionLocation(_$[$0-2], $$[$0-2]);
+    this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+  
 break;
 case 1087:
-this.$ = { function: $$[$0-3], expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression, types: ['UDFREF'] };
+
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
+     this.$ = {
+       function: $$[$0-3],
+       expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression,
+       types: ['UDFREF']
+     }
+   
 break;
 case 1088:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
      parser.valueExpressionSuggest();
      parser.applyArgumentTypesToSuggestions($$[$0-3], 1);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
@@ -2348,18 +2433,35 @@ case 1088:
 break;
 case 1089:
 
+     parser.addFunctionLocation(_$[$0-4], $$[$0-4]);
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-2].expressions);
      parser.suggestValueExpressionKeywords($$[$0-2].expressions[$$[$0-2].expressions.length - 1].expression);
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
 break;
 case 1090:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
      parser.applyArgumentTypesToSuggestions($$[$0-3], $$[$0-1].activePosition);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
    
 break;
-case 1097: case 1102:
-this.$ = { types: [ $$[$0-1].toUpperCase() ] };
+case 1097:
+
+     var expression = $$[$0-3];
+     parser.extractExpressionText(expression, $$[$0-3], $$[$0-2], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-5], [{
+       expression: expression,
+       location: {
+         first_line: _$[$0-3].first_line,
+         last_line: _$[$0-1].last_line,
+         first_column: _$[$0-3].first_column,
+         last_column: _$[$0-3].last_column
+       }
+     }]);
+     this.$ = { types: [ $$[$0-1].toUpperCase() ] }
+   
 break;
 case 1099:
 
@@ -2372,6 +2474,9 @@ case 1100: case 1101:
      parser.valueExpressionSuggest();
      this.$ = { types: [ 'T' ] };
    
+break;
+case 1102:
+this.$ = { types: [ $$[$0-1].toUpperCase() ] };
 break;
 case 1105:
 
@@ -2391,11 +2496,25 @@ case 1107: case 1108:
      this.$ = { types: [ 'T' ] };
    
 break;
-case 1109: case 1115:
-this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+case 1109:
+
+     parser.addFunctionArgumentLocations($$[$0-3], [{
+       expression: { text: $$[$0-1] },
+       location: _$[$0-1]
+     }]);
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] }
+   
 break;
-case 1111: case 1116: case 1128:
-this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+case 1110: case 1129:
+
+     this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+   
+break;
+case 1111:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] }
+   
 break;
 case 1112:
 
@@ -2428,6 +2547,17 @@ case 1114:
      }
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
+break;
+case 1115:
+
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+   
+break;
+case 1116: case 1128:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+   
 break;
 case 1117:
 
@@ -2528,7 +2658,10 @@ case 1168:
 this.$ = { inValueEdit: true, cursorAtStart: true };
 break;
 case 1169: case 1170: case 1171:
-this.$ = { suggestKeywords: ['NOT'] };
+
+     this.$ = { suggestKeywords: ['NOT'] }
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
 case 1175: case 1176: case 1177:
 
@@ -2537,14 +2670,21 @@ case 1175: case 1176: case 1177:
      this.$ = { types: ['BOOLEAN'] }
    
 break;
-case 1178: case 1180:
-this.$ = parser.findCaseType($$[$0-1]);
+case 1178:
+
+     this.$ = parser.findCaseType($$[$0-1])
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
-case 1179: case 1182:
+case 1179:
 
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
+break;
+case 1180:
+this.$ = parser.findCaseType($$[$0-1]);
 break;
 case 1181:
 
@@ -2552,6 +2692,12 @@ case 1181:
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
    
+break;
+case 1182:
+
+     $$[$0-3].caseTypes.push($$[$0-1]);
+     this.$ = parser.findCaseType($$[$0-3]);
+   
 break;
 case 1183:
 this.$ = parser.findCaseType($$[$0-2]);
@@ -2610,18 +2756,28 @@ case 1190:
    
 break;
 case 1193:
-this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] };
+
+     this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] }
+     parser.extractExpressionText(this.$, $$[$0]);
+   
 break;
 case 1194:
 
      $$[$0-1].caseTypes.push($$[$0]);
      this.$ = { caseTypes: $$[$0-1].caseTypes, lastType: $$[$0] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 1198:
 
      parser.suggestValueExpressionKeywords($$[$0-2], ['WHEN']);
    
+break;
+case 1200:
+
+     this.$ = $$[$0]
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 1201:
 this.$ = { caseTypes: [{ types: ['T'] }], suggestFilters: $$[$0].suggestFilters };

+ 24 - 0
desktop/core/src/desktop/js/parse/sql/elasticsearch/test/elasticsearchAutocompleteParser.Locations.test.js

@@ -64,6 +64,14 @@ describe('elasticsearchAutocompleteParser.js locations', () => {
           location: { first_line: 1, last_line: 1, first_column: 8, last_column: 10 },
           function: 'cos'
         },
+        {
+          type: 'functionArgument',
+          location: { first_line: 1, last_line: 1, first_column: 12, last_column: 13 },
+          function: 'cos',
+          argumentPosition: 0,
+          identifierChain: [{ name: 'cos' }],
+          expression: { types: ['NUMBER'], text: '1' }
+        },
         {
           type: 'alias',
           source: 'column',
@@ -1038,6 +1046,14 @@ describe('elasticsearchAutocompleteParser.js locations', () => {
             location: { first_line: 1, last_line: 1, first_column: 13, last_column: 15 },
             function: 'cos'
           },
+          {
+            type: 'functionArgument',
+            location: { first_line: 1, last_line: 1, first_column: 17, last_column: 22 },
+            function: 'cos',
+            argumentPosition: 0,
+            identifierChain: [{ name: 'cos' }],
+            expression: { types: ['COLREF'], columnReference: [{ name: 'boo' }, { name: 'a' }] }
+          },
           {
             type: 'table',
             location: { first_line: 1, last_line: 1, first_column: 17, last_column: 20 },
@@ -1280,6 +1296,14 @@ describe('elasticsearchAutocompleteParser.js locations', () => {
           location: { first_line: 1, last_line: 1, first_column: 8, last_column: 12 },
           function: 'count'
         },
+        {
+          type: 'functionArgument',
+          location: { first_line: 1, last_line: 1, first_column: 14, last_column: 15 },
+          function: 'count',
+          argumentPosition: 0,
+          identifierChain: [{ name: 'count' }],
+          expression: { text: '*' }
+        },
         {
           type: 'table',
           location: { first_line: 1, last_line: 1, first_column: 22, last_column: 31 },

+ 118 - 2
desktop/core/src/desktop/js/parse/sql/elasticsearch/test/elasticsearchAutocompleteParser.Select.test.js

@@ -371,6 +371,97 @@ describe('elasticsearchAutocompleteParser.js SELECT statements', () => {
   });
 
   describe('Select List Completion', () => {
+    it('should handle "select testUdf(not id), cos(-1), sin(1+id) from autocomp_test;"', () => {
+      assertAutoComplete({
+        beforeCursor: 'select testUdf(not id), cos(-1), sin(1+id) from autocomp_test;',
+        afterCursor: '',
+        noErrors: true,
+        containsKeywords: ['SELECT'],
+        expectedResult: {
+          locations: [
+            {
+              type: 'statement',
+              location: { first_line: 1, last_line: 1, first_column: 1, last_column: 62 }
+            },
+            {
+              type: 'selectList',
+              missing: false,
+              location: { first_line: 1, last_line: 1, first_column: 8, last_column: 43 }
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 8, last_column: 14 },
+              function: 'testudf'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 16, last_column: 22 },
+              function: 'testudf',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'testUdf' }],
+              expression: { types: ['BOOLEAN'], text: 'not id' }
+            },
+            {
+              type: 'column',
+              location: { first_line: 1, last_line: 1, first_column: 20, last_column: 22 },
+              identifierChain: [{ name: 'id' }],
+              qualified: false,
+              tables: [{ identifierChain: [{ name: 'autocomp_test' }] }]
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 25, last_column: 27 },
+              function: 'cos'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 29, last_column: 31 },
+              function: 'cos',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'cos' }],
+              expression: { types: ['NUMBER'], text: '- 1' }
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
+              function: 'sin'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 38, last_column: 42 },
+              function: 'sin',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'sin' }],
+              expression: { types: ['NUMBER'], text: '1 + id' }
+            },
+            {
+              type: 'column',
+              location: { first_line: 1, last_line: 1, first_column: 40, last_column: 42 },
+              identifierChain: [{ name: 'id' }],
+              qualified: false,
+              tables: [{ identifierChain: [{ name: 'autocomp_test' }] }]
+            },
+            {
+              type: 'table',
+              location: { first_line: 1, last_line: 1, first_column: 49, last_column: 62 },
+              identifierChain: [{ name: 'autocomp_test' }]
+            },
+            {
+              type: 'whereClause',
+              missing: true,
+              location: { first_line: 1, last_line: 1, first_column: 62, last_column: 62 }
+            },
+            {
+              type: 'limitClause',
+              missing: true,
+              location: { first_line: 1, last_line: 1, first_column: 62, last_column: 62 }
+            }
+          ],
+          lowerCase: true
+        }
+      });
+    });
+
     it('should handle "select count(*), tst.count, avg (id), avg from autocomp_test tst;"', () => {
       assertAutoComplete({
         beforeCursor: 'select count(*), tst.count, avg (id), avg from autocomp_test tst;',
@@ -393,6 +484,14 @@ describe('elasticsearchAutocompleteParser.js SELECT statements', () => {
               location: { first_line: 1, last_line: 1, first_column: 8, last_column: 12 },
               function: 'count'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 14, last_column: 15 },
+              function: 'count',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'count' }],
+              expression: { text: '*' }
+            },
             {
               type: 'table',
               location: { first_line: 1, last_line: 1, first_column: 18, last_column: 21 },
@@ -410,6 +509,14 @@ describe('elasticsearchAutocompleteParser.js SELECT statements', () => {
               location: { first_line: 1, last_line: 1, first_column: 29, last_column: 32 },
               function: 'avg'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
+              function: 'avg',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'avg' }],
+              expression: { types: ['COLREF'], columnReference: [{ name: 'id' }] }
+            },
             {
               type: 'column',
               location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
@@ -2192,6 +2299,14 @@ describe('elasticsearchAutocompleteParser.js SELECT statements', () => {
               identifierChain: [{ name: 'db' }, { name: 'customUdf' }],
               function: 'customudf'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 21, last_column: 24 },
+              function: 'customudf',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'db' }, { name: 'customUdf' }],
+              expression: { types: ['COLREF'], columnReference: [{ name: 'col' }] }
+            },
             {
               type: 'column',
               location: { first_line: 1, last_line: 1, first_column: 21, last_column: 24 },
@@ -4638,13 +4753,14 @@ describe('elasticsearchAutocompleteParser.js SELECT statements', () => {
   });
 
   describe('LIMIT clause', () => {
-    it('should not suggest anything for "SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT |"', () => {
+    it('should suggest values for "SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT |"', () => {
       assertAutoComplete({
         beforeCursor: 'SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT ',
         afterCursor: '',
         noErrors: true,
         expectedResult: {
-          lowerCase: false
+          lowerCase: false,
+          suggestKeywords: ['10', '100', '1000', '10000', '5000']
         }
       });
     });

+ 186 - 30
desktop/core/src/desktop/js/parse/sql/flink/flinkAutocompleteParser.js

@@ -596,7 +596,7 @@ case 283:
      parser.suggestDatabases({ prependFrom: true, appendDot: true });
    
 break;
-case 289: case 290: case 291: case 646: case 674: case 695: case 708: case 712: case 736: case 761: case 762: case 843: case 845: case 909: case 919: case 926: case 938: case 1027: case 1200: case 1238: case 1239:
+case 289: case 290: case 291: case 646: case 674: case 695: case 708: case 712: case 843: case 845: case 909: case 919: case 926: case 938: case 1027: case 1200: case 1238:
 this.$ = $$[$0];
 break;
 case 292: case 293:
@@ -1222,6 +1222,13 @@ case 734: case 735:
 
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 736: case 761:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 737:
@@ -1229,10 +1236,20 @@ case 737:
      // verifyType($$[$0], 'NUMBER');
      this.$ = $$[$0];
      $$[$0].types = ['NUMBER'];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
-case 738: case 739: case 740: case 741: case 748: case 749: case 750: case 751: case 752: case 753: case 759: case 760: case 781: case 839: case 840: case 898:
-this.$ = { types: [ 'BOOLEAN' ] };
+case 738: case 739: case 740:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 741:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 742:
 
@@ -1245,6 +1262,22 @@ case 744: case 745: case 746: case 747:
 
      parser.addColRefToVariableIfExists($$[$0-2], $$[$0]);
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 748: case 749: case 750: case 751: case 781: case 839: case 840:
+this.$ = { types: [ 'BOOLEAN' ] };
+break;
+case 752:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 753:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 754: case 755:
@@ -1252,6 +1285,7 @@ case 754: case 755:
      // verifyType($$[$0-2], 'BOOLEAN');
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 756: case 757: case 758:
@@ -1259,6 +1293,25 @@ case 756: case 757: case 758:
      // verifyType($$[$0-2], 'NUMBER');
      // verifyType($$[$0], 'NUMBER');
      this.$ = { types: [ 'NUMBER' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 759:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 760:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 762:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 764:
@@ -1672,13 +1725,14 @@ case 866:
 this.$ = { types: ['COLREF'], columnReference: $$[$0].chain };
 break;
 case 867:
-this.$ = { types: [ 'NULL' ] };
+this.$ = { types: [ 'NULL' ], text: $$[$0] };
 break;
 case 868:
 
      // We need to handle arbitrary UDFs here instead of inside UserDefinedFunction or there will be a conflict
      // with columnReference for functions like: db.udf(foo)
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1720,6 +1774,7 @@ break;
 case 873:
 
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1742,6 +1797,7 @@ break;
 case 874: case 1095: case 1096:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].activePosition) {
        parser.applyArgumentTypesToSuggestions($$[$0-1], $$[$0].activePosition);
      }
@@ -1765,23 +1821,39 @@ case 883:
    
 break;
 case 884:
-this.$ = { types: [ 'NUMBER' ] };
+this.$ = { types: [ 'NUMBER' ], text: $$[$0] };
 break;
-case 890: case 892:
+case 890: case 892: case 893:
 this.$ = $$[$0-1] + $$[$0];
 break;
-case 891:
+case 891: case 894:
 this.$ = $$[$0-2] + $$[$0-1] + $$[$0];
 break;
-case 896: case 897:
+case 895:
+this.$ = $$[$0-3] + $$[$0-2] + $$[$0-1] + $$[$0];
+break;
+case 896:
 
      if (/\$\{[^}]*\}/.test($$[$0])) {
        parser.addVariableLocation(_$[$0], $$[$0]);
-       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }] }
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: "'" + $$[$0] + "'" }
      } else {
        this.$ = { types: [ 'STRING' ] }
      }
    
+break;
+case 897:
+
+     if (/\$\{[^}]*\}/.test($$[$0])) {
+       parser.addVariableLocation(_$[$0], $$[$0]);
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: '"' + $$[$0] + '"' }
+     } else {
+       this.$ = { types: [ 'STRING' ], text: '"' + $$[$0] + '"' }
+     }
+   
+break;
+case 898:
+this.$ = { types: [ 'BOOLEAN' ], text: $$[$0] };
 break;
 case 899:
 
@@ -2253,6 +2325,7 @@ break;
 case 1093: case 1094:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].expressions && $$[$0].expressions.length) {
        this.$ = { function: $$[$0-1], expression: $$[$0].expressions[$$[$0].expressions.length - 1].expression, types: ['UDFREF'] }
      } else {
@@ -2265,7 +2338,7 @@ case 1103:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }]
      }
    
 break;
@@ -2312,7 +2385,7 @@ case 1112:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-2].activePosition + 1,
-       expressions: $$[$0-2].expressions.concat([{ expression: undefined, location: _$[$0] }])
+       expressions: $$[$0-2].expressions.concat([{ expression: { text: '' }, location: _$[$0] }])
      }
    
 break;
@@ -2321,7 +2394,7 @@ case 1113:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-4].activePosition + 1,
-       expressions: $$[$0-4].expressions.concat([{ expression: undefined, location: _$[$0-2] }]).concat($$[$0].expressions)
+       expressions: $$[$0-4].expressions.concat([{ expression: { text: '' }, location: _$[$0-2] }]).concat($$[$0].expressions)
      }
    
 break;
@@ -2340,7 +2413,7 @@ case 1115:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
@@ -2350,7 +2423,7 @@ case 1116:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2359,7 +2432,7 @@ case 1117:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2368,18 +2441,30 @@ case 1118:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-3] }, { expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-3] }, { expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
-case 1125: case 1149: case 1168:
-this.$ = { function: $$[$0-2], types: ['UDFREF'] };
+case 1125:
+
+    parser.addFunctionLocation(_$[$0-2], $$[$0-2]);
+    this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+  
 break;
 case 1126:
-this.$ = { function: $$[$0-3], expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression, types: ['UDFREF'] };
+
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
+     this.$ = {
+       function: $$[$0-3],
+       expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression,
+       types: ['UDFREF']
+     }
+   
 break;
 case 1127:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
      parser.valueExpressionSuggest();
      parser.applyArgumentTypesToSuggestions($$[$0-3], 1);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
@@ -2387,18 +2472,35 @@ case 1127:
 break;
 case 1128:
 
+     parser.addFunctionLocation(_$[$0-4], $$[$0-4]);
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-2].expressions);
      parser.suggestValueExpressionKeywords($$[$0-2].expressions[$$[$0-2].expressions.length - 1].expression);
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
 break;
 case 1129:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
      parser.applyArgumentTypesToSuggestions($$[$0-3], $$[$0-1].activePosition);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
    
 break;
-case 1136: case 1141:
-this.$ = { types: [ $$[$0-1].toUpperCase() ] };
+case 1136:
+
+     var expression = $$[$0-3];
+     parser.extractExpressionText(expression, $$[$0-3], $$[$0-2], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-5], [{
+       expression: expression,
+       location: {
+         first_line: _$[$0-3].first_line,
+         last_line: _$[$0-1].last_line,
+         first_column: _$[$0-3].first_column,
+         last_column: _$[$0-3].last_column
+       }
+     }]);
+     this.$ = { types: [ $$[$0-1].toUpperCase() ] }
+   
 break;
 case 1138:
 
@@ -2411,6 +2513,9 @@ case 1139: case 1140:
      parser.valueExpressionSuggest();
      this.$ = { types: [ 'T' ] };
    
+break;
+case 1141:
+this.$ = { types: [ $$[$0-1].toUpperCase() ] };
 break;
 case 1144:
 
@@ -2430,11 +2535,25 @@ case 1146: case 1147:
      this.$ = { types: [ 'T' ] };
    
 break;
-case 1148: case 1154:
-this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+case 1148:
+
+     parser.addFunctionArgumentLocations($$[$0-3], [{
+       expression: { text: $$[$0-1] },
+       location: _$[$0-1]
+     }]);
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] }
+   
 break;
-case 1150: case 1155: case 1167:
-this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+case 1149: case 1168:
+
+     this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+   
+break;
+case 1150:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] }
+   
 break;
 case 1151:
 
@@ -2467,6 +2586,17 @@ case 1153:
      }
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
+break;
+case 1154:
+
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+   
+break;
+case 1155: case 1167:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+   
 break;
 case 1156:
 
@@ -2567,7 +2697,10 @@ case 1207:
 this.$ = { inValueEdit: true, cursorAtStart: true };
 break;
 case 1208: case 1209: case 1210:
-this.$ = { suggestKeywords: ['NOT'] };
+
+     this.$ = { suggestKeywords: ['NOT'] }
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
 case 1214: case 1215: case 1216:
 
@@ -2576,14 +2709,21 @@ case 1214: case 1215: case 1216:
      this.$ = { types: ['BOOLEAN'] }
    
 break;
-case 1217: case 1219:
-this.$ = parser.findCaseType($$[$0-1]);
+case 1217:
+
+     this.$ = parser.findCaseType($$[$0-1])
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
-case 1218: case 1221:
+case 1218:
 
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
+break;
+case 1219:
+this.$ = parser.findCaseType($$[$0-1]);
 break;
 case 1220:
 
@@ -2591,6 +2731,12 @@ case 1220:
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
    
+break;
+case 1221:
+
+     $$[$0-3].caseTypes.push($$[$0-1]);
+     this.$ = parser.findCaseType($$[$0-3]);
+   
 break;
 case 1222:
 this.$ = parser.findCaseType($$[$0-2]);
@@ -2649,18 +2795,28 @@ case 1229:
    
 break;
 case 1232:
-this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] };
+
+     this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] }
+     parser.extractExpressionText(this.$, $$[$0]);
+   
 break;
 case 1233:
 
      $$[$0-1].caseTypes.push($$[$0]);
      this.$ = { caseTypes: $$[$0-1].caseTypes, lastType: $$[$0] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 1237:
 
      parser.suggestValueExpressionKeywords($$[$0-2], ['WHEN']);
    
+break;
+case 1239:
+
+     this.$ = $$[$0]
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 1240:
 this.$ = { caseTypes: [{ types: ['T'] }], suggestFilters: $$[$0].suggestFilters };

+ 186 - 30
desktop/core/src/desktop/js/parse/sql/flink/flinkSyntaxParser.js

@@ -801,7 +801,7 @@ case 580:
      parser.suggestDatabases({ prependFrom: true, appendDot: true });
    
 break;
-case 581: case 619: case 640: case 653: case 657: case 681: case 706: case 707: case 788: case 790: case 854: case 864: case 871: case 883: case 986: case 1161: case 1199: case 1200:
+case 581: case 619: case 640: case 653: case 657: case 788: case 790: case 854: case 864: case 871: case 883: case 986: case 1161: case 1199:
 this.$ = $$[$0];
 break;
 case 584:
@@ -1172,6 +1172,13 @@ case 679: case 680:
 
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 681: case 706:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 682:
@@ -1179,10 +1186,20 @@ case 682:
      // verifyType($$[$0], 'NUMBER');
      this.$ = $$[$0];
      $$[$0].types = ['NUMBER'];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
-case 683: case 684: case 685: case 686: case 693: case 694: case 695: case 696: case 697: case 698: case 704: case 705: case 726: case 784: case 785: case 843:
-this.$ = { types: [ 'BOOLEAN' ] };
+case 683: case 684: case 685:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 686:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 687:
 
@@ -1195,6 +1212,22 @@ case 689: case 690: case 691: case 692:
 
      parser.addColRefToVariableIfExists($$[$0-2], $$[$0]);
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 693: case 694: case 695: case 696: case 726: case 784: case 785:
+this.$ = { types: [ 'BOOLEAN' ] };
+break;
+case 697:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 698:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 699: case 700:
@@ -1202,6 +1235,7 @@ case 699: case 700:
      // verifyType($$[$0-2], 'BOOLEAN');
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 701: case 702: case 703:
@@ -1209,6 +1243,25 @@ case 701: case 702: case 703:
      // verifyType($$[$0-2], 'NUMBER');
      // verifyType($$[$0], 'NUMBER');
      this.$ = { types: [ 'NUMBER' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 704:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 705:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 707:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 709:
@@ -1622,13 +1675,14 @@ case 811:
 this.$ = { types: ['COLREF'], columnReference: $$[$0].chain };
 break;
 case 812:
-this.$ = { types: [ 'NULL' ] };
+this.$ = { types: [ 'NULL' ], text: $$[$0] };
 break;
 case 813:
 
      // We need to handle arbitrary UDFs here instead of inside UserDefinedFunction or there will be a conflict
      // with columnReference for functions like: db.udf(foo)
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1670,6 +1724,7 @@ break;
 case 818:
 
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1692,6 +1747,7 @@ break;
 case 819: case 1056: case 1057:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].activePosition) {
        parser.applyArgumentTypesToSuggestions($$[$0-1], $$[$0].activePosition);
      }
@@ -1715,23 +1771,39 @@ case 828:
    
 break;
 case 829:
-this.$ = { types: [ 'NUMBER' ] };
+this.$ = { types: [ 'NUMBER' ], text: $$[$0] };
 break;
-case 835: case 837:
+case 835: case 837: case 838:
 this.$ = $$[$0-1] + $$[$0];
 break;
-case 836:
+case 836: case 839:
 this.$ = $$[$0-2] + $$[$0-1] + $$[$0];
 break;
-case 841: case 842:
+case 840:
+this.$ = $$[$0-3] + $$[$0-2] + $$[$0-1] + $$[$0];
+break;
+case 841:
 
      if (/\$\{[^}]*\}/.test($$[$0])) {
        parser.addVariableLocation(_$[$0], $$[$0]);
-       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }] }
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: "'" + $$[$0] + "'" }
      } else {
        this.$ = { types: [ 'STRING' ] }
      }
    
+break;
+case 842:
+
+     if (/\$\{[^}]*\}/.test($$[$0])) {
+       parser.addVariableLocation(_$[$0], $$[$0]);
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: '"' + $$[$0] + '"' }
+     } else {
+       this.$ = { types: [ 'STRING' ], text: '"' + $$[$0] + '"' }
+     }
+   
+break;
+case 843:
+this.$ = { types: [ 'BOOLEAN' ], text: $$[$0] };
 break;
 case 844:
 
@@ -2214,6 +2286,7 @@ break;
 case 1054: case 1055:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].expressions && $$[$0].expressions.length) {
        this.$ = { function: $$[$0-1], expression: $$[$0].expressions[$$[$0].expressions.length - 1].expression, types: ['UDFREF'] }
      } else {
@@ -2226,7 +2299,7 @@ case 1064:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }]
      }
    
 break;
@@ -2273,7 +2346,7 @@ case 1073:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-2].activePosition + 1,
-       expressions: $$[$0-2].expressions.concat([{ expression: undefined, location: _$[$0] }])
+       expressions: $$[$0-2].expressions.concat([{ expression: { text: '' }, location: _$[$0] }])
      }
    
 break;
@@ -2282,7 +2355,7 @@ case 1074:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-4].activePosition + 1,
-       expressions: $$[$0-4].expressions.concat([{ expression: undefined, location: _$[$0-2] }]).concat($$[$0].expressions)
+       expressions: $$[$0-4].expressions.concat([{ expression: { text: '' }, location: _$[$0-2] }]).concat($$[$0].expressions)
      }
    
 break;
@@ -2301,7 +2374,7 @@ case 1076:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
@@ -2311,7 +2384,7 @@ case 1077:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2320,7 +2393,7 @@ case 1078:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2329,18 +2402,30 @@ case 1079:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-3] }, { expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-3] }, { expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
-case 1086: case 1110: case 1129:
-this.$ = { function: $$[$0-2], types: ['UDFREF'] };
+case 1086:
+
+    parser.addFunctionLocation(_$[$0-2], $$[$0-2]);
+    this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+  
 break;
 case 1087:
-this.$ = { function: $$[$0-3], expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression, types: ['UDFREF'] };
+
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
+     this.$ = {
+       function: $$[$0-3],
+       expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression,
+       types: ['UDFREF']
+     }
+   
 break;
 case 1088:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
      parser.valueExpressionSuggest();
      parser.applyArgumentTypesToSuggestions($$[$0-3], 1);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
@@ -2348,18 +2433,35 @@ case 1088:
 break;
 case 1089:
 
+     parser.addFunctionLocation(_$[$0-4], $$[$0-4]);
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-2].expressions);
      parser.suggestValueExpressionKeywords($$[$0-2].expressions[$$[$0-2].expressions.length - 1].expression);
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
 break;
 case 1090:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
      parser.applyArgumentTypesToSuggestions($$[$0-3], $$[$0-1].activePosition);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
    
 break;
-case 1097: case 1102:
-this.$ = { types: [ $$[$0-1].toUpperCase() ] };
+case 1097:
+
+     var expression = $$[$0-3];
+     parser.extractExpressionText(expression, $$[$0-3], $$[$0-2], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-5], [{
+       expression: expression,
+       location: {
+         first_line: _$[$0-3].first_line,
+         last_line: _$[$0-1].last_line,
+         first_column: _$[$0-3].first_column,
+         last_column: _$[$0-3].last_column
+       }
+     }]);
+     this.$ = { types: [ $$[$0-1].toUpperCase() ] }
+   
 break;
 case 1099:
 
@@ -2372,6 +2474,9 @@ case 1100: case 1101:
      parser.valueExpressionSuggest();
      this.$ = { types: [ 'T' ] };
    
+break;
+case 1102:
+this.$ = { types: [ $$[$0-1].toUpperCase() ] };
 break;
 case 1105:
 
@@ -2391,11 +2496,25 @@ case 1107: case 1108:
      this.$ = { types: [ 'T' ] };
    
 break;
-case 1109: case 1115:
-this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+case 1109:
+
+     parser.addFunctionArgumentLocations($$[$0-3], [{
+       expression: { text: $$[$0-1] },
+       location: _$[$0-1]
+     }]);
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] }
+   
 break;
-case 1111: case 1116: case 1128:
-this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+case 1110: case 1129:
+
+     this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+   
+break;
+case 1111:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] }
+   
 break;
 case 1112:
 
@@ -2428,6 +2547,17 @@ case 1114:
      }
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
+break;
+case 1115:
+
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+   
+break;
+case 1116: case 1128:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+   
 break;
 case 1117:
 
@@ -2528,7 +2658,10 @@ case 1168:
 this.$ = { inValueEdit: true, cursorAtStart: true };
 break;
 case 1169: case 1170: case 1171:
-this.$ = { suggestKeywords: ['NOT'] };
+
+     this.$ = { suggestKeywords: ['NOT'] }
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
 case 1175: case 1176: case 1177:
 
@@ -2537,14 +2670,21 @@ case 1175: case 1176: case 1177:
      this.$ = { types: ['BOOLEAN'] }
    
 break;
-case 1178: case 1180:
-this.$ = parser.findCaseType($$[$0-1]);
+case 1178:
+
+     this.$ = parser.findCaseType($$[$0-1])
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
-case 1179: case 1182:
+case 1179:
 
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
+break;
+case 1180:
+this.$ = parser.findCaseType($$[$0-1]);
 break;
 case 1181:
 
@@ -2552,6 +2692,12 @@ case 1181:
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
    
+break;
+case 1182:
+
+     $$[$0-3].caseTypes.push($$[$0-1]);
+     this.$ = parser.findCaseType($$[$0-3]);
+   
 break;
 case 1183:
 this.$ = parser.findCaseType($$[$0-2]);
@@ -2610,18 +2756,28 @@ case 1190:
    
 break;
 case 1193:
-this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] };
+
+     this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] }
+     parser.extractExpressionText(this.$, $$[$0]);
+   
 break;
 case 1194:
 
      $$[$0-1].caseTypes.push($$[$0]);
      this.$ = { caseTypes: $$[$0-1].caseTypes, lastType: $$[$0] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 1198:
 
      parser.suggestValueExpressionKeywords($$[$0-2], ['WHEN']);
    
+break;
+case 1200:
+
+     this.$ = $$[$0]
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 1201:
 this.$ = { caseTypes: [{ types: ['T'] }], suggestFilters: $$[$0].suggestFilters };

+ 24 - 0
desktop/core/src/desktop/js/parse/sql/flink/test/flinkAutocompleteParser.Locations.test.js

@@ -64,6 +64,14 @@ describe('flinkAutocompleteParser.js locations', () => {
           location: { first_line: 1, last_line: 1, first_column: 8, last_column: 10 },
           function: 'cos'
         },
+        {
+          type: 'functionArgument',
+          location: { first_line: 1, last_line: 1, first_column: 12, last_column: 13 },
+          function: 'cos',
+          argumentPosition: 0,
+          identifierChain: [{ name: 'cos' }],
+          expression: { types: ['NUMBER'], text: '1' }
+        },
         {
           type: 'alias',
           source: 'column',
@@ -1038,6 +1046,14 @@ describe('flinkAutocompleteParser.js locations', () => {
             location: { first_line: 1, last_line: 1, first_column: 13, last_column: 15 },
             function: 'cos'
           },
+          {
+            type: 'functionArgument',
+            location: { first_line: 1, last_line: 1, first_column: 17, last_column: 22 },
+            function: 'cos',
+            argumentPosition: 0,
+            identifierChain: [{ name: 'cos' }],
+            expression: { types: ['COLREF'], columnReference: [{ name: 'boo' }, { name: 'a' }] }
+          },
           {
             type: 'table',
             location: { first_line: 1, last_line: 1, first_column: 17, last_column: 20 },
@@ -1280,6 +1296,14 @@ describe('flinkAutocompleteParser.js locations', () => {
           location: { first_line: 1, last_line: 1, first_column: 8, last_column: 12 },
           function: 'count'
         },
+        {
+          type: 'functionArgument',
+          location: { first_line: 1, last_line: 1, first_column: 14, last_column: 15 },
+          function: 'count',
+          argumentPosition: 0,
+          identifierChain: [{ name: 'count' }],
+          expression: { text: '*' }
+        },
         {
           type: 'table',
           location: { first_line: 1, last_line: 1, first_column: 22, last_column: 31 },

+ 118 - 2
desktop/core/src/desktop/js/parse/sql/flink/test/flinkAutocompleteParser.Select.test.js

@@ -371,6 +371,97 @@ describe('flinkAutocompleteParser.js SELECT statements', () => {
   });
 
   describe('Select List Completion', () => {
+    it('should handle "select testUdf(not id), cos(-1), sin(1+id) from autocomp_test;"', () => {
+      assertAutoComplete({
+        beforeCursor: 'select testUdf(not id), cos(-1), sin(1+id) from autocomp_test;',
+        afterCursor: '',
+        noErrors: true,
+        containsKeywords: ['SELECT'],
+        expectedResult: {
+          locations: [
+            {
+              type: 'statement',
+              location: { first_line: 1, last_line: 1, first_column: 1, last_column: 62 }
+            },
+            {
+              type: 'selectList',
+              missing: false,
+              location: { first_line: 1, last_line: 1, first_column: 8, last_column: 43 }
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 8, last_column: 14 },
+              function: 'testudf'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 16, last_column: 22 },
+              function: 'testudf',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'testUdf' }],
+              expression: { types: ['BOOLEAN'], text: 'not id' }
+            },
+            {
+              type: 'column',
+              location: { first_line: 1, last_line: 1, first_column: 20, last_column: 22 },
+              identifierChain: [{ name: 'id' }],
+              qualified: false,
+              tables: [{ identifierChain: [{ name: 'autocomp_test' }] }]
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 25, last_column: 27 },
+              function: 'cos'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 29, last_column: 31 },
+              function: 'cos',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'cos' }],
+              expression: { types: ['NUMBER'], text: '- 1' }
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
+              function: 'sin'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 38, last_column: 42 },
+              function: 'sin',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'sin' }],
+              expression: { types: ['NUMBER'], text: '1 + id' }
+            },
+            {
+              type: 'column',
+              location: { first_line: 1, last_line: 1, first_column: 40, last_column: 42 },
+              identifierChain: [{ name: 'id' }],
+              qualified: false,
+              tables: [{ identifierChain: [{ name: 'autocomp_test' }] }]
+            },
+            {
+              type: 'table',
+              location: { first_line: 1, last_line: 1, first_column: 49, last_column: 62 },
+              identifierChain: [{ name: 'autocomp_test' }]
+            },
+            {
+              type: 'whereClause',
+              missing: true,
+              location: { first_line: 1, last_line: 1, first_column: 62, last_column: 62 }
+            },
+            {
+              type: 'limitClause',
+              missing: true,
+              location: { first_line: 1, last_line: 1, first_column: 62, last_column: 62 }
+            }
+          ],
+          lowerCase: true
+        }
+      });
+    });
+
     it('should handle "select count(*), tst.count, avg (id), avg from autocomp_test tst;"', () => {
       assertAutoComplete({
         beforeCursor: 'select count(*), tst.count, avg (id), avg from autocomp_test tst;',
@@ -393,6 +484,14 @@ describe('flinkAutocompleteParser.js SELECT statements', () => {
               location: { first_line: 1, last_line: 1, first_column: 8, last_column: 12 },
               function: 'count'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 14, last_column: 15 },
+              function: 'count',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'count' }],
+              expression: { text: '*' }
+            },
             {
               type: 'table',
               location: { first_line: 1, last_line: 1, first_column: 18, last_column: 21 },
@@ -410,6 +509,14 @@ describe('flinkAutocompleteParser.js SELECT statements', () => {
               location: { first_line: 1, last_line: 1, first_column: 29, last_column: 32 },
               function: 'avg'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
+              function: 'avg',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'avg' }],
+              expression: { types: ['COLREF'], columnReference: [{ name: 'id' }] }
+            },
             {
               type: 'column',
               location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
@@ -2192,6 +2299,14 @@ describe('flinkAutocompleteParser.js SELECT statements', () => {
               identifierChain: [{ name: 'db' }, { name: 'customUdf' }],
               function: 'customudf'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 21, last_column: 24 },
+              function: 'customudf',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'db' }, { name: 'customUdf' }],
+              expression: { types: ['COLREF'], columnReference: [{ name: 'col' }] }
+            },
             {
               type: 'column',
               location: { first_line: 1, last_line: 1, first_column: 21, last_column: 24 },
@@ -4638,13 +4753,14 @@ describe('flinkAutocompleteParser.js SELECT statements', () => {
   });
 
   describe('LIMIT clause', () => {
-    it('should not suggest anything for "SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT |"', () => {
+    it('should suggest values for "SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT |"', () => {
       assertAutoComplete({
         beforeCursor: 'SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT ',
         afterCursor: '',
         noErrors: true,
         expectedResult: {
-          lowerCase: false
+          lowerCase: false,
+          suggestKeywords: ['10', '100', '1000', '10000', '5000']
         }
       });
     });

+ 186 - 30
desktop/core/src/desktop/js/parse/sql/generic/genericAutocompleteParser.js

@@ -596,7 +596,7 @@ case 283:
      parser.suggestDatabases({ prependFrom: true, appendDot: true });
    
 break;
-case 289: case 290: case 291: case 646: case 674: case 695: case 708: case 712: case 736: case 761: case 762: case 843: case 845: case 909: case 919: case 926: case 938: case 1027: case 1200: case 1238: case 1239:
+case 289: case 290: case 291: case 646: case 674: case 695: case 708: case 712: case 843: case 845: case 909: case 919: case 926: case 938: case 1027: case 1200: case 1238:
 this.$ = $$[$0];
 break;
 case 292: case 293:
@@ -1222,6 +1222,13 @@ case 734: case 735:
 
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 736: case 761:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 737:
@@ -1229,10 +1236,20 @@ case 737:
      // verifyType($$[$0], 'NUMBER');
      this.$ = $$[$0];
      $$[$0].types = ['NUMBER'];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
-case 738: case 739: case 740: case 741: case 748: case 749: case 750: case 751: case 752: case 753: case 759: case 760: case 781: case 839: case 840: case 898:
-this.$ = { types: [ 'BOOLEAN' ] };
+case 738: case 739: case 740:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 741:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 742:
 
@@ -1245,6 +1262,22 @@ case 744: case 745: case 746: case 747:
 
      parser.addColRefToVariableIfExists($$[$0-2], $$[$0]);
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 748: case 749: case 750: case 751: case 781: case 839: case 840:
+this.$ = { types: [ 'BOOLEAN' ] };
+break;
+case 752:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 753:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 754: case 755:
@@ -1252,6 +1285,7 @@ case 754: case 755:
      // verifyType($$[$0-2], 'BOOLEAN');
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 756: case 757: case 758:
@@ -1259,6 +1293,25 @@ case 756: case 757: case 758:
      // verifyType($$[$0-2], 'NUMBER');
      // verifyType($$[$0], 'NUMBER');
      this.$ = { types: [ 'NUMBER' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 759:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 760:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 762:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 764:
@@ -1672,13 +1725,14 @@ case 866:
 this.$ = { types: ['COLREF'], columnReference: $$[$0].chain };
 break;
 case 867:
-this.$ = { types: [ 'NULL' ] };
+this.$ = { types: [ 'NULL' ], text: $$[$0] };
 break;
 case 868:
 
      // We need to handle arbitrary UDFs here instead of inside UserDefinedFunction or there will be a conflict
      // with columnReference for functions like: db.udf(foo)
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1720,6 +1774,7 @@ break;
 case 873:
 
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1742,6 +1797,7 @@ break;
 case 874: case 1095: case 1096:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].activePosition) {
        parser.applyArgumentTypesToSuggestions($$[$0-1], $$[$0].activePosition);
      }
@@ -1765,23 +1821,39 @@ case 883:
    
 break;
 case 884:
-this.$ = { types: [ 'NUMBER' ] };
+this.$ = { types: [ 'NUMBER' ], text: $$[$0] };
 break;
-case 890: case 892:
+case 890: case 892: case 893:
 this.$ = $$[$0-1] + $$[$0];
 break;
-case 891:
+case 891: case 894:
 this.$ = $$[$0-2] + $$[$0-1] + $$[$0];
 break;
-case 896: case 897:
+case 895:
+this.$ = $$[$0-3] + $$[$0-2] + $$[$0-1] + $$[$0];
+break;
+case 896:
 
      if (/\$\{[^}]*\}/.test($$[$0])) {
        parser.addVariableLocation(_$[$0], $$[$0]);
-       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }] }
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: "'" + $$[$0] + "'" }
      } else {
        this.$ = { types: [ 'STRING' ] }
      }
    
+break;
+case 897:
+
+     if (/\$\{[^}]*\}/.test($$[$0])) {
+       parser.addVariableLocation(_$[$0], $$[$0]);
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: '"' + $$[$0] + '"' }
+     } else {
+       this.$ = { types: [ 'STRING' ], text: '"' + $$[$0] + '"' }
+     }
+   
+break;
+case 898:
+this.$ = { types: [ 'BOOLEAN' ], text: $$[$0] };
 break;
 case 899:
 
@@ -2253,6 +2325,7 @@ break;
 case 1093: case 1094:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].expressions && $$[$0].expressions.length) {
        this.$ = { function: $$[$0-1], expression: $$[$0].expressions[$$[$0].expressions.length - 1].expression, types: ['UDFREF'] }
      } else {
@@ -2265,7 +2338,7 @@ case 1103:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }]
      }
    
 break;
@@ -2312,7 +2385,7 @@ case 1112:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-2].activePosition + 1,
-       expressions: $$[$0-2].expressions.concat([{ expression: undefined, location: _$[$0] }])
+       expressions: $$[$0-2].expressions.concat([{ expression: { text: '' }, location: _$[$0] }])
      }
    
 break;
@@ -2321,7 +2394,7 @@ case 1113:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-4].activePosition + 1,
-       expressions: $$[$0-4].expressions.concat([{ expression: undefined, location: _$[$0-2] }]).concat($$[$0].expressions)
+       expressions: $$[$0-4].expressions.concat([{ expression: { text: '' }, location: _$[$0-2] }]).concat($$[$0].expressions)
      }
    
 break;
@@ -2340,7 +2413,7 @@ case 1115:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
@@ -2350,7 +2423,7 @@ case 1116:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2359,7 +2432,7 @@ case 1117:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2368,18 +2441,30 @@ case 1118:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-3] }, { expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-3] }, { expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
-case 1125: case 1149: case 1168:
-this.$ = { function: $$[$0-2], types: ['UDFREF'] };
+case 1125:
+
+    parser.addFunctionLocation(_$[$0-2], $$[$0-2]);
+    this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+  
 break;
 case 1126:
-this.$ = { function: $$[$0-3], expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression, types: ['UDFREF'] };
+
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
+     this.$ = {
+       function: $$[$0-3],
+       expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression,
+       types: ['UDFREF']
+     }
+   
 break;
 case 1127:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
      parser.valueExpressionSuggest();
      parser.applyArgumentTypesToSuggestions($$[$0-3], 1);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
@@ -2387,18 +2472,35 @@ case 1127:
 break;
 case 1128:
 
+     parser.addFunctionLocation(_$[$0-4], $$[$0-4]);
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-2].expressions);
      parser.suggestValueExpressionKeywords($$[$0-2].expressions[$$[$0-2].expressions.length - 1].expression);
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
 break;
 case 1129:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
      parser.applyArgumentTypesToSuggestions($$[$0-3], $$[$0-1].activePosition);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
    
 break;
-case 1136: case 1141:
-this.$ = { types: [ $$[$0-1].toUpperCase() ] };
+case 1136:
+
+     var expression = $$[$0-3];
+     parser.extractExpressionText(expression, $$[$0-3], $$[$0-2], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-5], [{
+       expression: expression,
+       location: {
+         first_line: _$[$0-3].first_line,
+         last_line: _$[$0-1].last_line,
+         first_column: _$[$0-3].first_column,
+         last_column: _$[$0-3].last_column
+       }
+     }]);
+     this.$ = { types: [ $$[$0-1].toUpperCase() ] }
+   
 break;
 case 1138:
 
@@ -2411,6 +2513,9 @@ case 1139: case 1140:
      parser.valueExpressionSuggest();
      this.$ = { types: [ 'T' ] };
    
+break;
+case 1141:
+this.$ = { types: [ $$[$0-1].toUpperCase() ] };
 break;
 case 1144:
 
@@ -2430,11 +2535,25 @@ case 1146: case 1147:
      this.$ = { types: [ 'T' ] };
    
 break;
-case 1148: case 1154:
-this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+case 1148:
+
+     parser.addFunctionArgumentLocations($$[$0-3], [{
+       expression: { text: $$[$0-1] },
+       location: _$[$0-1]
+     }]);
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] }
+   
 break;
-case 1150: case 1155: case 1167:
-this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+case 1149: case 1168:
+
+     this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+   
+break;
+case 1150:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] }
+   
 break;
 case 1151:
 
@@ -2467,6 +2586,17 @@ case 1153:
      }
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
+break;
+case 1154:
+
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+   
+break;
+case 1155: case 1167:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+   
 break;
 case 1156:
 
@@ -2567,7 +2697,10 @@ case 1207:
 this.$ = { inValueEdit: true, cursorAtStart: true };
 break;
 case 1208: case 1209: case 1210:
-this.$ = { suggestKeywords: ['NOT'] };
+
+     this.$ = { suggestKeywords: ['NOT'] }
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
 case 1214: case 1215: case 1216:
 
@@ -2576,14 +2709,21 @@ case 1214: case 1215: case 1216:
      this.$ = { types: ['BOOLEAN'] }
    
 break;
-case 1217: case 1219:
-this.$ = parser.findCaseType($$[$0-1]);
+case 1217:
+
+     this.$ = parser.findCaseType($$[$0-1])
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
-case 1218: case 1221:
+case 1218:
 
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
+break;
+case 1219:
+this.$ = parser.findCaseType($$[$0-1]);
 break;
 case 1220:
 
@@ -2591,6 +2731,12 @@ case 1220:
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
    
+break;
+case 1221:
+
+     $$[$0-3].caseTypes.push($$[$0-1]);
+     this.$ = parser.findCaseType($$[$0-3]);
+   
 break;
 case 1222:
 this.$ = parser.findCaseType($$[$0-2]);
@@ -2649,18 +2795,28 @@ case 1229:
    
 break;
 case 1232:
-this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] };
+
+     this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] }
+     parser.extractExpressionText(this.$, $$[$0]);
+   
 break;
 case 1233:
 
      $$[$0-1].caseTypes.push($$[$0]);
      this.$ = { caseTypes: $$[$0-1].caseTypes, lastType: $$[$0] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 1237:
 
      parser.suggestValueExpressionKeywords($$[$0-2], ['WHEN']);
    
+break;
+case 1239:
+
+     this.$ = $$[$0]
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 1240:
 this.$ = { caseTypes: [{ types: ['T'] }], suggestFilters: $$[$0].suggestFilters };

+ 186 - 30
desktop/core/src/desktop/js/parse/sql/generic/genericSyntaxParser.js

@@ -801,7 +801,7 @@ case 580:
      parser.suggestDatabases({ prependFrom: true, appendDot: true });
    
 break;
-case 581: case 619: case 640: case 653: case 657: case 681: case 706: case 707: case 788: case 790: case 854: case 864: case 871: case 883: case 986: case 1161: case 1199: case 1200:
+case 581: case 619: case 640: case 653: case 657: case 788: case 790: case 854: case 864: case 871: case 883: case 986: case 1161: case 1199:
 this.$ = $$[$0];
 break;
 case 584:
@@ -1172,6 +1172,13 @@ case 679: case 680:
 
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 681: case 706:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 682:
@@ -1179,10 +1186,20 @@ case 682:
      // verifyType($$[$0], 'NUMBER');
      this.$ = $$[$0];
      $$[$0].types = ['NUMBER'];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
-case 683: case 684: case 685: case 686: case 693: case 694: case 695: case 696: case 697: case 698: case 704: case 705: case 726: case 784: case 785: case 843:
-this.$ = { types: [ 'BOOLEAN' ] };
+case 683: case 684: case 685:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 686:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 687:
 
@@ -1195,6 +1212,22 @@ case 689: case 690: case 691: case 692:
 
      parser.addColRefToVariableIfExists($$[$0-2], $$[$0]);
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 693: case 694: case 695: case 696: case 726: case 784: case 785:
+this.$ = { types: [ 'BOOLEAN' ] };
+break;
+case 697:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 698:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 699: case 700:
@@ -1202,6 +1235,7 @@ case 699: case 700:
      // verifyType($$[$0-2], 'BOOLEAN');
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 701: case 702: case 703:
@@ -1209,6 +1243,25 @@ case 701: case 702: case 703:
      // verifyType($$[$0-2], 'NUMBER');
      // verifyType($$[$0], 'NUMBER');
      this.$ = { types: [ 'NUMBER' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 704:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 705:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 707:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 709:
@@ -1622,13 +1675,14 @@ case 811:
 this.$ = { types: ['COLREF'], columnReference: $$[$0].chain };
 break;
 case 812:
-this.$ = { types: [ 'NULL' ] };
+this.$ = { types: [ 'NULL' ], text: $$[$0] };
 break;
 case 813:
 
      // We need to handle arbitrary UDFs here instead of inside UserDefinedFunction or there will be a conflict
      // with columnReference for functions like: db.udf(foo)
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1670,6 +1724,7 @@ break;
 case 818:
 
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1692,6 +1747,7 @@ break;
 case 819: case 1056: case 1057:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].activePosition) {
        parser.applyArgumentTypesToSuggestions($$[$0-1], $$[$0].activePosition);
      }
@@ -1715,23 +1771,39 @@ case 828:
    
 break;
 case 829:
-this.$ = { types: [ 'NUMBER' ] };
+this.$ = { types: [ 'NUMBER' ], text: $$[$0] };
 break;
-case 835: case 837:
+case 835: case 837: case 838:
 this.$ = $$[$0-1] + $$[$0];
 break;
-case 836:
+case 836: case 839:
 this.$ = $$[$0-2] + $$[$0-1] + $$[$0];
 break;
-case 841: case 842:
+case 840:
+this.$ = $$[$0-3] + $$[$0-2] + $$[$0-1] + $$[$0];
+break;
+case 841:
 
      if (/\$\{[^}]*\}/.test($$[$0])) {
        parser.addVariableLocation(_$[$0], $$[$0]);
-       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }] }
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: "'" + $$[$0] + "'" }
      } else {
        this.$ = { types: [ 'STRING' ] }
      }
    
+break;
+case 842:
+
+     if (/\$\{[^}]*\}/.test($$[$0])) {
+       parser.addVariableLocation(_$[$0], $$[$0]);
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: '"' + $$[$0] + '"' }
+     } else {
+       this.$ = { types: [ 'STRING' ], text: '"' + $$[$0] + '"' }
+     }
+   
+break;
+case 843:
+this.$ = { types: [ 'BOOLEAN' ], text: $$[$0] };
 break;
 case 844:
 
@@ -2214,6 +2286,7 @@ break;
 case 1054: case 1055:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].expressions && $$[$0].expressions.length) {
        this.$ = { function: $$[$0-1], expression: $$[$0].expressions[$$[$0].expressions.length - 1].expression, types: ['UDFREF'] }
      } else {
@@ -2226,7 +2299,7 @@ case 1064:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }]
      }
    
 break;
@@ -2273,7 +2346,7 @@ case 1073:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-2].activePosition + 1,
-       expressions: $$[$0-2].expressions.concat([{ expression: undefined, location: _$[$0] }])
+       expressions: $$[$0-2].expressions.concat([{ expression: { text: '' }, location: _$[$0] }])
      }
    
 break;
@@ -2282,7 +2355,7 @@ case 1074:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-4].activePosition + 1,
-       expressions: $$[$0-4].expressions.concat([{ expression: undefined, location: _$[$0-2] }]).concat($$[$0].expressions)
+       expressions: $$[$0-4].expressions.concat([{ expression: { text: '' }, location: _$[$0-2] }]).concat($$[$0].expressions)
      }
    
 break;
@@ -2301,7 +2374,7 @@ case 1076:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
@@ -2311,7 +2384,7 @@ case 1077:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2320,7 +2393,7 @@ case 1078:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2329,18 +2402,30 @@ case 1079:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-3] }, { expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-3] }, { expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
-case 1086: case 1110: case 1129:
-this.$ = { function: $$[$0-2], types: ['UDFREF'] };
+case 1086:
+
+    parser.addFunctionLocation(_$[$0-2], $$[$0-2]);
+    this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+  
 break;
 case 1087:
-this.$ = { function: $$[$0-3], expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression, types: ['UDFREF'] };
+
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
+     this.$ = {
+       function: $$[$0-3],
+       expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression,
+       types: ['UDFREF']
+     }
+   
 break;
 case 1088:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
      parser.valueExpressionSuggest();
      parser.applyArgumentTypesToSuggestions($$[$0-3], 1);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
@@ -2348,18 +2433,35 @@ case 1088:
 break;
 case 1089:
 
+     parser.addFunctionLocation(_$[$0-4], $$[$0-4]);
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-2].expressions);
      parser.suggestValueExpressionKeywords($$[$0-2].expressions[$$[$0-2].expressions.length - 1].expression);
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
 break;
 case 1090:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
      parser.applyArgumentTypesToSuggestions($$[$0-3], $$[$0-1].activePosition);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
    
 break;
-case 1097: case 1102:
-this.$ = { types: [ $$[$0-1].toUpperCase() ] };
+case 1097:
+
+     var expression = $$[$0-3];
+     parser.extractExpressionText(expression, $$[$0-3], $$[$0-2], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-5], [{
+       expression: expression,
+       location: {
+         first_line: _$[$0-3].first_line,
+         last_line: _$[$0-1].last_line,
+         first_column: _$[$0-3].first_column,
+         last_column: _$[$0-3].last_column
+       }
+     }]);
+     this.$ = { types: [ $$[$0-1].toUpperCase() ] }
+   
 break;
 case 1099:
 
@@ -2372,6 +2474,9 @@ case 1100: case 1101:
      parser.valueExpressionSuggest();
      this.$ = { types: [ 'T' ] };
    
+break;
+case 1102:
+this.$ = { types: [ $$[$0-1].toUpperCase() ] };
 break;
 case 1105:
 
@@ -2391,11 +2496,25 @@ case 1107: case 1108:
      this.$ = { types: [ 'T' ] };
    
 break;
-case 1109: case 1115:
-this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+case 1109:
+
+     parser.addFunctionArgumentLocations($$[$0-3], [{
+       expression: { text: $$[$0-1] },
+       location: _$[$0-1]
+     }]);
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] }
+   
 break;
-case 1111: case 1116: case 1128:
-this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+case 1110: case 1129:
+
+     this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+   
+break;
+case 1111:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] }
+   
 break;
 case 1112:
 
@@ -2428,6 +2547,17 @@ case 1114:
      }
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
+break;
+case 1115:
+
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+   
+break;
+case 1116: case 1128:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+   
 break;
 case 1117:
 
@@ -2528,7 +2658,10 @@ case 1168:
 this.$ = { inValueEdit: true, cursorAtStart: true };
 break;
 case 1169: case 1170: case 1171:
-this.$ = { suggestKeywords: ['NOT'] };
+
+     this.$ = { suggestKeywords: ['NOT'] }
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
 case 1175: case 1176: case 1177:
 
@@ -2537,14 +2670,21 @@ case 1175: case 1176: case 1177:
      this.$ = { types: ['BOOLEAN'] }
    
 break;
-case 1178: case 1180:
-this.$ = parser.findCaseType($$[$0-1]);
+case 1178:
+
+     this.$ = parser.findCaseType($$[$0-1])
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
-case 1179: case 1182:
+case 1179:
 
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
+break;
+case 1180:
+this.$ = parser.findCaseType($$[$0-1]);
 break;
 case 1181:
 
@@ -2552,6 +2692,12 @@ case 1181:
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
    
+break;
+case 1182:
+
+     $$[$0-3].caseTypes.push($$[$0-1]);
+     this.$ = parser.findCaseType($$[$0-3]);
+   
 break;
 case 1183:
 this.$ = parser.findCaseType($$[$0-2]);
@@ -2610,18 +2756,28 @@ case 1190:
    
 break;
 case 1193:
-this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] };
+
+     this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] }
+     parser.extractExpressionText(this.$, $$[$0]);
+   
 break;
 case 1194:
 
      $$[$0-1].caseTypes.push($$[$0]);
      this.$ = { caseTypes: $$[$0-1].caseTypes, lastType: $$[$0] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 1198:
 
      parser.suggestValueExpressionKeywords($$[$0-2], ['WHEN']);
    
+break;
+case 1200:
+
+     this.$ = $$[$0]
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 1201:
 this.$ = { caseTypes: [{ types: ['T'] }], suggestFilters: $$[$0].suggestFilters };

+ 24 - 0
desktop/core/src/desktop/js/parse/sql/generic/test/genericAutocompleteParser.Locations.test.js

@@ -64,6 +64,14 @@ describe('genericAutocompleteParser.js locations', () => {
           location: { first_line: 1, last_line: 1, first_column: 8, last_column: 10 },
           function: 'cos'
         },
+        {
+          type: 'functionArgument',
+          location: { first_line: 1, last_line: 1, first_column: 12, last_column: 13 },
+          function: 'cos',
+          argumentPosition: 0,
+          identifierChain: [{ name: 'cos' }],
+          expression: { types: ['NUMBER'], text: '1' }
+        },
         {
           type: 'alias',
           source: 'column',
@@ -1038,6 +1046,14 @@ describe('genericAutocompleteParser.js locations', () => {
             location: { first_line: 1, last_line: 1, first_column: 13, last_column: 15 },
             function: 'cos'
           },
+          {
+            type: 'functionArgument',
+            location: { first_line: 1, last_line: 1, first_column: 17, last_column: 22 },
+            function: 'cos',
+            argumentPosition: 0,
+            identifierChain: [{ name: 'cos' }],
+            expression: { types: ['COLREF'], columnReference: [{ name: 'boo' }, { name: 'a' }] }
+          },
           {
             type: 'table',
             location: { first_line: 1, last_line: 1, first_column: 17, last_column: 20 },
@@ -1280,6 +1296,14 @@ describe('genericAutocompleteParser.js locations', () => {
           location: { first_line: 1, last_line: 1, first_column: 8, last_column: 12 },
           function: 'count'
         },
+        {
+          type: 'functionArgument',
+          location: { first_line: 1, last_line: 1, first_column: 14, last_column: 15 },
+          function: 'count',
+          argumentPosition: 0,
+          identifierChain: [{ name: 'count' }],
+          expression: { text: '*' }
+        },
         {
           type: 'table',
           location: { first_line: 1, last_line: 1, first_column: 22, last_column: 31 },

+ 115 - 0
desktop/core/src/desktop/js/parse/sql/generic/test/genericAutocompleteParser.Select.test.js

@@ -371,6 +371,97 @@ describe('genericAutocompleteParser.js SELECT statements', () => {
   });
 
   describe('Select List Completion', () => {
+    it('should handle "select testUdf(not id), cos(-1), sin(1+id) from autocomp_test;"', () => {
+      assertAutoComplete({
+        beforeCursor: 'select testUdf(not id), cos(-1), sin(1+id) from autocomp_test;',
+        afterCursor: '',
+        noErrors: true,
+        containsKeywords: ['SELECT'],
+        expectedResult: {
+          locations: [
+            {
+              type: 'statement',
+              location: { first_line: 1, last_line: 1, first_column: 1, last_column: 62 }
+            },
+            {
+              type: 'selectList',
+              missing: false,
+              location: { first_line: 1, last_line: 1, first_column: 8, last_column: 43 }
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 8, last_column: 14 },
+              function: 'testudf'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 16, last_column: 22 },
+              function: 'testudf',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'testUdf' }],
+              expression: { types: ['BOOLEAN'], text: 'not id' }
+            },
+            {
+              type: 'column',
+              location: { first_line: 1, last_line: 1, first_column: 20, last_column: 22 },
+              identifierChain: [{ name: 'id' }],
+              qualified: false,
+              tables: [{ identifierChain: [{ name: 'autocomp_test' }] }]
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 25, last_column: 27 },
+              function: 'cos'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 29, last_column: 31 },
+              function: 'cos',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'cos' }],
+              expression: { types: ['NUMBER'], text: '- 1' }
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
+              function: 'sin'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 38, last_column: 42 },
+              function: 'sin',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'sin' }],
+              expression: { types: ['NUMBER'], text: '1 + id' }
+            },
+            {
+              type: 'column',
+              location: { first_line: 1, last_line: 1, first_column: 40, last_column: 42 },
+              identifierChain: [{ name: 'id' }],
+              qualified: false,
+              tables: [{ identifierChain: [{ name: 'autocomp_test' }] }]
+            },
+            {
+              type: 'table',
+              location: { first_line: 1, last_line: 1, first_column: 49, last_column: 62 },
+              identifierChain: [{ name: 'autocomp_test' }]
+            },
+            {
+              type: 'whereClause',
+              missing: true,
+              location: { first_line: 1, last_line: 1, first_column: 62, last_column: 62 }
+            },
+            {
+              type: 'limitClause',
+              missing: true,
+              location: { first_line: 1, last_line: 1, first_column: 62, last_column: 62 }
+            }
+          ],
+          lowerCase: true
+        }
+      });
+    });
+
     it('should handle "select count(*), tst.count, avg (id), avg from autocomp_test tst;"', () => {
       assertAutoComplete({
         beforeCursor: 'select count(*), tst.count, avg (id), avg from autocomp_test tst;',
@@ -393,6 +484,14 @@ describe('genericAutocompleteParser.js SELECT statements', () => {
               location: { first_line: 1, last_line: 1, first_column: 8, last_column: 12 },
               function: 'count'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 14, last_column: 15 },
+              function: 'count',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'count' }],
+              expression: { text: '*' }
+            },
             {
               type: 'table',
               location: { first_line: 1, last_line: 1, first_column: 18, last_column: 21 },
@@ -410,6 +509,14 @@ describe('genericAutocompleteParser.js SELECT statements', () => {
               location: { first_line: 1, last_line: 1, first_column: 29, last_column: 32 },
               function: 'avg'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
+              function: 'avg',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'avg' }],
+              expression: { types: ['COLREF'], columnReference: [{ name: 'id' }] }
+            },
             {
               type: 'column',
               location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
@@ -2192,6 +2299,14 @@ describe('genericAutocompleteParser.js SELECT statements', () => {
               identifierChain: [{ name: 'db' }, { name: 'customUdf' }],
               function: 'customudf'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 21, last_column: 24 },
+              function: 'customudf',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'db' }, { name: 'customUdf' }],
+              expression: { types: ['COLREF'], columnReference: [{ name: 'col' }] }
+            },
             {
               type: 'column',
               location: { first_line: 1, last_line: 1, first_column: 21, last_column: 24 },

+ 186 - 30
desktop/core/src/desktop/js/parse/sql/ksql/ksqlAutocompleteParser.js

@@ -574,7 +574,7 @@ case 261:
      parser.suggestDatabases({ prependFrom: true, appendDot: true });
    
 break;
-case 267: case 268: case 269: case 626: case 654: case 675: case 688: case 692: case 716: case 741: case 742: case 823: case 825: case 889: case 899: case 906: case 918: case 1007: case 1191: case 1192:
+case 267: case 268: case 269: case 626: case 654: case 675: case 688: case 692: case 823: case 825: case 889: case 899: case 906: case 918: case 1007: case 1191:
 this.$ = $$[$0];
 break;
 case 270: case 271:
@@ -1210,6 +1210,13 @@ case 714: case 715:
 
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 716: case 741:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 717:
@@ -1217,10 +1224,20 @@ case 717:
      // verifyType($$[$0], 'NUMBER');
      this.$ = $$[$0];
      $$[$0].types = ['NUMBER'];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
-case 718: case 719: case 720: case 721: case 728: case 729: case 730: case 731: case 732: case 733: case 739: case 740: case 761: case 819: case 820: case 878:
-this.$ = { types: [ 'BOOLEAN' ] };
+case 718: case 719: case 720:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 721:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 722:
 
@@ -1233,6 +1250,22 @@ case 724: case 725: case 726: case 727:
 
      parser.addColRefToVariableIfExists($$[$0-2], $$[$0]);
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 728: case 729: case 730: case 731: case 761: case 819: case 820:
+this.$ = { types: [ 'BOOLEAN' ] };
+break;
+case 732:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 733:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 734: case 735:
@@ -1240,6 +1273,7 @@ case 734: case 735:
      // verifyType($$[$0-2], 'BOOLEAN');
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 736: case 737: case 738:
@@ -1247,6 +1281,25 @@ case 736: case 737: case 738:
      // verifyType($$[$0-2], 'NUMBER');
      // verifyType($$[$0], 'NUMBER');
      this.$ = { types: [ 'NUMBER' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 739:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 740:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 742:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 744:
@@ -1660,13 +1713,14 @@ case 846:
 this.$ = { types: ['COLREF'], columnReference: $$[$0].chain };
 break;
 case 847:
-this.$ = { types: [ 'NULL' ] };
+this.$ = { types: [ 'NULL' ], text: $$[$0] };
 break;
 case 848:
 
      // We need to handle arbitrary UDFs here instead of inside UserDefinedFunction or there will be a conflict
      // with columnReference for functions like: db.udf(foo)
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1708,6 +1762,7 @@ break;
 case 853:
 
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1730,6 +1785,7 @@ break;
 case 854: case 1081: case 1082:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].activePosition) {
        parser.applyArgumentTypesToSuggestions($$[$0-1], $$[$0].activePosition);
      }
@@ -1753,23 +1809,39 @@ case 863:
    
 break;
 case 864:
-this.$ = { types: [ 'NUMBER' ] };
+this.$ = { types: [ 'NUMBER' ], text: $$[$0] };
 break;
-case 870: case 872:
+case 870: case 872: case 873:
 this.$ = $$[$0-1] + $$[$0];
 break;
-case 871:
+case 871: case 874:
 this.$ = $$[$0-2] + $$[$0-1] + $$[$0];
 break;
-case 876: case 877:
+case 875:
+this.$ = $$[$0-3] + $$[$0-2] + $$[$0-1] + $$[$0];
+break;
+case 876:
 
      if (/\$\{[^}]*\}/.test($$[$0])) {
        parser.addVariableLocation(_$[$0], $$[$0]);
-       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }] }
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: "'" + $$[$0] + "'" }
      } else {
        this.$ = { types: [ 'STRING' ] }
      }
    
+break;
+case 877:
+
+     if (/\$\{[^}]*\}/.test($$[$0])) {
+       parser.addVariableLocation(_$[$0], $$[$0]);
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: '"' + $$[$0] + '"' }
+     } else {
+       this.$ = { types: [ 'STRING' ], text: '"' + $$[$0] + '"' }
+     }
+   
+break;
+case 878:
+this.$ = { types: [ 'BOOLEAN' ], text: $$[$0] };
 break;
 case 879:
 
@@ -2246,6 +2318,7 @@ break;
 case 1079: case 1080:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].expressions && $$[$0].expressions.length) {
        this.$ = { function: $$[$0-1], expression: $$[$0].expressions[$$[$0].expressions.length - 1].expression, types: ['UDFREF'] }
      } else {
@@ -2258,7 +2331,7 @@ case 1089:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }]
      }
    
 break;
@@ -2305,7 +2378,7 @@ case 1098:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-2].activePosition + 1,
-       expressions: $$[$0-2].expressions.concat([{ expression: undefined, location: _$[$0] }])
+       expressions: $$[$0-2].expressions.concat([{ expression: { text: '' }, location: _$[$0] }])
      }
    
 break;
@@ -2314,7 +2387,7 @@ case 1099:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-4].activePosition + 1,
-       expressions: $$[$0-4].expressions.concat([{ expression: undefined, location: _$[$0-2] }]).concat($$[$0].expressions)
+       expressions: $$[$0-4].expressions.concat([{ expression: { text: '' }, location: _$[$0-2] }]).concat($$[$0].expressions)
      }
    
 break;
@@ -2333,7 +2406,7 @@ case 1101:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
@@ -2343,7 +2416,7 @@ case 1102:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2352,7 +2425,7 @@ case 1103:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2361,18 +2434,30 @@ case 1104:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-3] }, { expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-3] }, { expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
-case 1111: case 1135: case 1154:
-this.$ = { function: $$[$0-2], types: ['UDFREF'] };
+case 1111:
+
+    parser.addFunctionLocation(_$[$0-2], $$[$0-2]);
+    this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+  
 break;
 case 1112:
-this.$ = { function: $$[$0-3], expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression, types: ['UDFREF'] };
+
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
+     this.$ = {
+       function: $$[$0-3],
+       expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression,
+       types: ['UDFREF']
+     }
+   
 break;
 case 1113:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
      parser.valueExpressionSuggest();
      parser.applyArgumentTypesToSuggestions($$[$0-3], 1);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
@@ -2380,18 +2465,35 @@ case 1113:
 break;
 case 1114:
 
+     parser.addFunctionLocation(_$[$0-4], $$[$0-4]);
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-2].expressions);
      parser.suggestValueExpressionKeywords($$[$0-2].expressions[$$[$0-2].expressions.length - 1].expression);
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
 break;
 case 1115:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
      parser.applyArgumentTypesToSuggestions($$[$0-3], $$[$0-1].activePosition);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
    
 break;
-case 1122: case 1127:
-this.$ = { types: [ $$[$0-1].toUpperCase() ] };
+case 1122:
+
+     var expression = $$[$0-3];
+     parser.extractExpressionText(expression, $$[$0-3], $$[$0-2], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-5], [{
+       expression: expression,
+       location: {
+         first_line: _$[$0-3].first_line,
+         last_line: _$[$0-1].last_line,
+         first_column: _$[$0-3].first_column,
+         last_column: _$[$0-3].last_column
+       }
+     }]);
+     this.$ = { types: [ $$[$0-1].toUpperCase() ] }
+   
 break;
 case 1124:
 
@@ -2404,6 +2506,9 @@ case 1125: case 1126:
      parser.valueExpressionSuggest();
      this.$ = { types: [ 'T' ] };
    
+break;
+case 1127:
+this.$ = { types: [ $$[$0-1].toUpperCase() ] };
 break;
 case 1130:
 
@@ -2423,11 +2528,25 @@ case 1132: case 1133:
      this.$ = { types: [ 'T' ] };
    
 break;
-case 1134: case 1140:
-this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+case 1134:
+
+     parser.addFunctionArgumentLocations($$[$0-3], [{
+       expression: { text: $$[$0-1] },
+       location: _$[$0-1]
+     }]);
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] }
+   
 break;
-case 1136: case 1141: case 1153:
-this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+case 1135: case 1154:
+
+     this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+   
+break;
+case 1136:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] }
+   
 break;
 case 1137:
 
@@ -2460,6 +2579,17 @@ case 1139:
      }
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
+break;
+case 1140:
+
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+   
+break;
+case 1141: case 1153:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+   
 break;
 case 1142:
 
@@ -2538,7 +2668,10 @@ case 1160:
 this.$ = { inValueEdit: true, cursorAtStart: true };
 break;
 case 1161: case 1162: case 1163:
-this.$ = { suggestKeywords: ['NOT'] };
+
+     this.$ = { suggestKeywords: ['NOT'] }
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
 case 1167: case 1168: case 1169:
 
@@ -2547,14 +2680,21 @@ case 1167: case 1168: case 1169:
      this.$ = { types: ['BOOLEAN'] }
    
 break;
-case 1170: case 1172:
-this.$ = parser.findCaseType($$[$0-1]);
+case 1170:
+
+     this.$ = parser.findCaseType($$[$0-1])
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
-case 1171: case 1174:
+case 1171:
 
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
+break;
+case 1172:
+this.$ = parser.findCaseType($$[$0-1]);
 break;
 case 1173:
 
@@ -2562,6 +2702,12 @@ case 1173:
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
    
+break;
+case 1174:
+
+     $$[$0-3].caseTypes.push($$[$0-1]);
+     this.$ = parser.findCaseType($$[$0-3]);
+   
 break;
 case 1175:
 this.$ = parser.findCaseType($$[$0-2]);
@@ -2620,18 +2766,28 @@ case 1182:
    
 break;
 case 1185:
-this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] };
+
+     this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] }
+     parser.extractExpressionText(this.$, $$[$0]);
+   
 break;
 case 1186:
 
      $$[$0-1].caseTypes.push($$[$0]);
      this.$ = { caseTypes: $$[$0-1].caseTypes, lastType: $$[$0] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 1190:
 
      parser.suggestValueExpressionKeywords($$[$0-2], ['WHEN']);
    
+break;
+case 1192:
+
+     this.$ = $$[$0]
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 1193:
 this.$ = { caseTypes: [{ types: ['T'] }], suggestFilters: $$[$0].suggestFilters };

+ 186 - 30
desktop/core/src/desktop/js/parse/sql/ksql/ksqlSyntaxParser.js

@@ -784,7 +784,7 @@ case 560:
      parser.suggestDatabases({ prependFrom: true, appendDot: true });
    
 break;
-case 561: case 599: case 620: case 633: case 637: case 661: case 686: case 687: case 768: case 770: case 834: case 844: case 851: case 863: case 966: case 1152: case 1153:
+case 561: case 599: case 620: case 633: case 637: case 768: case 770: case 834: case 844: case 851: case 863: case 966: case 1152:
 this.$ = $$[$0];
 break;
 case 564:
@@ -1160,6 +1160,13 @@ case 659: case 660:
 
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 661: case 686:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 662:
@@ -1167,10 +1174,20 @@ case 662:
      // verifyType($$[$0], 'NUMBER');
      this.$ = $$[$0];
      $$[$0].types = ['NUMBER'];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
-case 663: case 664: case 665: case 666: case 673: case 674: case 675: case 676: case 677: case 678: case 684: case 685: case 706: case 764: case 765: case 823:
-this.$ = { types: [ 'BOOLEAN' ] };
+case 663: case 664: case 665:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 666:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 667:
 
@@ -1183,6 +1200,22 @@ case 669: case 670: case 671: case 672:
 
      parser.addColRefToVariableIfExists($$[$0-2], $$[$0]);
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 673: case 674: case 675: case 676: case 706: case 764: case 765:
+this.$ = { types: [ 'BOOLEAN' ] };
+break;
+case 677:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 678:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 679: case 680:
@@ -1190,6 +1223,7 @@ case 679: case 680:
      // verifyType($$[$0-2], 'BOOLEAN');
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 681: case 682: case 683:
@@ -1197,6 +1231,25 @@ case 681: case 682: case 683:
      // verifyType($$[$0-2], 'NUMBER');
      // verifyType($$[$0], 'NUMBER');
      this.$ = { types: [ 'NUMBER' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 684:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 685:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 687:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 689:
@@ -1610,13 +1663,14 @@ case 791:
 this.$ = { types: ['COLREF'], columnReference: $$[$0].chain };
 break;
 case 792:
-this.$ = { types: [ 'NULL' ] };
+this.$ = { types: [ 'NULL' ], text: $$[$0] };
 break;
 case 793:
 
      // We need to handle arbitrary UDFs here instead of inside UserDefinedFunction or there will be a conflict
      // with columnReference for functions like: db.udf(foo)
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1658,6 +1712,7 @@ break;
 case 798:
 
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1680,6 +1735,7 @@ break;
 case 799: case 1042: case 1043:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].activePosition) {
        parser.applyArgumentTypesToSuggestions($$[$0-1], $$[$0].activePosition);
      }
@@ -1703,23 +1759,39 @@ case 808:
    
 break;
 case 809:
-this.$ = { types: [ 'NUMBER' ] };
+this.$ = { types: [ 'NUMBER' ], text: $$[$0] };
 break;
-case 815: case 817:
+case 815: case 817: case 818:
 this.$ = $$[$0-1] + $$[$0];
 break;
-case 816:
+case 816: case 819:
 this.$ = $$[$0-2] + $$[$0-1] + $$[$0];
 break;
-case 821: case 822:
+case 820:
+this.$ = $$[$0-3] + $$[$0-2] + $$[$0-1] + $$[$0];
+break;
+case 821:
 
      if (/\$\{[^}]*\}/.test($$[$0])) {
        parser.addVariableLocation(_$[$0], $$[$0]);
-       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }] }
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: "'" + $$[$0] + "'" }
      } else {
        this.$ = { types: [ 'STRING' ] }
      }
    
+break;
+case 822:
+
+     if (/\$\{[^}]*\}/.test($$[$0])) {
+       parser.addVariableLocation(_$[$0], $$[$0]);
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: '"' + $$[$0] + '"' }
+     } else {
+       this.$ = { types: [ 'STRING' ], text: '"' + $$[$0] + '"' }
+     }
+   
+break;
+case 823:
+this.$ = { types: [ 'BOOLEAN' ], text: $$[$0] };
 break;
 case 824:
 
@@ -2207,6 +2279,7 @@ break;
 case 1040: case 1041:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].expressions && $$[$0].expressions.length) {
        this.$ = { function: $$[$0-1], expression: $$[$0].expressions[$$[$0].expressions.length - 1].expression, types: ['UDFREF'] }
      } else {
@@ -2219,7 +2292,7 @@ case 1050:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }]
      }
    
 break;
@@ -2266,7 +2339,7 @@ case 1059:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-2].activePosition + 1,
-       expressions: $$[$0-2].expressions.concat([{ expression: undefined, location: _$[$0] }])
+       expressions: $$[$0-2].expressions.concat([{ expression: { text: '' }, location: _$[$0] }])
      }
    
 break;
@@ -2275,7 +2348,7 @@ case 1060:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-4].activePosition + 1,
-       expressions: $$[$0-4].expressions.concat([{ expression: undefined, location: _$[$0-2] }]).concat($$[$0].expressions)
+       expressions: $$[$0-4].expressions.concat([{ expression: { text: '' }, location: _$[$0-2] }]).concat($$[$0].expressions)
      }
    
 break;
@@ -2294,7 +2367,7 @@ case 1062:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
@@ -2304,7 +2377,7 @@ case 1063:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2313,7 +2386,7 @@ case 1064:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2322,18 +2395,30 @@ case 1065:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-3] }, { expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-3] }, { expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
-case 1072: case 1096: case 1115:
-this.$ = { function: $$[$0-2], types: ['UDFREF'] };
+case 1072:
+
+    parser.addFunctionLocation(_$[$0-2], $$[$0-2]);
+    this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+  
 break;
 case 1073:
-this.$ = { function: $$[$0-3], expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression, types: ['UDFREF'] };
+
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
+     this.$ = {
+       function: $$[$0-3],
+       expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression,
+       types: ['UDFREF']
+     }
+   
 break;
 case 1074:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
      parser.valueExpressionSuggest();
      parser.applyArgumentTypesToSuggestions($$[$0-3], 1);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
@@ -2341,18 +2426,35 @@ case 1074:
 break;
 case 1075:
 
+     parser.addFunctionLocation(_$[$0-4], $$[$0-4]);
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-2].expressions);
      parser.suggestValueExpressionKeywords($$[$0-2].expressions[$$[$0-2].expressions.length - 1].expression);
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
 break;
 case 1076:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
      parser.applyArgumentTypesToSuggestions($$[$0-3], $$[$0-1].activePosition);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
    
 break;
-case 1083: case 1088:
-this.$ = { types: [ $$[$0-1].toUpperCase() ] };
+case 1083:
+
+     var expression = $$[$0-3];
+     parser.extractExpressionText(expression, $$[$0-3], $$[$0-2], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-5], [{
+       expression: expression,
+       location: {
+         first_line: _$[$0-3].first_line,
+         last_line: _$[$0-1].last_line,
+         first_column: _$[$0-3].first_column,
+         last_column: _$[$0-3].last_column
+       }
+     }]);
+     this.$ = { types: [ $$[$0-1].toUpperCase() ] }
+   
 break;
 case 1085:
 
@@ -2365,6 +2467,9 @@ case 1086: case 1087:
      parser.valueExpressionSuggest();
      this.$ = { types: [ 'T' ] };
    
+break;
+case 1088:
+this.$ = { types: [ $$[$0-1].toUpperCase() ] };
 break;
 case 1091:
 
@@ -2384,11 +2489,25 @@ case 1093: case 1094:
      this.$ = { types: [ 'T' ] };
    
 break;
-case 1095: case 1101:
-this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+case 1095:
+
+     parser.addFunctionArgumentLocations($$[$0-3], [{
+       expression: { text: $$[$0-1] },
+       location: _$[$0-1]
+     }]);
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] }
+   
 break;
-case 1097: case 1102: case 1114:
-this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+case 1096: case 1115:
+
+     this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+   
+break;
+case 1097:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] }
+   
 break;
 case 1098:
 
@@ -2421,6 +2540,17 @@ case 1100:
      }
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
+break;
+case 1101:
+
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+   
+break;
+case 1102: case 1114:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+   
 break;
 case 1103:
 
@@ -2499,7 +2629,10 @@ case 1121:
 this.$ = { inValueEdit: true, cursorAtStart: true };
 break;
 case 1122: case 1123: case 1124:
-this.$ = { suggestKeywords: ['NOT'] };
+
+     this.$ = { suggestKeywords: ['NOT'] }
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
 case 1128: case 1129: case 1130:
 
@@ -2508,14 +2641,21 @@ case 1128: case 1129: case 1130:
      this.$ = { types: ['BOOLEAN'] }
    
 break;
-case 1131: case 1133:
-this.$ = parser.findCaseType($$[$0-1]);
+case 1131:
+
+     this.$ = parser.findCaseType($$[$0-1])
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
-case 1132: case 1135:
+case 1132:
 
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
+break;
+case 1133:
+this.$ = parser.findCaseType($$[$0-1]);
 break;
 case 1134:
 
@@ -2523,6 +2663,12 @@ case 1134:
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
    
+break;
+case 1135:
+
+     $$[$0-3].caseTypes.push($$[$0-1]);
+     this.$ = parser.findCaseType($$[$0-3]);
+   
 break;
 case 1136:
 this.$ = parser.findCaseType($$[$0-2]);
@@ -2581,18 +2727,28 @@ case 1143:
    
 break;
 case 1146:
-this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] };
+
+     this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] }
+     parser.extractExpressionText(this.$, $$[$0]);
+   
 break;
 case 1147:
 
      $$[$0-1].caseTypes.push($$[$0]);
      this.$ = { caseTypes: $$[$0-1].caseTypes, lastType: $$[$0] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 1151:
 
      parser.suggestValueExpressionKeywords($$[$0-2], ['WHEN']);
    
+break;
+case 1153:
+
+     this.$ = $$[$0]
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 1154:
 this.$ = { caseTypes: [{ types: ['T'] }], suggestFilters: $$[$0].suggestFilters };

+ 24 - 0
desktop/core/src/desktop/js/parse/sql/ksql/test/ksqlAutocompleteParser.Locations.test.js

@@ -64,6 +64,14 @@ describe('ksqlAutocompleteParser.js locations', () => {
           location: { first_line: 1, last_line: 1, first_column: 8, last_column: 10 },
           function: 'cos'
         },
+        {
+          type: 'functionArgument',
+          location: { first_line: 1, last_line: 1, first_column: 12, last_column: 13 },
+          function: 'cos',
+          argumentPosition: 0,
+          identifierChain: [{ name: 'cos' }],
+          expression: { types: ['NUMBER'], text: '1' }
+        },
         {
           type: 'alias',
           source: 'column',
@@ -1038,6 +1046,14 @@ describe('ksqlAutocompleteParser.js locations', () => {
             location: { first_line: 1, last_line: 1, first_column: 13, last_column: 15 },
             function: 'cos'
           },
+          {
+            type: 'functionArgument',
+            location: { first_line: 1, last_line: 1, first_column: 17, last_column: 22 },
+            function: 'cos',
+            argumentPosition: 0,
+            identifierChain: [{ name: 'cos' }],
+            expression: { types: ['COLREF'], columnReference: [{ name: 'boo' }, { name: 'a' }] }
+          },
           {
             type: 'table',
             location: { first_line: 1, last_line: 1, first_column: 17, last_column: 20 },
@@ -1280,6 +1296,14 @@ describe('ksqlAutocompleteParser.js locations', () => {
           location: { first_line: 1, last_line: 1, first_column: 8, last_column: 12 },
           function: 'count'
         },
+        {
+          type: 'functionArgument',
+          location: { first_line: 1, last_line: 1, first_column: 14, last_column: 15 },
+          function: 'count',
+          argumentPosition: 0,
+          identifierChain: [{ name: 'count' }],
+          expression: { text: '*' }
+        },
         {
           type: 'table',
           location: { first_line: 1, last_line: 1, first_column: 22, last_column: 31 },

+ 822 - 42
desktop/core/src/desktop/js/parse/sql/ksql/test/ksqlAutocompleteParser.Select.test.js

@@ -165,6 +165,7 @@ describe('ksqlAutocompleteParser.js SELECT statements', () => {
           'HAVING',
           'ORDER BY',
           'LIMIT',
+          'UNION',
           'FULL JOIN',
           'FULL OUTER JOIN',
           'INNER JOIN',
@@ -370,6 +371,97 @@ describe('ksqlAutocompleteParser.js SELECT statements', () => {
   });
 
   describe('Select List Completion', () => {
+    it('should handle "select testUdf(not id), cos(-1), sin(1+id) from autocomp_test;"', () => {
+      assertAutoComplete({
+        beforeCursor: 'select testUdf(not id), cos(-1), sin(1+id) from autocomp_test;',
+        afterCursor: '',
+        noErrors: true,
+        containsKeywords: ['SELECT'],
+        expectedResult: {
+          locations: [
+            {
+              type: 'statement',
+              location: { first_line: 1, last_line: 1, first_column: 1, last_column: 62 }
+            },
+            {
+              type: 'selectList',
+              missing: false,
+              location: { first_line: 1, last_line: 1, first_column: 8, last_column: 43 }
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 8, last_column: 14 },
+              function: 'testudf'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 16, last_column: 22 },
+              function: 'testudf',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'testUdf' }],
+              expression: { types: ['BOOLEAN'], text: 'not id' }
+            },
+            {
+              type: 'column',
+              location: { first_line: 1, last_line: 1, first_column: 20, last_column: 22 },
+              identifierChain: [{ name: 'id' }],
+              qualified: false,
+              tables: [{ identifierChain: [{ name: 'autocomp_test' }] }]
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 25, last_column: 27 },
+              function: 'cos'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 29, last_column: 31 },
+              function: 'cos',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'cos' }],
+              expression: { types: ['NUMBER'], text: '- 1' }
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
+              function: 'sin'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 38, last_column: 42 },
+              function: 'sin',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'sin' }],
+              expression: { types: ['NUMBER'], text: '1 + id' }
+            },
+            {
+              type: 'column',
+              location: { first_line: 1, last_line: 1, first_column: 40, last_column: 42 },
+              identifierChain: [{ name: 'id' }],
+              qualified: false,
+              tables: [{ identifierChain: [{ name: 'autocomp_test' }] }]
+            },
+            {
+              type: 'table',
+              location: { first_line: 1, last_line: 1, first_column: 49, last_column: 62 },
+              identifierChain: [{ name: 'autocomp_test' }]
+            },
+            {
+              type: 'whereClause',
+              missing: true,
+              location: { first_line: 1, last_line: 1, first_column: 62, last_column: 62 }
+            },
+            {
+              type: 'limitClause',
+              missing: true,
+              location: { first_line: 1, last_line: 1, first_column: 62, last_column: 62 }
+            }
+          ],
+          lowerCase: true
+        }
+      });
+    });
+
     it('should handle "select count(*), tst.count, avg (id), avg from autocomp_test tst;"', () => {
       assertAutoComplete({
         beforeCursor: 'select count(*), tst.count, avg (id), avg from autocomp_test tst;',
@@ -392,6 +484,14 @@ describe('ksqlAutocompleteParser.js SELECT statements', () => {
               location: { first_line: 1, last_line: 1, first_column: 8, last_column: 12 },
               function: 'count'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 14, last_column: 15 },
+              function: 'count',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'count' }],
+              expression: { text: '*' }
+            },
             {
               type: 'table',
               location: { first_line: 1, last_line: 1, first_column: 18, last_column: 21 },
@@ -409,6 +509,14 @@ describe('ksqlAutocompleteParser.js SELECT statements', () => {
               location: { first_line: 1, last_line: 1, first_column: 29, last_column: 32 },
               function: 'avg'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
+              function: 'avg',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'avg' }],
+              expression: { types: ['COLREF'], columnReference: [{ name: 'id' }] }
+            },
             {
               type: 'column',
               location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
@@ -1227,63 +1335,448 @@ describe('ksqlAutocompleteParser.js SELECT statements', () => {
         }
       });
     });
-  });
+  });
+
+  describe('Variable References', () => {
+    it('should suggest tables for "SELECT | FROM ${some_variable};"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT ',
+        afterCursor: ' FROM ${some_variable};',
+        containsKeywords: ['*', 'ALL', 'DISTINCT'],
+        expectedResult: {
+          lowerCase: false,
+          suggestAggregateFunctions: {
+            tables: [{ identifierChain: [{ name: '${some_variable}' }] }]
+          },
+          suggestAnalyticFunctions: true,
+          suggestFunctions: {},
+          suggestColumns: {
+            source: 'select',
+            tables: [{ identifierChain: [{ name: '${some_variable}' }] }]
+          }
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT * FROM testTable WHERE ${some_variable} |"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT * FROM testTable WHERE ${some_variable} ',
+        afterCursor: '',
+        containsKeywords: ['<', 'BETWEEN'],
+        containsColRefKeywords: true,
+        expectedResult: {
+          lowerCase: false,
+          suggestGroupBys: {
+            prefix: 'GROUP BY',
+            tables: [{ identifierChain: [{ name: 'testTable' }] }]
+          },
+          suggestOrderBys: {
+            prefix: 'ORDER BY',
+            tables: [{ identifierChain: [{ name: 'testTable' }] }]
+          },
+          colRef: { identifierChain: [{ name: 'testTable' }, { name: '${some_variable}' }] }
+        }
+      });
+    });
+
+    it('should suggest columns for "SELECT * FROM testTable WHERE ${some_variable} + 1 = |"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT * FROM testTable WHERE ${some_variable} + 1 = ',
+        afterCursor: '',
+        containsKeywords: ['CASE', 'NULL'],
+        expectedResult: {
+          lowerCase: false,
+          suggestFunctions: { types: ['NUMBER'] },
+          suggestColumns: {
+            source: 'where',
+            types: ['NUMBER'],
+            tables: [{ identifierChain: [{ name: 'testTable' }] }]
+          }
+        }
+      });
+    });
+  });
+
+  describe('Window and analytic functions', () => {
+    it('should handle "SELECT row_number() OVER (PARTITION BY a) FROM testTable;|"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT row_number() OVER (PARTITION BY a) FROM testTable;',
+        afterCursor: '',
+        noErrors: true,
+        containsKeywords: ['SELECT'],
+        expectedResult: {
+          lowerCase: false
+        }
+      });
+    });
+
+    it('should handle "SELECT COUNT(DISTINCT a) OVER (PARTITION by c) FROM testTable;|"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT COUNT(DISTINCT a) OVER (PARTITION by c) FROM testTable;',
+        afterCursor: '',
+        noErrors: true,
+        containsKeywords: ['SELECT'],
+        expectedResult: {
+          lowerCase: false
+        }
+      });
+    });
+
+    it('should suggest analytical functions for "SELECT |"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT ',
+        afterCursor: '',
+        containsKeywords: ['*'],
+        expectedResult: {
+          lowerCase: false,
+          suggestAggregateFunctions: { tables: [] },
+          suggestAnalyticFunctions: true,
+          suggestFunctions: {},
+          suggestTables: { prependQuestionMark: true, prependFrom: true },
+          suggestDatabases: { prependQuestionMark: true, prependFrom: true, appendDot: true }
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT row_number() |"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT row_number() ',
+        afterCursor: '',
+        containsKeywords: ['OVER'],
+        expectedResult: {
+          lowerCase: false
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT row_number() | FROM testTable"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT row_number() ',
+        afterCursor: ' FROM testTable',
+        containsKeywords: ['OVER'],
+        expectedResult: {
+          lowerCase: false
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT row_number() |, b, c FROM testTable"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT row_number() ',
+        afterCursor: ', b, c FROM testTable',
+        containsKeywords: ['OVER'],
+        expectedResult: {
+          lowerCase: false
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT count(DISTINCT a) |"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT count(DISTINCT a) ',
+        afterCursor: '',
+        containsKeywords: ['OVER'],
+        expectedResult: {
+          lowerCase: false,
+          suggestTables: { prependFrom: true },
+          suggestDatabases: { prependFrom: true, appendDot: true }
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT count(DISTINCT a) | FROM testTable"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT count(DISTINCT a) ',
+        afterCursor: ' FROM testTable',
+        containsKeywords: ['OVER'],
+        expectedResult: {
+          lowerCase: false
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT count(DISTINCT a) |, b, c FROM testTable"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT count(DISTINCT a) ',
+        afterCursor: ', b, c FROM testTable',
+        containsKeywords: ['OVER'],
+        expectedResult: {
+          lowerCase: false
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT row_number() OVER (| FROM testTable"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT row_number() OVER ( ',
+        afterCursor: ' FROM testTable',
+        containsKeywords: ['PARTITION BY'],
+        expectedResult: {
+          lowerCase: false
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT row_number() OVER (PARTITION | FROM testTable"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT row_number() OVER (PARTITION ',
+        afterCursor: ' FROM testTable',
+        containsKeywords: ['BY'],
+        expectedResult: {
+          lowerCase: false
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT row_number() OVER (PARTITION BY a, b ORDER | FROM testTable"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT row_number() OVER (PARTITION BY a, b ORDER ',
+        afterCursor: ' FROM testTable',
+        containsKeywords: ['BY'],
+        expectedResult: {
+          lowerCase: false
+        }
+      });
+    });
+
+    it('should suggest columns for "SELECT row_number() OVER (ORDER BY | FROM testTable"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT row_number() OVER (ORDER BY ',
+        afterCursor: ' FROM testTable',
+        containsKeywords: ['CASE'],
+        expectedResult: {
+          lowerCase: false,
+          suggestAggregateFunctions: { tables: [] },
+          suggestFunctions: {},
+          suggestColumns: {
+            source: 'select',
+            tables: [{ identifierChain: [{ name: 'testTable' }] }]
+          }
+        }
+      });
+    });
+
+    it('should suggest columns for "SELECT row_number() OVER (ORDER BY |) FROM testTable"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT row_number() OVER (ORDER BY ',
+        afterCursor: ') FROM testTable',
+        containsKeywords: ['CASE'],
+        expectedResult: {
+          lowerCase: false,
+          suggestAggregateFunctions: { tables: [] },
+          suggestFunctions: {},
+          suggestColumns: {
+            source: 'select',
+            tables: [{ identifierChain: [{ name: 'testTable' }] }]
+          } // TODO: source: 'order by'
+        }
+      });
+    });
+
+    it('should suggest columns for "SELECT row_number() OVER (ORDER BY foo |) FROM testTable"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT row_number() OVER (ORDER BY a ',
+        afterCursor: ') FROM testTable',
+        containsKeywords: ['ASC', 'DESC'],
+        expectedResult: {
+          lowerCase: false
+        }
+      });
+    });
+
+    it('should suggest columns for "SELECT row_number() OVER (PARTITION BY | FROM testTable"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT row_number() OVER (PARTITION BY ',
+        afterCursor: ' FROM testTable',
+        containsKeywords: ['CASE'],
+        expectedResult: {
+          lowerCase: false,
+          suggestAggregateFunctions: { tables: [] },
+          suggestFunctions: {},
+          suggestColumns: {
+            source: 'select',
+            tables: [{ identifierChain: [{ name: 'testTable' }] }]
+          }
+        }
+      });
+    });
+
+    it('should suggest columns for "SELECT row_number() OVER (PARTITION BY a, | FROM testTable"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT row_number() OVER (PARTITION BY a, ',
+        afterCursor: ' FROM testTable',
+        containsKeywords: ['CASE'],
+        expectedResult: {
+          lowerCase: false,
+          suggestAggregateFunctions: { tables: [] },
+          suggestFunctions: {},
+          suggestColumns: {
+            source: 'select',
+            tables: [{ identifierChain: [{ name: 'testTable' }] }]
+          }
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT row_number() OVER (PARTITION BY a ORDER BY b |"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT row_number() OVER (PARTITION BY a ORDER BY b ',
+        afterCursor: '',
+        containsKeywords: ['ASC', 'ROWS BETWEEN', 'RANGE BETWEEN'],
+        expectedResult: {
+          lowerCase: false
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN |"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN ',
+        afterCursor: '',
+        expectedResult: {
+          lowerCase: false,
+          suggestKeywords: ['CURRENT ROW', 'UNBOUNDED PRECEDING']
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN 1 |"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN 1 ',
+        afterCursor: '',
+        expectedResult: {
+          lowerCase: false,
+          suggestKeywords: ['PRECEDING']
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN UNBOUNDED |"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN UNBOUNDED ',
+        afterCursor: '',
+        expectedResult: {
+          lowerCase: false,
+          suggestKeywords: ['PRECEDING']
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN CURRENT |"', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN CURRENT ',
+        afterCursor: '',
+        expectedResult: {
+          lowerCase: false,
+          suggestKeywords: ['ROW']
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN 1 PRECEDING |"', () => {
+      assertAutoComplete({
+        beforeCursor:
+          'SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN 1 PRECEDING ',
+        afterCursor: '',
+        expectedResult: {
+          lowerCase: false,
+          suggestKeywords: ['AND']
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN UNBOUNDED PRECEDING |"', () => {
+      assertAutoComplete({
+        beforeCursor:
+          'SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN UNBOUNDED PRECEDING ',
+        afterCursor: '',
+        expectedResult: {
+          lowerCase: false,
+          suggestKeywords: ['AND']
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN CURRENT ROW |"', () => {
+      assertAutoComplete({
+        beforeCursor:
+          'SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN CURRENT ROW ',
+        afterCursor: '',
+        expectedResult: {
+          lowerCase: false,
+          suggestKeywords: ['AND']
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN 1 PRECEDING AND |"', () => {
+      assertAutoComplete({
+        beforeCursor:
+          'SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN 1 PRECEDING AND ',
+        afterCursor: '',
+        expectedResult: {
+          lowerCase: false,
+          suggestKeywords: ['CURRENT ROW', 'UNBOUNDED FOLLOWING']
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN UNBOUNDED PRECEDING AND |"', () => {
+      assertAutoComplete({
+        beforeCursor:
+          'SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN UNBOUNDED PRECEDING AND ',
+        afterCursor: '',
+        expectedResult: {
+          lowerCase: false,
+          suggestKeywords: ['CURRENT ROW', 'UNBOUNDED FOLLOWING']
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN CURRENT ROW AND |"', () => {
+      assertAutoComplete({
+        beforeCursor:
+          'SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN CURRENT ROW AND ',
+        afterCursor: '',
+        expectedResult: {
+          lowerCase: false,
+          suggestKeywords: ['CURRENT ROW', 'UNBOUNDED FOLLOWING']
+        }
+      });
+    });
 
-  describe('Variable References', () => {
-    it('should suggest tables for "SELECT | FROM ${some_variable};"', () => {
+    it('should suggest keywords for "SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN 1 PRECEDING AND CURRENT |"', () => {
       assertAutoComplete({
-        beforeCursor: 'SELECT ',
-        afterCursor: ' FROM ${some_variable};',
-        containsKeywords: ['*', 'ALL', 'DISTINCT'],
+        beforeCursor:
+          'SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN 1 PRECEDING AND CURRENT ',
+        afterCursor: '',
         expectedResult: {
           lowerCase: false,
-          suggestAggregateFunctions: {
-            tables: [{ identifierChain: [{ name: '${some_variable}' }] }]
-          },
-          suggestAnalyticFunctions: true,
-          suggestFunctions: {},
-          suggestColumns: {
-            source: 'select',
-            tables: [{ identifierChain: [{ name: '${some_variable}' }] }]
-          }
+          suggestKeywords: ['ROW']
         }
       });
     });
 
-    it('should suggest keywords for "SELECT * FROM testTable WHERE ${some_variable} |"', () => {
+    it('should suggest keywords for "SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED |"', () => {
       assertAutoComplete({
-        beforeCursor: 'SELECT * FROM testTable WHERE ${some_variable} ',
+        beforeCursor:
+          'SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED ',
         afterCursor: '',
-        containsKeywords: ['<', 'BETWEEN'],
-        containsColRefKeywords: true,
         expectedResult: {
           lowerCase: false,
-          suggestGroupBys: {
-            prefix: 'GROUP BY',
-            tables: [{ identifierChain: [{ name: 'testTable' }] }]
-          },
-          suggestOrderBys: {
-            prefix: 'ORDER BY',
-            tables: [{ identifierChain: [{ name: 'testTable' }] }]
-          },
-          colRef: { identifierChain: [{ name: 'testTable' }, { name: '${some_variable}' }] }
+          suggestKeywords: ['FOLLOWING']
         }
       });
     });
 
-    it('should suggest columns for "SELECT * FROM testTable WHERE ${some_variable} + 1 = |"', () => {
+    it('should suggest keywords for "SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN CURRENT ROW AND 1 |"', () => {
       assertAutoComplete({
-        beforeCursor: 'SELECT * FROM testTable WHERE ${some_variable} + 1 = ',
+        beforeCursor:
+          'SELECT row_number() OVER (PARTITION BY a ORDER BY b ROWS BETWEEN CURRENT ROW AND 1 ',
         afterCursor: '',
-        containsKeywords: ['CASE', 'NULL'],
         expectedResult: {
           lowerCase: false,
-          suggestFunctions: { types: ['NUMBER'] },
-          suggestColumns: {
-            source: 'where',
-            types: ['NUMBER'],
-            tables: [{ identifierChain: [{ name: 'testTable' }] }]
-          }
+          suggestKeywords: ['FOLLOWING']
         }
       });
     });
@@ -1806,6 +2299,14 @@ describe('ksqlAutocompleteParser.js SELECT statements', () => {
               identifierChain: [{ name: 'db' }, { name: 'customUdf' }],
               function: 'customudf'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 21, last_column: 24 },
+              function: 'customudf',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'db' }, { name: 'customUdf' }],
+              expression: { types: ['COLREF'], columnReference: [{ name: 'col' }] }
+            },
             {
               type: 'column',
               location: { first_line: 1, last_line: 1, first_column: 21, last_column: 24 },
@@ -3213,6 +3714,7 @@ describe('ksqlAutocompleteParser.js SELECT statements', () => {
             'HAVING',
             'ORDER BY',
             'LIMIT',
+            'UNION',
             '<',
             '<=',
             '<=>',
@@ -3250,6 +3752,7 @@ describe('ksqlAutocompleteParser.js SELECT statements', () => {
             'HAVING',
             'ORDER BY',
             'LIMIT',
+            'UNION',
             '<',
             '<=',
             '<=>',
@@ -3993,7 +4496,7 @@ describe('ksqlAutocompleteParser.js SELECT statements', () => {
         afterCursor: '',
         expectedResult: {
           lowerCase: false,
-          suggestKeywords: ['ASC', 'DESC', 'LIMIT']
+          suggestKeywords: ['ASC', 'DESC', 'LIMIT', 'UNION']
         }
       });
     });
@@ -4072,7 +4575,7 @@ describe('ksqlAutocompleteParser.js SELECT statements', () => {
         afterCursor: '',
         expectedResult: {
           lowerCase: false,
-          suggestKeywords: ['ASC', 'DESC', 'LIMIT']
+          suggestKeywords: ['ASC', 'DESC', 'LIMIT', 'UNION']
         }
       });
     });
@@ -4088,13 +4591,13 @@ describe('ksqlAutocompleteParser.js SELECT statements', () => {
       });
     });
 
-    it('should not suggest keywords for "SELECT * FROM database_two.testTable ORDER BY foo LIMIT 10 |"', () => {
+    it('should suggest keywords for "SELECT * FROM database_two.testTable ORDER BY foo LIMIT 10 |"', () => {
       assertAutoComplete({
         beforeCursor: 'SELECT * FROM database_two.testTable ORDER BY foo LIMIT 10 ',
         afterCursor: '',
         expectedResult: {
           lowerCase: false,
-          suggestKeywords: undefined
+          suggestKeywords: ['UNION']
         }
       });
     });
@@ -4250,16 +4753,293 @@ describe('ksqlAutocompleteParser.js SELECT statements', () => {
   });
 
   describe('LIMIT clause', () => {
-    it('should not suggest anything for "SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT |"', () => {
+    it('should suggest values for "SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT |"', () => {
       assertAutoComplete({
         beforeCursor: 'SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT ',
         afterCursor: '',
         noErrors: true,
+        expectedResult: {
+          lowerCase: false,
+          suggestKeywords: ['10', '100', '1000', '10000', '5000']
+        }
+      });
+    });
+  });
+
+  describe('UNION clause', () => {
+    // TODO: Fix locations
+    xit('should handle "SELECT * FROM (SELECT x FROM few_ints UNION ALL SELECT x FROM few_ints) AS t1 ORDER BY x;|', () => {
+      assertAutoComplete({
+        beforeCursor:
+          'SELECT * FROM (SELECT x FROM few_ints UNION ALL SELECT x FROM few_ints) AS t1 ORDER BY x;',
+        afterCursor: '',
+        noErrors: true,
+        containsKeywords: ['SELECT'],
+        expectedResult: {
+          lowerCase: false
+        }
+      });
+    });
+
+    it('should handle "SELECT key FROM (SELECT key FROM src ORDER BY key LIMIT 10)subq1 UNION SELECT key FROM (SELECT key FROM src1 ORDER BY key LIMIT 10)subq2;|', () => {
+      assertAutoComplete({
+        beforeCursor:
+          'SELECT key FROM (SELECT key FROM src ORDER BY key LIMIT 10)subq1 UNION SELECT key FROM (SELECT key FROM src1 ORDER BY key LIMIT 10)subq2;',
+        afterCursor: '',
+        noErrors: true,
+        containsKeywords: ['SELECT'],
+        expectedResult: {
+          lowerCase: false
+        }
+      });
+    });
+
+    it('should handle "SELECT * FROM t1 UNION DISTINCT SELECT * FROM t2;|', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT * FROM t1 UNION DISTINCT SELECT * FROM t2;',
+        afterCursor: '',
+        noErrors: true,
+        containsKeywords: ['SELECT'],
+        expectedResult: {
+          lowerCase: false
+        }
+      });
+    });
+
+    it('should handle "SELECT * FROM t1 UNION SELECT * FROM t2;|', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT * FROM t1 UNION SELECT * FROM t2;',
+        afterCursor: '',
+        noErrors: true,
+        containsKeywords: ['SELECT'],
+        expectedResult: {
+          lowerCase: false
+        }
+      });
+    });
+
+    it('should suggest keywords for "SELECT * FROM t1 UNION |', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT * FROM t1 UNION ',
+        afterCursor: '',
+        noErrors: true,
+        expectedResult: {
+          lowerCase: false,
+          suggestKeywords: ['ALL', 'DISTINCT', 'SELECT']
+        }
+      });
+    });
+
+    it('should suggest tables for "SELECT * FROM t1 UNION ALL SELECT |', () => {
+      assertAutoComplete({
+        beforeCursor: 'SELECT * FROM t1 UNION ALL SELECT ',
+        afterCursor: '',
+        noErrors: true,
+        containsKeywords: ['*', 'ALL', 'DISTINCT'],
+        expectedResult: {
+          lowerCase: false,
+          suggestAggregateFunctions: { tables: [] },
+          suggestAnalyticFunctions: true,
+          suggestFunctions: {},
+          suggestTables: {
+            prependQuestionMark: true,
+            prependFrom: true
+          },
+          suggestDatabases: {
+            prependQuestionMark: true,
+            prependFrom: true,
+            appendDot: true
+          }
+        }
+      });
+    });
+  });
+
+  describe('WITH clause', () => {
+    it('should handle "WITH q1 AS ( SELECT key FROM src WHERE something) SELECT * FROM q1;|', () => {
+      assertAutoComplete({
+        beforeCursor: 'WITH q1 AS ( SELECT key FROM src WHERE something) SELECT * FROM q1;',
+        afterCursor: '',
+        noErrors: true,
+        containsKeywords: ['SELECT'],
+        expectedResult: {
+          lowerCase: false
+        }
+      });
+    });
+
+    it('should handle "WITH q1 AS (SELECT * FROM src WHERE something), q2 AS (SELECT * FROM src s2 WHERE something) SELECT * FROM q1 UNION ALL SELECT * FROM q2;|', () => {
+      assertAutoComplete({
+        beforeCursor:
+          'WITH q1 AS (SELECT * FROM src WHERE something), q2 AS (SELECT * FROM src s2 WHERE something) SELECT * FROM q1 UNION ALL SELECT * FROM q2;',
+        afterCursor: '',
+        noErrors: true,
+        containsKeywords: ['SELECT'],
+        expectedResult: {
+          lowerCase: false
+        }
+      });
+    });
+
+    it('should handle "WITH t1 AS (SELECT 1) (WITH t2 AS (SELECT 2) SELECT * FROM t2) UNION ALL SELECT * FROM t1;|', () => {
+      assertAutoComplete({
+        beforeCursor:
+          'WITH t1 AS (SELECT 1) (WITH t2 AS (SELECT 2) SELECT * FROM t2) UNION ALL SELECT * FROM t1;',
+        afterCursor: '',
+        noErrors: true,
+        containsKeywords: ['SELECT'],
+        expectedResult: {
+          lowerCase: false
+        }
+      });
+    });
+
+    it('should suggest keywords for "WITH t1 |', () => {
+      assertAutoComplete({
+        beforeCursor: 'WITH t1 ',
+        afterCursor: '',
+        expectedResult: {
+          lowerCase: false,
+          suggestKeywords: ['AS']
+        }
+      });
+    });
+
+    it('should suggest keywords for "WITH t1 AS (|', () => {
+      assertAutoComplete({
+        beforeCursor: 'WITH t1 AS (',
+        afterCursor: '',
+        expectedResult: {
+          lowerCase: false,
+          suggestKeywords: ['SELECT']
+        }
+      });
+    });
+
+    it('should suggest keywords for "WITH t1 AS (SELECT * FROM boo) |', () => {
+      assertAutoComplete({
+        beforeCursor: 'WITH t1 AS (SELECT * FROM boo) ',
+        afterCursor: '',
+        containsKeywords: ['SELECT'],
         expectedResult: {
           lowerCase: false
         }
       });
     });
+
+    it('should suggest identifiers for "WITH t1 AS (SELECT * FROM FOO) SELECT |', () => {
+      assertAutoComplete({
+        beforeCursor: 'WITH t1 AS (SELECT * FROM FOO) SELECT ',
+        afterCursor: '',
+        noErrors: true,
+        containsKeywords: ['*', 'ALL', 'DISTINCT'],
+        expectedResult: {
+          lowerCase: false,
+          suggestAggregateFunctions: { tables: [] },
+          suggestAnalyticFunctions: true,
+          suggestFunctions: {},
+          suggestTables: { prependQuestionMark: true, prependFrom: true },
+          suggestDatabases: { prependQuestionMark: true, prependFrom: true, appendDot: true },
+          suggestCommonTableExpressions: [
+            { name: 't1', prependFrom: true, prependQuestionMark: true }
+          ],
+          commonTableExpressions: [
+            { alias: 't1', columns: [{ tables: [{ identifierChain: [{ name: 'FOO' }] }] }] }
+          ]
+        }
+      });
+    });
+
+    it('should suggest identifiers for "WITH t1 AS (SELECT * FROM FOO), t2 AS (SELECT |', () => {
+      assertAutoComplete({
+        beforeCursor: 'WITH t1 AS (SELECT * FROM FOO), t2 AS (SELECT ',
+        afterCursor: '',
+        containsKeywords: ['*', 'ALL', 'DISTINCT'],
+        expectedResult: {
+          suggestAggregateFunctions: { tables: [] },
+          suggestAnalyticFunctions: true,
+          suggestFunctions: {},
+          suggestTables: { prependQuestionMark: true, prependFrom: true },
+          suggestDatabases: { prependQuestionMark: true, prependFrom: true, appendDot: true },
+          lowerCase: false,
+          suggestCommonTableExpressions: [
+            { name: 't1', prependFrom: true, prependQuestionMark: true }
+          ],
+          commonTableExpressions: [
+            { alias: 't1', columns: [{ tables: [{ identifierChain: [{ name: 'FOO' }] }] }] }
+          ]
+        }
+      });
+    });
+
+    it('should suggest identifiers for "WITH t1 AS (SELECT * FROM FOO) SELECT * FROM |', () => {
+      assertAutoComplete({
+        beforeCursor: 'WITH t1 AS (SELECT * FROM FOO) SELECT * FROM ',
+        afterCursor: '',
+        noErrors: true,
+        expectedResult: {
+          lowerCase: false,
+          suggestTables: {},
+          suggestDatabases: { appendDot: true },
+          suggestCommonTableExpressions: [{ name: 't1' }],
+          commonTableExpressions: [
+            { alias: 't1', columns: [{ tables: [{ identifierChain: [{ name: 'FOO' }] }] }] }
+          ]
+        }
+      });
+    });
+
+    it('should suggest keywords for "with s as (select * from foo join bar) select * from |"', () => {
+      assertAutoComplete({
+        beforeCursor: 'with s as (select * from foo join bar) select * from ',
+        afterCursor: '',
+        expectedResult: {
+          lowerCase: true,
+          suggestTables: {},
+          suggestDatabases: { appendDot: true },
+          commonTableExpressions: [
+            {
+              columns: [
+                {
+                  tables: [
+                    { identifierChain: [{ name: 'foo' }] },
+                    { identifierChain: [{ name: 'bar' }] }
+                  ]
+                }
+              ],
+              alias: 's'
+            }
+          ],
+          suggestCommonTableExpressions: [{ name: 's' }]
+        }
+      });
+    });
+
+    it('should suggest keywords for "with s as (select * from foo join bar) select * from |;', () => {
+      assertAutoComplete({
+        beforeCursor: 'with s as (select * from foo join bar) select * from ',
+        afterCursor: ';',
+        expectedResult: {
+          lowerCase: true,
+          suggestTables: {},
+          suggestDatabases: { appendDot: true },
+          commonTableExpressions: [
+            {
+              columns: [
+                {
+                  tables: [
+                    { identifierChain: [{ name: 'foo' }] },
+                    { identifierChain: [{ name: 'bar' }] }
+                  ]
+                }
+              ],
+              alias: 's'
+            }
+          ],
+          suggestCommonTableExpressions: [{ name: 's' }]
+        }
+      });
+    });
   });
 
   describe('Joins', () => {

+ 186 - 30
desktop/core/src/desktop/js/parse/sql/phoenix/phoenixAutocompleteParser.js

@@ -596,7 +596,7 @@ case 283:
      parser.suggestDatabases({ prependFrom: true, appendDot: true });
    
 break;
-case 289: case 290: case 291: case 646: case 674: case 695: case 708: case 712: case 736: case 761: case 762: case 843: case 845: case 909: case 919: case 926: case 938: case 1027: case 1200: case 1238: case 1239:
+case 289: case 290: case 291: case 646: case 674: case 695: case 708: case 712: case 843: case 845: case 909: case 919: case 926: case 938: case 1027: case 1200: case 1238:
 this.$ = $$[$0];
 break;
 case 292: case 293:
@@ -1222,6 +1222,13 @@ case 734: case 735:
 
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 736: case 761:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 737:
@@ -1229,10 +1236,20 @@ case 737:
      // verifyType($$[$0], 'NUMBER');
      this.$ = $$[$0];
      $$[$0].types = ['NUMBER'];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
-case 738: case 739: case 740: case 741: case 748: case 749: case 750: case 751: case 752: case 753: case 759: case 760: case 781: case 839: case 840: case 898:
-this.$ = { types: [ 'BOOLEAN' ] };
+case 738: case 739: case 740:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 741:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 742:
 
@@ -1245,6 +1262,22 @@ case 744: case 745: case 746: case 747:
 
      parser.addColRefToVariableIfExists($$[$0-2], $$[$0]);
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 748: case 749: case 750: case 751: case 781: case 839: case 840:
+this.$ = { types: [ 'BOOLEAN' ] };
+break;
+case 752:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 753:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 754: case 755:
@@ -1252,6 +1285,7 @@ case 754: case 755:
      // verifyType($$[$0-2], 'BOOLEAN');
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 756: case 757: case 758:
@@ -1259,6 +1293,25 @@ case 756: case 757: case 758:
      // verifyType($$[$0-2], 'NUMBER');
      // verifyType($$[$0], 'NUMBER');
      this.$ = { types: [ 'NUMBER' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 759:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 760:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 762:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 764:
@@ -1672,13 +1725,14 @@ case 866:
 this.$ = { types: ['COLREF'], columnReference: $$[$0].chain };
 break;
 case 867:
-this.$ = { types: [ 'NULL' ] };
+this.$ = { types: [ 'NULL' ], text: $$[$0] };
 break;
 case 868:
 
      // We need to handle arbitrary UDFs here instead of inside UserDefinedFunction or there will be a conflict
      // with columnReference for functions like: db.udf(foo)
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1720,6 +1774,7 @@ break;
 case 873:
 
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1742,6 +1797,7 @@ break;
 case 874: case 1095: case 1096:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].activePosition) {
        parser.applyArgumentTypesToSuggestions($$[$0-1], $$[$0].activePosition);
      }
@@ -1765,23 +1821,39 @@ case 883:
    
 break;
 case 884:
-this.$ = { types: [ 'NUMBER' ] };
+this.$ = { types: [ 'NUMBER' ], text: $$[$0] };
 break;
-case 890: case 892:
+case 890: case 892: case 893:
 this.$ = $$[$0-1] + $$[$0];
 break;
-case 891:
+case 891: case 894:
 this.$ = $$[$0-2] + $$[$0-1] + $$[$0];
 break;
-case 896: case 897:
+case 895:
+this.$ = $$[$0-3] + $$[$0-2] + $$[$0-1] + $$[$0];
+break;
+case 896:
 
      if (/\$\{[^}]*\}/.test($$[$0])) {
        parser.addVariableLocation(_$[$0], $$[$0]);
-       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }] }
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: "'" + $$[$0] + "'" }
      } else {
        this.$ = { types: [ 'STRING' ] }
      }
    
+break;
+case 897:
+
+     if (/\$\{[^}]*\}/.test($$[$0])) {
+       parser.addVariableLocation(_$[$0], $$[$0]);
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: '"' + $$[$0] + '"' }
+     } else {
+       this.$ = { types: [ 'STRING' ], text: '"' + $$[$0] + '"' }
+     }
+   
+break;
+case 898:
+this.$ = { types: [ 'BOOLEAN' ], text: $$[$0] };
 break;
 case 899:
 
@@ -2253,6 +2325,7 @@ break;
 case 1093: case 1094:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].expressions && $$[$0].expressions.length) {
        this.$ = { function: $$[$0-1], expression: $$[$0].expressions[$$[$0].expressions.length - 1].expression, types: ['UDFREF'] }
      } else {
@@ -2265,7 +2338,7 @@ case 1103:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }]
      }
    
 break;
@@ -2312,7 +2385,7 @@ case 1112:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-2].activePosition + 1,
-       expressions: $$[$0-2].expressions.concat([{ expression: undefined, location: _$[$0] }])
+       expressions: $$[$0-2].expressions.concat([{ expression: { text: '' }, location: _$[$0] }])
      }
    
 break;
@@ -2321,7 +2394,7 @@ case 1113:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-4].activePosition + 1,
-       expressions: $$[$0-4].expressions.concat([{ expression: undefined, location: _$[$0-2] }]).concat($$[$0].expressions)
+       expressions: $$[$0-4].expressions.concat([{ expression: { text: '' }, location: _$[$0-2] }]).concat($$[$0].expressions)
      }
    
 break;
@@ -2340,7 +2413,7 @@ case 1115:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
@@ -2350,7 +2423,7 @@ case 1116:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2359,7 +2432,7 @@ case 1117:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2368,18 +2441,30 @@ case 1118:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-3] }, { expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-3] }, { expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
-case 1125: case 1149: case 1168:
-this.$ = { function: $$[$0-2], types: ['UDFREF'] };
+case 1125:
+
+    parser.addFunctionLocation(_$[$0-2], $$[$0-2]);
+    this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+  
 break;
 case 1126:
-this.$ = { function: $$[$0-3], expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression, types: ['UDFREF'] };
+
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
+     this.$ = {
+       function: $$[$0-3],
+       expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression,
+       types: ['UDFREF']
+     }
+   
 break;
 case 1127:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
      parser.valueExpressionSuggest();
      parser.applyArgumentTypesToSuggestions($$[$0-3], 1);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
@@ -2387,18 +2472,35 @@ case 1127:
 break;
 case 1128:
 
+     parser.addFunctionLocation(_$[$0-4], $$[$0-4]);
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-2].expressions);
      parser.suggestValueExpressionKeywords($$[$0-2].expressions[$$[$0-2].expressions.length - 1].expression);
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
 break;
 case 1129:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
      parser.applyArgumentTypesToSuggestions($$[$0-3], $$[$0-1].activePosition);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
    
 break;
-case 1136: case 1141:
-this.$ = { types: [ $$[$0-1].toUpperCase() ] };
+case 1136:
+
+     var expression = $$[$0-3];
+     parser.extractExpressionText(expression, $$[$0-3], $$[$0-2], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-5], [{
+       expression: expression,
+       location: {
+         first_line: _$[$0-3].first_line,
+         last_line: _$[$0-1].last_line,
+         first_column: _$[$0-3].first_column,
+         last_column: _$[$0-3].last_column
+       }
+     }]);
+     this.$ = { types: [ $$[$0-1].toUpperCase() ] }
+   
 break;
 case 1138:
 
@@ -2411,6 +2513,9 @@ case 1139: case 1140:
      parser.valueExpressionSuggest();
      this.$ = { types: [ 'T' ] };
    
+break;
+case 1141:
+this.$ = { types: [ $$[$0-1].toUpperCase() ] };
 break;
 case 1144:
 
@@ -2430,11 +2535,25 @@ case 1146: case 1147:
      this.$ = { types: [ 'T' ] };
    
 break;
-case 1148: case 1154:
-this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+case 1148:
+
+     parser.addFunctionArgumentLocations($$[$0-3], [{
+       expression: { text: $$[$0-1] },
+       location: _$[$0-1]
+     }]);
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] }
+   
 break;
-case 1150: case 1155: case 1167:
-this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+case 1149: case 1168:
+
+     this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+   
+break;
+case 1150:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] }
+   
 break;
 case 1151:
 
@@ -2467,6 +2586,17 @@ case 1153:
      }
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
+break;
+case 1154:
+
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+   
+break;
+case 1155: case 1167:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+   
 break;
 case 1156:
 
@@ -2567,7 +2697,10 @@ case 1207:
 this.$ = { inValueEdit: true, cursorAtStart: true };
 break;
 case 1208: case 1209: case 1210:
-this.$ = { suggestKeywords: ['NOT'] };
+
+     this.$ = { suggestKeywords: ['NOT'] }
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
 case 1214: case 1215: case 1216:
 
@@ -2576,14 +2709,21 @@ case 1214: case 1215: case 1216:
      this.$ = { types: ['BOOLEAN'] }
    
 break;
-case 1217: case 1219:
-this.$ = parser.findCaseType($$[$0-1]);
+case 1217:
+
+     this.$ = parser.findCaseType($$[$0-1])
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
-case 1218: case 1221:
+case 1218:
 
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
+break;
+case 1219:
+this.$ = parser.findCaseType($$[$0-1]);
 break;
 case 1220:
 
@@ -2591,6 +2731,12 @@ case 1220:
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
    
+break;
+case 1221:
+
+     $$[$0-3].caseTypes.push($$[$0-1]);
+     this.$ = parser.findCaseType($$[$0-3]);
+   
 break;
 case 1222:
 this.$ = parser.findCaseType($$[$0-2]);
@@ -2649,18 +2795,28 @@ case 1229:
    
 break;
 case 1232:
-this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] };
+
+     this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] }
+     parser.extractExpressionText(this.$, $$[$0]);
+   
 break;
 case 1233:
 
      $$[$0-1].caseTypes.push($$[$0]);
      this.$ = { caseTypes: $$[$0-1].caseTypes, lastType: $$[$0] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 1237:
 
      parser.suggestValueExpressionKeywords($$[$0-2], ['WHEN']);
    
+break;
+case 1239:
+
+     this.$ = $$[$0]
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 1240:
 this.$ = { caseTypes: [{ types: ['T'] }], suggestFilters: $$[$0].suggestFilters };

+ 186 - 30
desktop/core/src/desktop/js/parse/sql/phoenix/phoenixSyntaxParser.js

@@ -801,7 +801,7 @@ case 580:
      parser.suggestDatabases({ prependFrom: true, appendDot: true });
    
 break;
-case 581: case 619: case 640: case 653: case 657: case 681: case 706: case 707: case 788: case 790: case 854: case 864: case 871: case 883: case 986: case 1161: case 1199: case 1200:
+case 581: case 619: case 640: case 653: case 657: case 788: case 790: case 854: case 864: case 871: case 883: case 986: case 1161: case 1199:
 this.$ = $$[$0];
 break;
 case 584:
@@ -1172,6 +1172,13 @@ case 679: case 680:
 
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 681: case 706:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 682:
@@ -1179,10 +1186,20 @@ case 682:
      // verifyType($$[$0], 'NUMBER');
      this.$ = $$[$0];
      $$[$0].types = ['NUMBER'];
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
-case 683: case 684: case 685: case 686: case 693: case 694: case 695: case 696: case 697: case 698: case 704: case 705: case 726: case 784: case 785: case 843:
-this.$ = { types: [ 'BOOLEAN' ] };
+case 683: case 684: case 685:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 686:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 687:
 
@@ -1195,6 +1212,22 @@ case 689: case 690: case 691: case 692:
 
      parser.addColRefToVariableIfExists($$[$0-2], $$[$0]);
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 693: case 694: case 695: case 696: case 726: case 784: case 785:
+this.$ = { types: [ 'BOOLEAN' ] };
+break;
+case 697:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 698:
+
+     this.$ = { types: [ 'BOOLEAN' ] }
+     parser.extractExpressionText(this.$, $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 699: case 700:
@@ -1202,6 +1235,7 @@ case 699: case 700:
      // verifyType($$[$0-2], 'BOOLEAN');
      // verifyType($$[$0], 'BOOLEAN');
      this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 701: case 702: case 703:
@@ -1209,6 +1243,25 @@ case 701: case 702: case 703:
      // verifyType($$[$0-2], 'NUMBER');
      // verifyType($$[$0], 'NUMBER');
      this.$ = { types: [ 'NUMBER' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 704:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
+break;
+case 705:
+
+     this.$ = { types: [ 'BOOLEAN' ] };
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
+   
+break;
+case 707:
+
+     this.$ = $$[$0];
+     parser.extractExpressionText(this.$, $$[$0-2], $$[$0-1], $$[$0]);
    
 break;
 case 709:
@@ -1622,13 +1675,14 @@ case 811:
 this.$ = { types: ['COLREF'], columnReference: $$[$0].chain };
 break;
 case 812:
-this.$ = { types: [ 'NULL' ] };
+this.$ = { types: [ 'NULL' ], text: $$[$0] };
 break;
 case 813:
 
      // We need to handle arbitrary UDFs here instead of inside UserDefinedFunction or there will be a conflict
      // with columnReference for functions like: db.udf(foo)
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1670,6 +1724,7 @@ break;
 case 818:
 
      var fn = $$[$0-1].chain[$$[$0-1].chain.length - 1].name.toLowerCase();
+     parser.addFunctionArgumentLocations(fn, $$[$0].expressions, $$[$0-1].chain);
      $$[$0-1].lastLoc.type = 'function';
      $$[$0-1].lastLoc.function = fn;
      $$[$0-1].lastLoc.location = {
@@ -1692,6 +1747,7 @@ break;
 case 819: case 1056: case 1057:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].activePosition) {
        parser.applyArgumentTypesToSuggestions($$[$0-1], $$[$0].activePosition);
      }
@@ -1715,23 +1771,39 @@ case 828:
    
 break;
 case 829:
-this.$ = { types: [ 'NUMBER' ] };
+this.$ = { types: [ 'NUMBER' ], text: $$[$0] };
 break;
-case 835: case 837:
+case 835: case 837: case 838:
 this.$ = $$[$0-1] + $$[$0];
 break;
-case 836:
+case 836: case 839:
 this.$ = $$[$0-2] + $$[$0-1] + $$[$0];
 break;
-case 841: case 842:
+case 840:
+this.$ = $$[$0-3] + $$[$0-2] + $$[$0-1] + $$[$0];
+break;
+case 841:
 
      if (/\$\{[^}]*\}/.test($$[$0])) {
        parser.addVariableLocation(_$[$0], $$[$0]);
-       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }] }
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: "'" + $$[$0] + "'" }
      } else {
        this.$ = { types: [ 'STRING' ] }
      }
    
+break;
+case 842:
+
+     if (/\$\{[^}]*\}/.test($$[$0])) {
+       parser.addVariableLocation(_$[$0], $$[$0]);
+       this.$ = { types: [ 'STRING' ], columnReference: [{ name: $$[$0] }], text: '"' + $$[$0] + '"' }
+     } else {
+       this.$ = { types: [ 'STRING' ], text: '"' + $$[$0] + '"' }
+     }
+   
+break;
+case 843:
+this.$ = { types: [ 'BOOLEAN' ], text: $$[$0] };
 break;
 case 844:
 
@@ -2214,6 +2286,7 @@ break;
 case 1054: case 1055:
 
      parser.addFunctionLocation(_$[$0-1], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-1], $$[$0].expressions);
      if ($$[$0].expressions && $$[$0].expressions.length) {
        this.$ = { function: $$[$0-1], expression: $$[$0].expressions[$$[$0].expressions.length - 1].expression, types: ['UDFREF'] }
      } else {
@@ -2226,7 +2299,7 @@ case 1064:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }]
      }
    
 break;
@@ -2273,7 +2346,7 @@ case 1073:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-2].activePosition + 1,
-       expressions: $$[$0-2].expressions.concat([{ expression: undefined, location: _$[$0] }])
+       expressions: $$[$0-2].expressions.concat([{ expression: { text: '' }, location: _$[$0] }])
      }
    
 break;
@@ -2282,7 +2355,7 @@ case 1074:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: $$[$0-4].activePosition + 1,
-       expressions: $$[$0-4].expressions.concat([{ expression: undefined, location: _$[$0-2] }]).concat($$[$0].expressions)
+       expressions: $$[$0-4].expressions.concat([{ expression: { text: '' }, location: _$[$0-2] }]).concat($$[$0].expressions)
      }
    
 break;
@@ -2301,7 +2374,7 @@ case 1076:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
@@ -2311,7 +2384,7 @@ case 1077:
      this.$ = {
        cursorAtStart : true,
        activePosition: 1,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2320,7 +2393,7 @@ case 1078:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-1] }, { expression: undefined, location: _$[$0] }]
+       expressions: [{ expression: { text: '' }, location: _$[$0-1] }, { expression: { text: '' }, location: _$[$0] }]
      };
    
 break;
@@ -2329,18 +2402,30 @@ case 1079:
      parser.valueExpressionSuggest();
      this.$ = {
        activePosition: 2,
-       expressions: [{ expression: undefined, location: _$[$0-3] }, { expression: undefined, location: _$[$0-2] }].concat($$[$0].expressions)
+       expressions: [{ expression: { text: '' }, location: _$[$0-3] }, { expression: { text: '' }, location: _$[$0-2] }].concat($$[$0].expressions)
      };
    
 break;
-case 1086: case 1110: case 1129:
-this.$ = { function: $$[$0-2], types: ['UDFREF'] };
+case 1086:
+
+    parser.addFunctionLocation(_$[$0-2], $$[$0-2]);
+    this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+  
 break;
 case 1087:
-this.$ = { function: $$[$0-3], expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression, types: ['UDFREF'] };
+
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
+     this.$ = {
+       function: $$[$0-3],
+       expression: $$[$0-1].expressions[$$[$0-1].expressions.length - 1].expression,
+       types: ['UDFREF']
+     }
+   
 break;
 case 1088:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
      parser.valueExpressionSuggest();
      parser.applyArgumentTypesToSuggestions($$[$0-3], 1);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
@@ -2348,18 +2433,35 @@ case 1088:
 break;
 case 1089:
 
+     parser.addFunctionLocation(_$[$0-4], $$[$0-4]);
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-2].expressions);
      parser.suggestValueExpressionKeywords($$[$0-2].expressions[$$[$0-2].expressions.length - 1].expression);
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
 break;
 case 1090:
 
+     parser.addFunctionLocation(_$[$0-3], $$[$0-3]);
+     parser.addFunctionArgumentLocations($$[$0-3], $$[$0-1].expressions);
      parser.applyArgumentTypesToSuggestions($$[$0-3], $$[$0-1].activePosition);
      this.$ = { function: $$[$0-3], types: ['UDFREF'] };
    
 break;
-case 1097: case 1102:
-this.$ = { types: [ $$[$0-1].toUpperCase() ] };
+case 1097:
+
+     var expression = $$[$0-3];
+     parser.extractExpressionText(expression, $$[$0-3], $$[$0-2], $$[$0-1]);
+     parser.addFunctionArgumentLocations($$[$0-5], [{
+       expression: expression,
+       location: {
+         first_line: _$[$0-3].first_line,
+         last_line: _$[$0-1].last_line,
+         first_column: _$[$0-3].first_column,
+         last_column: _$[$0-3].last_column
+       }
+     }]);
+     this.$ = { types: [ $$[$0-1].toUpperCase() ] }
+   
 break;
 case 1099:
 
@@ -2372,6 +2474,9 @@ case 1100: case 1101:
      parser.valueExpressionSuggest();
      this.$ = { types: [ 'T' ] };
    
+break;
+case 1102:
+this.$ = { types: [ $$[$0-1].toUpperCase() ] };
 break;
 case 1105:
 
@@ -2391,11 +2496,25 @@ case 1107: case 1108:
      this.$ = { types: [ 'T' ] };
    
 break;
-case 1109: case 1115:
-this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+case 1109:
+
+     parser.addFunctionArgumentLocations($$[$0-3], [{
+       expression: { text: $$[$0-1] },
+       location: _$[$0-1]
+     }]);
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] }
+   
 break;
-case 1111: case 1116: case 1128:
-this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+case 1110: case 1129:
+
+     this.$ = { function: $$[$0-2], types: ['UDFREF'] }
+   
+break;
+case 1111:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] }
+   
 break;
 case 1112:
 
@@ -2428,6 +2547,17 @@ case 1114:
      }
      this.$ = { function: $$[$0-4], types: ['UDFREF'] };
    
+break;
+case 1115:
+
+     this.$ = { function: $$[$0-3], types: ['UDFREF'] };
+   
+break;
+case 1116: case 1128:
+
+     parser.addFunctionArgumentLocations($$[$0-4], $$[$0-1].expressions);
+     this.$ = { function: $$[$0-4], types: ['UDFREF'] };
+   
 break;
 case 1117:
 
@@ -2528,7 +2658,10 @@ case 1168:
 this.$ = { inValueEdit: true, cursorAtStart: true };
 break;
 case 1169: case 1170: case 1171:
-this.$ = { suggestKeywords: ['NOT'] };
+
+     this.$ = { suggestKeywords: ['NOT'] }
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
 case 1175: case 1176: case 1177:
 
@@ -2537,14 +2670,21 @@ case 1175: case 1176: case 1177:
      this.$ = { types: ['BOOLEAN'] }
    
 break;
-case 1178: case 1180:
-this.$ = parser.findCaseType($$[$0-1]);
+case 1178:
+
+     this.$ = parser.findCaseType($$[$0-1])
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
+   
 break;
-case 1179: case 1182:
+case 1179:
 
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
    
+break;
+case 1180:
+this.$ = parser.findCaseType($$[$0-1]);
 break;
 case 1181:
 
@@ -2552,6 +2692,12 @@ case 1181:
      $$[$0-3].caseTypes.push($$[$0-1]);
      this.$ = parser.findCaseType($$[$0-3]);
    
+break;
+case 1182:
+
+     $$[$0-3].caseTypes.push($$[$0-1]);
+     this.$ = parser.findCaseType($$[$0-3]);
+   
 break;
 case 1183:
 this.$ = parser.findCaseType($$[$0-2]);
@@ -2610,18 +2756,28 @@ case 1190:
    
 break;
 case 1193:
-this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] };
+
+     this.$ = { caseTypes: [ $$[$0] ], lastType: $$[$0] }
+     parser.extractExpressionText(this.$, $$[$0]);
+   
 break;
 case 1194:
 
      $$[$0-1].caseTypes.push($$[$0]);
      this.$ = { caseTypes: $$[$0-1].caseTypes, lastType: $$[$0] };
+     parser.extractExpressionText(this.$, $$[$0-1], $$[$0]);
    
 break;
 case 1198:
 
      parser.suggestValueExpressionKeywords($$[$0-2], ['WHEN']);
    
+break;
+case 1200:
+
+     this.$ = $$[$0]
+     parser.extractExpressionText(this.$, $$[$0-3], $$[$0-2], $$[$0-1], $$[$0]);
+   
 break;
 case 1201:
 this.$ = { caseTypes: [{ types: ['T'] }], suggestFilters: $$[$0].suggestFilters };

+ 24 - 0
desktop/core/src/desktop/js/parse/sql/phoenix/test/phoenixAutocompleteParser.Locations.test.js

@@ -64,6 +64,14 @@ describe('phoenixAutocompleteParser.js locations', () => {
           location: { first_line: 1, last_line: 1, first_column: 8, last_column: 10 },
           function: 'cos'
         },
+        {
+          type: 'functionArgument',
+          location: { first_line: 1, last_line: 1, first_column: 12, last_column: 13 },
+          function: 'cos',
+          argumentPosition: 0,
+          identifierChain: [{ name: 'cos' }],
+          expression: { types: ['NUMBER'], text: '1' }
+        },
         {
           type: 'alias',
           source: 'column',
@@ -1038,6 +1046,14 @@ describe('phoenixAutocompleteParser.js locations', () => {
             location: { first_line: 1, last_line: 1, first_column: 13, last_column: 15 },
             function: 'cos'
           },
+          {
+            type: 'functionArgument',
+            location: { first_line: 1, last_line: 1, first_column: 17, last_column: 22 },
+            function: 'cos',
+            argumentPosition: 0,
+            identifierChain: [{ name: 'cos' }],
+            expression: { types: ['COLREF'], columnReference: [{ name: 'boo' }, { name: 'a' }] }
+          },
           {
             type: 'table',
             location: { first_line: 1, last_line: 1, first_column: 17, last_column: 20 },
@@ -1280,6 +1296,14 @@ describe('phoenixAutocompleteParser.js locations', () => {
           location: { first_line: 1, last_line: 1, first_column: 8, last_column: 12 },
           function: 'count'
         },
+        {
+          type: 'functionArgument',
+          location: { first_line: 1, last_line: 1, first_column: 14, last_column: 15 },
+          function: 'count',
+          argumentPosition: 0,
+          identifierChain: [{ name: 'count' }],
+          expression: { text: '*' }
+        },
         {
           type: 'table',
           location: { first_line: 1, last_line: 1, first_column: 22, last_column: 31 },

+ 122 - 2
desktop/core/src/desktop/js/parse/sql/phoenix/test/phoenixAutocompleteParser.Select.test.js

@@ -371,6 +371,97 @@ describe('phoenixAutocompleteParser.js SELECT statements', () => {
   });
 
   describe('Select List Completion', () => {
+    it('should handle "select testUdf(not id), cos(-1), sin(1+id) from autocomp_test;"', () => {
+      assertAutoComplete({
+        beforeCursor: 'select testUdf(not id), cos(-1), sin(1+id) from autocomp_test;',
+        afterCursor: '',
+        noErrors: true,
+        containsKeywords: ['SELECT'],
+        expectedResult: {
+          locations: [
+            {
+              type: 'statement',
+              location: { first_line: 1, last_line: 1, first_column: 1, last_column: 62 }
+            },
+            {
+              type: 'selectList',
+              missing: false,
+              location: { first_line: 1, last_line: 1, first_column: 8, last_column: 43 }
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 8, last_column: 14 },
+              function: 'testudf'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 16, last_column: 22 },
+              function: 'testudf',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'testUdf' }],
+              expression: { types: ['BOOLEAN'], text: 'not id' }
+            },
+            {
+              type: 'column',
+              location: { first_line: 1, last_line: 1, first_column: 20, last_column: 22 },
+              identifierChain: [{ name: 'id' }],
+              qualified: false,
+              tables: [{ identifierChain: [{ name: 'autocomp_test' }] }]
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 25, last_column: 27 },
+              function: 'cos'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 29, last_column: 31 },
+              function: 'cos',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'cos' }],
+              expression: { types: ['NUMBER'], text: '- 1' }
+            },
+            {
+              type: 'function',
+              location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
+              function: 'sin'
+            },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 38, last_column: 42 },
+              function: 'sin',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'sin' }],
+              expression: { types: ['NUMBER'], text: '1 + id' }
+            },
+            {
+              type: 'column',
+              location: { first_line: 1, last_line: 1, first_column: 40, last_column: 42 },
+              identifierChain: [{ name: 'id' }],
+              qualified: false,
+              tables: [{ identifierChain: [{ name: 'autocomp_test' }] }]
+            },
+            {
+              type: 'table',
+              location: { first_line: 1, last_line: 1, first_column: 49, last_column: 62 },
+              identifierChain: [{ name: 'autocomp_test' }]
+            },
+            {
+              type: 'whereClause',
+              missing: true,
+              location: { first_line: 1, last_line: 1, first_column: 62, last_column: 62 }
+            },
+            {
+              type: 'limitClause',
+              missing: true,
+              location: { first_line: 1, last_line: 1, first_column: 62, last_column: 62 }
+            }
+          ],
+          lowerCase: true
+        }
+      });
+    });
+
     it('should handle "select count(*), tst.count, avg (id), avg from autocomp_test tst;"', () => {
       assertAutoComplete({
         beforeCursor: 'select count(*), tst.count, avg (id), avg from autocomp_test tst;',
@@ -393,6 +484,14 @@ describe('phoenixAutocompleteParser.js SELECT statements', () => {
               location: { first_line: 1, last_line: 1, first_column: 8, last_column: 12 },
               function: 'count'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 14, last_column: 15 },
+              function: 'count',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'count' }],
+              expression: { text: '*' }
+            },
             {
               type: 'table',
               location: { first_line: 1, last_line: 1, first_column: 18, last_column: 21 },
@@ -410,6 +509,14 @@ describe('phoenixAutocompleteParser.js SELECT statements', () => {
               location: { first_line: 1, last_line: 1, first_column: 29, last_column: 32 },
               function: 'avg'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
+              function: 'avg',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'avg' }],
+              expression: { types: ['COLREF'], columnReference: [{ name: 'id' }] }
+            },
             {
               type: 'column',
               location: { first_line: 1, last_line: 1, first_column: 34, last_column: 36 },
@@ -2192,6 +2299,14 @@ describe('phoenixAutocompleteParser.js SELECT statements', () => {
               identifierChain: [{ name: 'db' }, { name: 'customUdf' }],
               function: 'customudf'
             },
+            {
+              type: 'functionArgument',
+              location: { first_line: 1, last_line: 1, first_column: 21, last_column: 24 },
+              function: 'customudf',
+              argumentPosition: 0,
+              identifierChain: [{ name: 'db' }, { name: 'customUdf' }],
+              expression: { types: ['COLREF'], columnReference: [{ name: 'col' }] }
+            },
             {
               type: 'column',
               location: { first_line: 1, last_line: 1, first_column: 21, last_column: 24 },
@@ -2293,6 +2408,10 @@ describe('phoenixAutocompleteParser.js SELECT statements', () => {
             suggestColumns: {
               source: 'select',
               tables: [{ identifierChain: [{ name: 'testTable' }] }]
+            },
+            udfArgument: {
+              name: aggregateFunction.name.toLowerCase(),
+              position: 1
             }
           };
           assertAutoComplete({
@@ -4634,13 +4753,14 @@ describe('phoenixAutocompleteParser.js SELECT statements', () => {
   });
 
   describe('LIMIT clause', () => {
-    it('should not suggest anything for "SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT |"', () => {
+    it('should suggest values for "SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT |"', () => {
       assertAutoComplete({
         beforeCursor: 'SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT ',
         afterCursor: '',
         noErrors: true,
         expectedResult: {
-          lowerCase: false
+          lowerCase: false,
+          suggestKeywords: ['10', '100', '1000', '10000', '5000']
         }
       });
     });

+ 89 - 20
desktop/core/src/desktop/js/parse/sql/sqlParseUtils.js

@@ -56,6 +56,21 @@ export const SIMPLE_TABLE_REF_SUGGESTIONS = [
   'suggestOrderBys'
 ];
 
+export const LOCATION_TYPES = {
+  ALIAS: 'alias',
+  ASTERISK: 'asterisk',
+  COLUMN: 'column',
+  DATABASE: 'database',
+  FILE: 'file',
+  FUNCTION: 'function',
+  FUNCTION_ARGUMENT: 'functionArgument',
+  STATEMENT: 'statement',
+  STATEMENT_TYPE: 'statementType',
+  TABLE: 'table',
+  UNKNOWN: 'unknown',
+  VARIABLE: 'variable'
+};
+
 export const initSharedAutocomplete = parser => {
   const adjustLocationForCursor = location => {
     // columns are 0-based and lines not, so add 1 to cols
@@ -81,7 +96,7 @@ export const initSharedAutocomplete = parser => {
 
   parser.addAsteriskLocation = (location, identifierChain) => {
     parser.yy.locations.push({
-      type: 'asterisk',
+      type: LOCATION_TYPES.ASTERISK,
       location: adjustLocationForCursor(location),
       identifierChain: identifierChain
     });
@@ -139,7 +154,7 @@ export const initSharedAutocomplete = parser => {
 
   parser.addColumnAliasLocation = (location, alias, parentLocation) => {
     const aliasLocation = {
-      type: 'alias',
+      type: LOCATION_TYPES.ALIAS,
       source: 'column',
       alias: alias,
       location: adjustLocationForCursor(location),
@@ -167,13 +182,13 @@ export const initSharedAutocomplete = parser => {
       identifierChain.length && /\${[^}]*}/.test(identifierChain[identifierChain.length - 1].name);
     if (isVariable) {
       parser.yy.locations.push({
-        type: 'variable',
+        type: LOCATION_TYPES.VARIABLE,
         location: adjustLocationForCursor(location),
         value: identifierChain[identifierChain.length - 1].name
       });
     } else {
       parser.yy.locations.push({
-        type: 'column',
+        type: LOCATION_TYPES.COLUMN,
         location: adjustLocationForCursor(location),
         identifierChain: identifierChain,
         qualified: identifierChain.length > 1
@@ -183,7 +198,7 @@ export const initSharedAutocomplete = parser => {
 
   parser.addCteAliasLocation = (location, alias) => {
     parser.yy.locations.push({
-      type: 'alias',
+      type: LOCATION_TYPES.ALIAS,
       source: 'cte',
       alias: alias,
       location: adjustLocationForCursor(location)
@@ -192,7 +207,7 @@ export const initSharedAutocomplete = parser => {
 
   parser.addDatabaseLocation = (location, identifierChain) => {
     parser.yy.locations.push({
-      type: 'database',
+      type: LOCATION_TYPES.DATABASE,
       location: adjustLocationForCursor(location),
       identifierChain: identifierChain
     });
@@ -200,7 +215,7 @@ export const initSharedAutocomplete = parser => {
 
   parser.addFileLocation = (location, path) => {
     parser.yy.locations.push({
-      type: 'file',
+      type: LOCATION_TYPES.FILE,
       location: adjustLocationForCursor(location),
       path: path
     });
@@ -215,15 +230,36 @@ export const initSharedAutocomplete = parser => {
       last_column: location.last_column - 1
     };
     parser.yy.locations.push({
-      type: 'function',
+      type: LOCATION_TYPES.FUNCTION,
       location: adjustLocationForCursor(adjustedLocation),
       function: functionName.toLowerCase()
     });
   };
 
+  parser.addFunctionArgumentLocations = (functionName, expressions, identifierChain) => {
+    if (!expressions || !expressions.length) {
+      return;
+    }
+    expressions.forEach((details, idx) => {
+      const location = {
+        type: LOCATION_TYPES.FUNCTION_ARGUMENT,
+        location: adjustLocationForCursor(details.location),
+        function: functionName.toLowerCase(),
+        argumentPosition: idx,
+        identifierChain: identifierChain || [{ name: functionName }],
+        expression: details.expression
+      };
+
+      if (details.suffix) {
+        location.suffix = details.suffix;
+      }
+      parser.yy.locations.push(location);
+    });
+  };
+
   parser.addNewDatabaseLocation = (location, identifierChain) => {
     parser.yy.definitions.push({
-      type: 'database',
+      type: LOCATION_TYPES.DATABASE,
       location: adjustLocationForCursor(location),
       identifierChain: identifierChain
     });
@@ -241,7 +277,7 @@ export const initSharedAutocomplete = parser => {
       });
     }
     parser.yy.definitions.push({
-      type: 'table',
+      type: LOCATION_TYPES.TABLE,
       location: adjustLocationForCursor(location),
       identifierChain: identifierChain,
       columns: columns
@@ -280,7 +316,7 @@ export const initSharedAutocomplete = parser => {
     }
 
     parser.yy.locations.push({
-      type: 'statement',
+      type: LOCATION_TYPES.STATEMENT,
       location: adjustedLocation
     });
   };
@@ -289,16 +325,22 @@ export const initSharedAutocomplete = parser => {
     // Don't add if already there except for SELECT
     if (identifier !== 'SELECT' && parser.yy.allLocations) {
       for (let i = parser.yy.allLocations.length - 1; i >= 0; i--) {
-        if (parser.yy.allLocations[i] && parser.yy.allLocations[i].type === 'statement') {
+        if (
+          parser.yy.allLocations[i] &&
+          parser.yy.allLocations[i].type === LOCATION_TYPES.STATEMENT
+        ) {
           break;
         }
-        if (parser.yy.allLocations[i] && parser.yy.allLocations[i].type === 'statementType') {
+        if (
+          parser.yy.allLocations[i] &&
+          parser.yy.allLocations[i].type === LOCATION_TYPES.STATEMENT_TYPE
+        ) {
           return;
         }
       }
     }
     const loc = {
-      type: 'statementType',
+      type: LOCATION_TYPES.STATEMENT_TYPE,
       location: adjustLocationForCursor(location),
       identifier: identifier
     };
@@ -363,7 +405,7 @@ export const initSharedAutocomplete = parser => {
 
   parser.addSubqueryAliasLocation = (location, alias) => {
     parser.yy.locations.push({
-      type: 'alias',
+      type: LOCATION_TYPES.ALIAS,
       source: 'subquery',
       alias: alias,
       location: adjustLocationForCursor(location)
@@ -372,7 +414,7 @@ export const initSharedAutocomplete = parser => {
 
   parser.addTableAliasLocation = (location, alias, identifierChain) => {
     parser.yy.locations.push({
-      type: 'alias',
+      type: LOCATION_TYPES.ALIAS,
       source: 'table',
       alias: alias,
       location: adjustLocationForCursor(location),
@@ -382,7 +424,7 @@ export const initSharedAutocomplete = parser => {
 
   parser.addTableLocation = (location, identifierChain) => {
     parser.yy.locations.push({
-      type: 'table',
+      type: LOCATION_TYPES.TABLE,
       location: adjustLocationForCursor(location),
       identifierChain: identifierChain
     });
@@ -391,7 +433,7 @@ export const initSharedAutocomplete = parser => {
   parser.addVariableLocation = (location, value) => {
     if (/\${[^}]*}/.test(value)) {
       parser.yy.locations.push({
-        type: 'variable',
+        type: LOCATION_TYPES.VARIABLE,
         location: adjustLocationForCursor(location),
         value: value
       });
@@ -404,13 +446,13 @@ export const initSharedAutocomplete = parser => {
     let loc;
     if (isVariable) {
       loc = {
-        type: 'variable',
+        type: LOCATION_TYPES.VARIABLE,
         location: adjustLocationForCursor(location),
         value: identifierChain[identifierChain.length - 1].name
       };
     } else {
       loc = {
-        type: 'unknown',
+        type: LOCATION_TYPES.UNKNOWN,
         location: adjustLocationForCursor(location),
         identifierChain: identifierChain,
         qualified: identifierChain.length > 1
@@ -446,6 +488,32 @@ export const initSharedAutocomplete = parser => {
     }
   };
 
+  parser.extractExpressionText = (result, ...expressions) => {
+    const parts = [];
+
+    const fail = expressions.some(expression => {
+      if (typeof expression === 'undefined') {
+        // Skip undefined (optionals)
+        return false;
+      }
+      if (typeof expression === 'string' || typeof expression === 'number') {
+        parts.push(expression);
+      } else if (typeof expression === 'object') {
+        if (expression.text) {
+          parts.push(expression.text);
+        } else if (expression.columnReference) {
+          parts.push(expression.columnReference.map(ref => ref.name).join('.'));
+        } else {
+          return true;
+        }
+      }
+    });
+
+    if (!fail) {
+      result.text = parts.join(' ');
+    }
+  };
+
   parser.getSubQuery = cols => {
     const columns = [];
     cols.selectList.forEach(col => {
@@ -556,6 +624,7 @@ const SYNTAX_PARSER_NOOP_FUNCTIONS = [
   'checkForKeywords',
   'checkForSelectListKeywords',
   'commitLocations',
+  'extractExpressionText',
   'firstDefined',
   'getSelectListKeywords',
   'getSubQuery',