소스 검색

HUE-4320 [editor] The new autocompleter should be aware of column reference types in value expressions

It will now suggest keywords, functions and columns based on any opposite column references in value expressions. For instance "SELECT bar = | FROM foo" will have suggestions with the matching type of "bar" taking implicit conversion into account.
Johan Ahlen 9 년 전
부모
커밋
fbbe393

+ 173 - 32
desktop/core/src/desktop/static/desktop/js/autocomplete/sql.jison

@@ -1373,7 +1373,10 @@ SelectConditions
      if (!$1 && !$2 && !$3 && !$4) {
      if (!$1 && !$2 && !$3 && !$4) {
        $$ = { suggestKeywords: [] };
        $$ = { suggestKeywords: [] };
      } else if ($1 && !$2 && !$3 && !$4) {
      } else if ($1 && !$2 && !$3 && !$4) {
-       $$ = { suggestKeywords: getValueExpressionKeywords($1, ['GROUP BY', 'LIMIT', 'ORDER BY']) }
+       $$ = getValueExpressionKeywords($1, ['GROUP BY', 'LIMIT', 'ORDER BY']);
+       if ($1.columnReference) {
+         $$.columnReference = $1.columnReference
+       }
      } else if ($2 && !$3 && !$4) {
      } else if ($2 && !$3 && !$4) {
        $$ = { suggestKeywords: ['ORDER BY', 'LIMIT'] };
        $$ = { suggestKeywords: ['ORDER BY', 'LIMIT'] };
      } else if ($3 && !$4) {
      } else if ($3 && !$4) {
@@ -1918,6 +1921,7 @@ ValueExpression_EDIT
    {
    {
      if ($4.inValueEdit) {
      if ($4.inValueEdit) {
        valueExpressionSuggest($1);
        valueExpressionSuggest($1);
+       applyTypeToSuggestions($1.types);
      }
      }
      if ($4.cursorAtStart) {
      if ($4.cursorAtStart) {
        suggestKeywords(['SELECT']);
        suggestKeywords(['SELECT']);
@@ -1928,6 +1932,7 @@ ValueExpression_EDIT
    {
    {
      if ($3.inValueEdit) {
      if ($3.inValueEdit) {
        valueExpressionSuggest($1);
        valueExpressionSuggest($1);
+       applyTypeToSuggestions($1.types);
      }
      }
      if ($3.cursorAtStart) {
      if ($3.cursorAtStart) {
        suggestKeywords(['SELECT']);
        suggestKeywords(['SELECT']);
@@ -1950,21 +1955,21 @@ ValueExpression_EDIT
  : ValueExpression_EDIT 'NOT' 'BETWEEN' ValueExpression 'BETWEEN_AND' ValueExpression
  : ValueExpression_EDIT 'NOT' 'BETWEEN' ValueExpression 'BETWEEN_AND' ValueExpression
    {
    {
      if ($4.types[0] === $6.types[0]) {
      if ($4.types[0] === $6.types[0]) {
-       applyTypeToSuggestions($4.types)
+       applyTypeToSuggestions($4.types);
      }
      }
      $$ = { types: [ 'BOOLEAN' ] };
      $$ = { types: [ 'BOOLEAN' ] };
    }
    }
  | ValueExpression 'NOT' 'BETWEEN' ValueExpression_EDIT 'BETWEEN_AND' ValueExpression
  | ValueExpression 'NOT' 'BETWEEN' ValueExpression_EDIT 'BETWEEN_AND' ValueExpression
    {
    {
      if ($1.types[0] === $6.types[0]) {
      if ($1.types[0] === $6.types[0]) {
-       applyTypeToSuggestions($1.types)
+       applyTypeToSuggestions($1.types);
      }
      }
      $$ = { types: [ 'BOOLEAN' ] };
      $$ = { types: [ 'BOOLEAN' ] };
    }
    }
  | ValueExpression 'NOT' 'BETWEEN' ValueExpression 'BETWEEN_AND' ValueExpression_EDIT
  | ValueExpression 'NOT' 'BETWEEN' ValueExpression 'BETWEEN_AND' ValueExpression_EDIT
    {
    {
      if ($1.types[0] === $4.types[0]) {
      if ($1.types[0] === $4.types[0]) {
-       applyTypeToSuggestions($1.types)
+       applyTypeToSuggestions($1.types);
      }
      }
      $$ = { types: [ 'BOOLEAN' ] };
      $$ = { types: [ 'BOOLEAN' ] };
    }
    }
@@ -2007,6 +2012,7 @@ ValueExpression_EDIT
  | ValueExpression 'BETWEEN' ValueExpression 'BETWEEN_AND' 'CURSOR'
  | ValueExpression 'BETWEEN' ValueExpression 'BETWEEN_AND' 'CURSOR'
    {
    {
      valueExpressionSuggest($1);
      valueExpressionSuggest($1);
+     applyTypeToSuggestions($1.types);
      $$ = { types: [ 'BOOLEAN' ] };
      $$ = { types: [ 'BOOLEAN' ] };
    }
    }
  | ValueExpression 'BETWEEN' ValueExpression 'CURSOR'
  | ValueExpression 'BETWEEN' ValueExpression 'CURSOR'
@@ -2017,6 +2023,7 @@ ValueExpression_EDIT
  | ValueExpression 'BETWEEN' 'CURSOR'
  | ValueExpression 'BETWEEN' 'CURSOR'
    {
    {
      valueExpressionSuggest($1);
      valueExpressionSuggest($1);
+     applyTypeToSuggestions($1.types);
      $$ = { types: [ 'BOOLEAN' ] };
      $$ = { types: [ 'BOOLEAN' ] };
    }
    }
  ;
  ;
@@ -2025,30 +2032,43 @@ ValueExpression_EDIT
  : ValueExpression '=' ValueExpression_EDIT
  : ValueExpression '=' ValueExpression_EDIT
    {
    {
      applyTypeToSuggestions($1.types);
      applyTypeToSuggestions($1.types);
+     addColRefIfExists($1);
      $$ = { types: [ 'BOOLEAN' ] }
      $$ = { types: [ 'BOOLEAN' ] }
    }
    }
  | ValueExpression 'COMPARISON_OPERATOR' ValueExpression_EDIT
  | ValueExpression 'COMPARISON_OPERATOR' ValueExpression_EDIT
    {
    {
      applyTypeToSuggestions($1.types);
      applyTypeToSuggestions($1.types);
+     addColRefIfExists($1);
      $$ = { types: [ 'BOOLEAN' ] }
      $$ = { types: [ 'BOOLEAN' ] }
    }
    }
  | ValueExpression '-' ValueExpression_EDIT
  | ValueExpression '-' ValueExpression_EDIT
    {
    {
      applyTypeToSuggestions(['NUMBER']);
      applyTypeToSuggestions(['NUMBER']);
+     addColRefIfExists($1);
      $$ = { types: [ 'NUMBER' ] };
      $$ = { types: [ 'NUMBER' ] };
    }
    }
  | ValueExpression '*' ValueExpression_EDIT
  | ValueExpression '*' ValueExpression_EDIT
    {
    {
      applyTypeToSuggestions(['NUMBER']);
      applyTypeToSuggestions(['NUMBER']);
+     addColRefIfExists($1);
      $$ = { types: [ 'NUMBER' ] };
      $$ = { types: [ 'NUMBER' ] };
    }
    }
  | ValueExpression 'ARITHMETIC_OPERATOR' ValueExpression_EDIT
  | ValueExpression 'ARITHMETIC_OPERATOR' ValueExpression_EDIT
    {
    {
      applyTypeToSuggestions(['NUMBER']);
      applyTypeToSuggestions(['NUMBER']);
+     addColRefIfExists($1);
      $$ = { types: [ 'NUMBER' ] };
      $$ = { types: [ 'NUMBER' ] };
    }
    }
- | ValueExpression 'OR' ValueExpression_EDIT                   -> { types: [ 'BOOLEAN' ] }
- | ValueExpression 'AND' ValueExpression_EDIT                  -> { types: [ 'BOOLEAN' ] }
+ | ValueExpression 'OR' ValueExpression_EDIT
+   {
+     addColRefIfExists($1);
+     $$ = { types: [ 'BOOLEAN' ] }
+   }
+ | ValueExpression 'AND' ValueExpression_EDIT
+   {
+     addColRefIfExists($1);
+     $$ = { types: [ 'BOOLEAN' ] }
+   }
  | ValueExpression '=' RightPart_EDIT
  | ValueExpression '=' RightPart_EDIT
    {
    {
      valueExpressionSuggest($1);
      valueExpressionSuggest($1);
@@ -2095,30 +2115,43 @@ ValueExpression_EDIT
  : ValueExpression_EDIT '=' ValueExpression
  : ValueExpression_EDIT '=' ValueExpression
    {
    {
      applyTypeToSuggestions($3.types);
      applyTypeToSuggestions($3.types);
+     addColRefIfExists($3);
      $$ = { types: [ 'BOOLEAN' ] }
      $$ = { types: [ 'BOOLEAN' ] }
    }
    }
  | ValueExpression_EDIT 'COMPARISON_OPERATOR' ValueExpression
  | ValueExpression_EDIT 'COMPARISON_OPERATOR' ValueExpression
    {
    {
      applyTypeToSuggestions($3.types);
      applyTypeToSuggestions($3.types);
+     addColRefIfExists($3);
      $$ = { types: [ 'BOOLEAN' ] }
      $$ = { types: [ 'BOOLEAN' ] }
    }
    }
  | ValueExpression_EDIT '-' ValueExpression
  | ValueExpression_EDIT '-' ValueExpression
    {
    {
      applyTypeToSuggestions(['NUMBER']);
      applyTypeToSuggestions(['NUMBER']);
+     addColRefIfExists($3);
      $$ = { types: [ 'NUMBER' ] }
      $$ = { types: [ 'NUMBER' ] }
    }
    }
  | ValueExpression_EDIT '*' ValueExpression
  | ValueExpression_EDIT '*' ValueExpression
    {
    {
      applyTypeToSuggestions(['NUMBER']);
      applyTypeToSuggestions(['NUMBER']);
+     addColRefIfExists($3);
      $$ = { types: [ 'NUMBER' ] }
      $$ = { types: [ 'NUMBER' ] }
    }
    }
  | ValueExpression_EDIT 'ARITHMETIC_OPERATOR' ValueExpression
  | ValueExpression_EDIT 'ARITHMETIC_OPERATOR' ValueExpression
    {
    {
      applyTypeToSuggestions(['NUMBER']);
      applyTypeToSuggestions(['NUMBER']);
+     addColRefIfExists($3);
      $$ = { types: [ 'NUMBER' ] }
      $$ = { types: [ 'NUMBER' ] }
    }
    }
- | ValueExpression_EDIT 'OR' ValueExpression   -> { types: [ 'BOOLEAN' ] }
- | ValueExpression_EDIT 'AND' ValueExpression  -> { types: [ 'BOOLEAN' ] }
+ | ValueExpression_EDIT 'OR' ValueExpression
+   {
+     addColRefIfExists($3);
+     $$ = { types: [ 'BOOLEAN' ] }
+   }
+ | ValueExpression_EDIT 'AND' ValueExpression
+   {
+     addColRefIfExists($3);
+     $$ = { types: [ 'BOOLEAN' ] }
+   }
  | 'CURSOR' '=' ValueExpression
  | 'CURSOR' '=' ValueExpression
    {
    {
      valueExpressionSuggest($3);
      valueExpressionSuggest($3);
@@ -2239,7 +2272,7 @@ RightPart_EDIT
 // TODO: Expand with more choices
 // TODO: Expand with more choices
 NonParenthesizedValueExpressionPrimary
 NonParenthesizedValueExpressionPrimary
  : UnsignedValueSpecification
  : UnsignedValueSpecification
- | ColumnReference             -> { types: ['T'], columnReference: $1 }
+ | ColumnReference             -> { types: ['COLREF'], columnReference: $1 }
  | UserDefinedFunction
  | UserDefinedFunction
 // | GroupingOperation
 // | GroupingOperation
  | 'NULL'                      -> { types: [ 'NULL' ] }
  | 'NULL'                      -> { types: [ 'NULL' ] }
@@ -2249,9 +2282,9 @@ NonParenthesizedValueExpressionPrimary_EDIT
  : ColumnReference_EDIT
  : ColumnReference_EDIT
    {
    {
      if ($1.suggestKeywords) {
      if ($1.suggestKeywords) {
-       $$ = { types: ['T'], columnReference: $1, suggestKeywords: $1.suggestKeywords };
+       $$ = { types: ['COLREF'], columnReference: $1, suggestKeywords: $1.suggestKeywords };
      } else {
      } else {
-       $$ = { types: ['T'], columnReference: $1 };
+       $$ = { types: ['COLREF'], columnReference: $1 };
      }
      }
    }
    }
  | UserDefinedFunction_EDIT
  | UserDefinedFunction_EDIT
@@ -2349,7 +2382,11 @@ SelectSubList
  : ValueExpression OptionalCorrelationName
  : ValueExpression OptionalCorrelationName
    {
    {
      if ($2 && $2.suggestKeywords) {
      if ($2 && $2.suggestKeywords) {
-       $$ = { suggestKeywords: getValueExpressionKeywords($1, $2.suggestKeywords || []) }
+       var result = getValueExpressionKeywords($1, $2.suggestKeywords || [])
+       if ($1.columnReference) {
+         result.columnReference = $1.columnReference;
+       }
+       $$ = result;
      } else {
      } else {
        $$ = $2;
        $$ = $2;
      }
      }
@@ -3042,6 +3079,7 @@ OtherAggregateFunction_EDIT
          suggestKeywords(['DISTINCT']);
          suggestKeywords(['DISTINCT']);
        }
        }
      }
      }
+     applyArgumentTypesToSuggestions($1, 1);
      $$ = { types: findReturnTypes($1) };
      $$ = { types: findReturnTypes($1) };
    }
    }
  | OtherAggregateFunction_Type OptionalAllOrDistinct ValueExpressionList 'CURSOR' RightParenthesisOrError
  | OtherAggregateFunction_Type OptionalAllOrDistinct ValueExpressionList 'CURSOR' RightParenthesisOrError
@@ -3060,6 +3098,9 @@ OtherAggregateFunction_EDIT
          suggestKeywords(['DISTINCT']);
          suggestKeywords(['DISTINCT']);
        }
        }
      }
      }
+     if (parser.yy.result.suggestFunctions && !parser.yy.result.suggestFunctions.types) {
+       applyArgumentTypesToSuggestions($1, $3.position);
+     }
      $$ = { types: findReturnTypes($1) };
      $$ = { types: findReturnTypes($1) };
    }
    }
  ;
  ;
@@ -3098,41 +3139,74 @@ ExtractFunction_EDIT
  : '<impala>EXTRACT(' AnyCursor FromOrComma ValueExpression RightParenthesisOrError
  : '<impala>EXTRACT(' AnyCursor FromOrComma ValueExpression RightParenthesisOrError
    {
    {
      valueExpressionSuggest();
      valueExpressionSuggest();
+     applyTypeToSuggestions($3.toLowerCase() === 'from' ? ['STRING'] : ['TIMESTAMP']);
      $$ = { types: findReturnTypes($1) };
      $$ = { types: findReturnTypes($1) };
    }
    }
  | '<impala>EXTRACT(' AnyCursor FromOrComma RightParenthesisOrError
  | '<impala>EXTRACT(' AnyCursor FromOrComma RightParenthesisOrError
    {
    {
      valueExpressionSuggest();
      valueExpressionSuggest();
+     applyTypeToSuggestions($3.toLowerCase() === 'from' ? ['STRING'] : ['TIMESTAMP']);
      $$ = { types: findReturnTypes($1) };
      $$ = { types: findReturnTypes($1) };
    }
    }
  | '<impala>EXTRACT(' AnyCursor RightParenthesisOrError
  | '<impala>EXTRACT(' AnyCursor RightParenthesisOrError
    {
    {
      valueExpressionSuggest();
      valueExpressionSuggest();
+     applyTypeToSuggestions(['STRING', 'TIMESTAMP']);
+     $$ = { types: findReturnTypes($1) };
+   }
+ | '<impala>EXTRACT(' ValueExpression_EDIT FromOrComma ValueExpression RightParenthesisOrError
+   {
+     applyTypeToSuggestions($3.toLowerCase() === 'from' ? ['STRING'] : ['TIMESTAMP']);
+     $$ = { types: findReturnTypes($1) };
+   }
+ | '<impala>EXTRACT(' ValueExpression_EDIT FromOrComma RightParenthesisOrError
+   {
+     applyTypeToSuggestions($3.toLowerCase() === 'from' ? ['STRING'] : ['TIMESTAMP']);
+     $$ = { types: findReturnTypes($1) };
+   }
+ | '<impala>EXTRACT(' ValueExpression_EDIT RightParenthesisOrError
+   {
+     applyTypeToSuggestions(['STRING', 'TIMESTAMP']);
      $$ = { types: findReturnTypes($1) };
      $$ = { types: findReturnTypes($1) };
    }
    }
- | '<impala>EXTRACT(' ValueExpression_EDIT FromOrComma ValueExpression RightParenthesisOrError  -> { types: findReturnTypes($1) }
- | '<impala>EXTRACT(' ValueExpression_EDIT FromOrComma RightParenthesisOrError                  -> { types: findReturnTypes($1) }
- | '<impala>EXTRACT(' ValueExpression_EDIT RightParenthesisOrError                              -> { types: findReturnTypes($1) }
  | '<impala>EXTRACT(' ValueExpression FromOrComma AnyCursor RightParenthesisOrError
  | '<impala>EXTRACT(' ValueExpression FromOrComma AnyCursor RightParenthesisOrError
    {
    {
      valueExpressionSuggest();
      valueExpressionSuggest();
+     applyTypeToSuggestions($3.toLowerCase() === 'from' ? ['TIMESTAMP'] : ['STRING']);
      $$ = { types: findReturnTypes($1) };
      $$ = { types: findReturnTypes($1) };
    }
    }
  | '<impala>EXTRACT(' FromOrComma AnyCursor RightParenthesisOrError
  | '<impala>EXTRACT(' FromOrComma AnyCursor RightParenthesisOrError
    {
    {
      valueExpressionSuggest();
      valueExpressionSuggest();
+     applyTypeToSuggestions($3.toLowerCase() === 'from' ? ['TIMESTAMP'] : ['STRING']);
+     $$ = { types: findReturnTypes($1) };
+   }
+ | '<impala>EXTRACT(' ValueExpression FromOrComma ValueExpression_EDIT RightParenthesisOrError
+   {
+     applyTypeToSuggestions($3.toLowerCase() === 'from' ? ['TIMESTAMP'] : ['STRING']);
+     $$ = { types: findReturnTypes($1) };
+   }
+ | '<impala>EXTRACT(' FromOrComma ValueExpression_EDIT RightParenthesisOrError
+   {
+    applyTypeToSuggestions($3.toLowerCase() === 'from' ? ['TIMESTAMP'] : ['STRING']);
      $$ = { types: findReturnTypes($1) };
      $$ = { types: findReturnTypes($1) };
    }
    }
- | '<impala>EXTRACT(' ValueExpression FromOrComma ValueExpression_EDIT RightParenthesisOrError  -> { types: findReturnTypes($1) }
- | '<impala>EXTRACT(' FromOrComma ValueExpression_EDIT RightParenthesisOrError                  -> { types: findReturnTypes($1) }
  | '<impala>EXTRACT(' ValueExpression 'CURSOR' ValueExpression RightParenthesisOrError
  | '<impala>EXTRACT(' ValueExpression 'CURSOR' ValueExpression RightParenthesisOrError
    {
    {
-     suggestValueExpressionKeywords($2, [',', 'FROM']);
+     if ($1.types[0] === 'STRING') {
+       suggestValueExpressionKeywords($2, ['FROM']);
+     } else {
+       suggestValueExpressionKeywords($2);
+     }
      $$ = { types: findReturnTypes($1) };
      $$ = { types: findReturnTypes($1) };
    }
    }
  | '<impala>EXTRACT(' ValueExpression 'CURSOR' RightParenthesisOrError
  | '<impala>EXTRACT(' ValueExpression 'CURSOR' RightParenthesisOrError
    {
    {
-     suggestValueExpressionKeywords($2, [',', 'FROM']);
+     if ($1.types[0] === 'STRING') {
+       suggestValueExpressionKeywords($2, ['FROM']);
+     } else {
+       suggestValueExpressionKeywords($2);
+     }
      $$ = { types: findReturnTypes($1) };
      $$ = { types: findReturnTypes($1) };
    }
    }
  ;
  ;
@@ -3151,6 +3225,7 @@ SumFunction_EDIT
  : 'SUM(' OptionalAllOrDistinct AnyCursor RightParenthesisOrError
  : 'SUM(' OptionalAllOrDistinct AnyCursor RightParenthesisOrError
    {
    {
      valueExpressionSuggest();
      valueExpressionSuggest();
+     applyArgumentTypesToSuggestions($1, 1);
      if (!$2) {
      if (!$2) {
        if (isImpala()) {
        if (isImpala()) {
          suggestKeywords(['ALL', 'DISTINCT']);
          suggestKeywords(['ALL', 'DISTINCT']);
@@ -3165,7 +3240,13 @@ SumFunction_EDIT
      suggestValueExpressionKeywords($3);
      suggestValueExpressionKeywords($3);
      $$ = { types: findReturnTypes($1) };
      $$ = { types: findReturnTypes($1) };
    }
    }
- | 'SUM(' OptionalAllOrDistinct ValueExpression_EDIT RightParenthesisOrError  -> { types: findReturnTypes($1) }
+ | 'SUM(' OptionalAllOrDistinct ValueExpression_EDIT RightParenthesisOrError
+   {
+     if (parser.yy.result.suggestFunctions && ! parser.yy.result.suggestFunctions.types) {
+       applyArgumentTypesToSuggestions($1, 1);
+     }
+     $$ = { types: findReturnTypes($1) };
+   }
  ;
  ;
 
 
 // TODO: WITHIN
 // TODO: WITHIN
@@ -3856,7 +3937,16 @@ var mergeSuggestKeywords = function() {
 }
 }
 
 
 var suggestValueExpressionKeywords = function (valueExpression, extras) {
 var suggestValueExpressionKeywords = function (valueExpression, extras) {
-  suggestKeywords(getValueExpressionKeywords(valueExpression, extras));
+  var expressionKeywords = getValueExpressionKeywords(valueExpression, extras)
+  suggestKeywords(expressionKeywords.suggestKeywords);
+  if (expressionKeywords.suggestColRefKeywords) {
+    suggestColRefKeywords(expressionKeywords.suggestColRefKeywords);
+  }
+  if (valueExpression.lastType) {
+    addColRefIfExists(valueExpression.lastType);
+  } else {
+    addColRefIfExists(valueExpression);
+  }
 }
 }
 
 
 var getValueExpressionKeywords = function (valueExpression, extras) {
 var getValueExpressionKeywords = function (valueExpression, extras) {
@@ -3866,9 +3956,22 @@ var getValueExpressionKeywords = function (valueExpression, extras) {
   if (isHive()) {
   if (isHive()) {
     keywords.push('<=>');
     keywords.push('<=>');
   }
   }
+  if (extras) {
+    keywords = keywords.concat(extras);
+  }
   if (valueExpression.suggestKeywords) {
   if (valueExpression.suggestKeywords) {
     keywords = keywords.concat(valueExpression.suggestKeywords);
     keywords = keywords.concat(valueExpression.suggestKeywords);
   }
   }
+  if (types.length === 1 &&  types[0] === 'COLREF') {
+    return {
+      suggestKeywords: keywords,
+      suggestColRefKeywords: {
+        BOOLEAN: ['AND', 'OR'],
+        NUMBER: ['+', '-', '*', '/', '%'],
+        STRING: ['LIKE', 'NOT LIKE', 'REGEX', 'RLIKE']
+      }
+    }
+  }
   if (parser.yy.sqlFunctions.matchesType(parser.yy.activeDialect, ['BOOLEAN'], types)) {
   if (parser.yy.sqlFunctions.matchesType(parser.yy.activeDialect, ['BOOLEAN'], types)) {
     keywords = keywords.concat(['AND', 'OR']);
     keywords = keywords.concat(['AND', 'OR']);
   }
   }
@@ -3878,10 +3981,7 @@ var getValueExpressionKeywords = function (valueExpression, extras) {
   if (parser.yy.sqlFunctions.matchesType(parser.yy.activeDialect, ['STRING'], types)) {
   if (parser.yy.sqlFunctions.matchesType(parser.yy.activeDialect, ['STRING'], types)) {
     keywords = keywords.concat(['LIKE', 'NOT LIKE', 'REGEX', 'RLIKE']);
     keywords = keywords.concat(['LIKE', 'NOT LIKE', 'REGEX', 'RLIKE']);
   }
   }
-  if (extras) {
-    keywords = keywords.concat(extras);
-  }
-  return keywords;
+  return { suggestKeywords: keywords };
 }
 }
 
 
 var suggestTypeKeywords = function () {
 var suggestTypeKeywords = function () {
@@ -3894,9 +3994,16 @@ var suggestTypeKeywords = function () {
   }
   }
 }
 }
 
 
+var addColRefIfExists = function (valueExpression) {
+  if (valueExpression.columnReference) {
+    parser.yy.result.colRef = { identifierChain: valueExpression.columnReference };
+  }
+}
+
 var valueExpressionSuggest = function (oppositeValueExpression) {
 var valueExpressionSuggest = function (oppositeValueExpression) {
   if (oppositeValueExpression && oppositeValueExpression.columnReference) {
   if (oppositeValueExpression && oppositeValueExpression.columnReference) {
-    suggestValues({ identifierChain: oppositeValueExpression.columnReference });
+    suggestValues();
+    parser.yy.result.colRef = { identifierChain: oppositeValueExpression.columnReference };
   }
   }
   suggestColumns();
   suggestColumns();
   suggestFunctions();
   suggestFunctions();
@@ -3950,6 +4057,30 @@ var applyArgumentTypesToSuggestions = function (funcToken, position) {
 
 
 var prioritizeSuggestions = function () {
 var prioritizeSuggestions = function () {
   parser.yy.result.lowerCase = parser.yy.lowerCase || false;
   parser.yy.result.lowerCase = parser.yy.lowerCase || false;
+  if (typeof parser.yy.result.colRef !== 'undefined') {
+    if (typeof parser.yy.result.colRef.table === 'undefined') {
+      delete parser.yy.result.colRef;
+      if (typeof parser.yy.result.suggestColRefKeywords !== 'undefined') {
+        Object.keys(parser.yy.result.suggestColRefKeywords).forEach(function (type) {
+          parser.yy.result.suggestKeywords = parser.yy.result.suggestKeywords.concat(parser.yy.result.suggestColRefKeywords[type]);
+        });
+        delete parser.yy.result.suggestColRefKeywords;
+      }
+      if (parser.yy.result.suggestColumns && parser.yy.result.suggestColumns.types.length === 1 && parser.yy.result.suggestColumns.types[0] === 'COLREF') {
+        parser.yy.result.suggestColumns.types = ['T'];
+      }
+      delete parser.yy.result.suggestValues;
+    }
+  }
+
+  if (typeof parser.yy.result.colRef !== 'undefined') {
+    if (!parser.yy.result.suggestValues &&
+        !parser.yy.result.suggestColRefKeywords &&
+        (!parser.yy.result.suggestColumns ||
+          parser.yy.result.suggestColumns.types[0] !== 'COLREF')) {
+      delete parser.yy.result.colRef;
+    }
+  }
   if (typeof parser.yy.result.suggestIdentifiers !== 'undefined' &&  parser.yy.result.suggestIdentifiers.length > 0) {
   if (typeof parser.yy.result.suggestIdentifiers !== 'undefined' &&  parser.yy.result.suggestIdentifiers.length > 0) {
     if (!parser.yy.keepColumns) {
     if (!parser.yy.keepColumns) {
      delete parser.yy.result.suggestColumns;
      delete parser.yy.result.suggestColumns;
@@ -4182,8 +4313,8 @@ var linkTablePrimaries = function () {
    if (typeof parser.yy.result.suggestColumns !== 'undefined') {
    if (typeof parser.yy.result.suggestColumns !== 'undefined') {
      linkSuggestion(parser.yy.result.suggestColumns, true);
      linkSuggestion(parser.yy.result.suggestColumns, true);
    }
    }
-   if (typeof parser.yy.result.suggestValues !== 'undefined') {
-     linkSuggestion(parser.yy.result.suggestValues, false);
+   if (typeof parser.yy.result.colRef !== 'undefined') {
+     linkSuggestion(parser.yy.result.colRef, false);
    }
    }
 }
 }
 
 
@@ -4212,9 +4343,15 @@ var suggestDdlAndDmlKeywords = function () {
   suggestKeywords(keywords);
   suggestKeywords(keywords);
 }
 }
 
 
-var checkForKeywords = function (rule) {
-  if (rule && rule.suggestKeywords && rule.suggestKeywords.length > 0) {
-    suggestKeywords(rule.suggestKeywords);
+var checkForKeywords = function (expression) {
+  if (expression) {
+    if (expression.suggestKeywords && expression.suggestKeywords.length > 0) {
+      suggestKeywords(expression.suggestKeywords);
+    }
+    if (expression.suggestColRefKeywords) {
+      suggestColRefKeywords(expression.suggestColRefKeywords)
+      addColRefIfExists(expression);
+    }
   }
   }
 }
 }
 
 
@@ -4222,6 +4359,10 @@ var suggestKeywords = function (keywords) {
   parser.yy.result.suggestKeywords = keywords.sort();
   parser.yy.result.suggestKeywords = keywords.sort();
 }
 }
 
 
+var suggestColRefKeywords = function (colRefKeywords) {
+  parser.yy.result.suggestColRefKeywords = colRefKeywords;
+}
+
 var suggestTablesOrColumns = function (identifier) {
 var suggestTablesOrColumns = function (identifier) {
   if (typeof parser.yy.latestTablePrimaries == 'undefined') {
   if (typeof parser.yy.latestTablePrimaries == 'undefined') {
     suggestTables({ database: identifier });
     suggestTables({ database: identifier });
@@ -4267,7 +4408,7 @@ var suggestHdfs = function (details) {
 }
 }
 
 
 var suggestValues = function (details) {
 var suggestValues = function (details) {
-  parser.yy.result.suggestValues = details || {};
+  parser.yy.result.suggestValues = true;
 }
 }
 
 
 var determineCase = function (text) {
 var determineCase = function (text) {

+ 179 - 34
desktop/core/src/desktop/static/desktop/js/autocomplete/sql.js

@@ -510,7 +510,10 @@ case 309:
      if (!$$[$0-3] && !$$[$0-2] && !$$[$0-1] && !$$[$0]) {
      if (!$$[$0-3] && !$$[$0-2] && !$$[$0-1] && !$$[$0]) {
        this.$ = { suggestKeywords: [] };
        this.$ = { suggestKeywords: [] };
      } else if ($$[$0-3] && !$$[$0-2] && !$$[$0-1] && !$$[$0]) {
      } else if ($$[$0-3] && !$$[$0-2] && !$$[$0-1] && !$$[$0]) {
-       this.$ = { suggestKeywords: getValueExpressionKeywords($$[$0-3], ['GROUP BY', 'LIMIT', 'ORDER BY']) }
+       this.$ = getValueExpressionKeywords($$[$0-3], ['GROUP BY', 'LIMIT', 'ORDER BY']);
+       if ($$[$0-3].columnReference) {
+         this.$.columnReference = $$[$0-3].columnReference
+       }
      } else if ($$[$0-2] && !$$[$0-1] && !$$[$0]) {
      } else if ($$[$0-2] && !$$[$0-1] && !$$[$0]) {
        this.$ = { suggestKeywords: ['ORDER BY', 'LIMIT'] };
        this.$ = { suggestKeywords: ['ORDER BY', 'LIMIT'] };
      } else if ($$[$0-1] && !$$[$0]) {
      } else if ($$[$0-1] && !$$[$0]) {
@@ -605,7 +608,7 @@ case 369: case 370: case 371:
      this.$ = { types: [ 'BOOLEAN' ] };
      this.$ = { types: [ 'BOOLEAN' ] };
    
    
 break;
 break;
-case 373: case 374: case 375: case 381: case 382: case 383: case 384: case 385: case 386: case 397: case 399: case 405: case 406: case 407: case 408: case 409: case 410: case 411: case 419: case 420: case 421: case 422: case 440: case 441: case 454: case 455: case 547:
+case 373: case 374: case 375: case 381: case 382: case 383: case 384: case 385: case 386: case 397: case 399: case 405: case 406: case 407: case 408: case 409: case 410: case 411: case 419: case 420: case 421: case 422: case 547:
 this.$ = { types: [ 'BOOLEAN' ] };
 this.$ = { types: [ 'BOOLEAN' ] };
 break;
 break;
 case 376: case 377: case 378:
 case 376: case 377: case 378:
@@ -701,6 +704,7 @@ case 417:
 
 
      if ($$[$0].inValueEdit) {
      if ($$[$0].inValueEdit) {
        valueExpressionSuggest($$[$0-3]);
        valueExpressionSuggest($$[$0-3]);
+       applyTypeToSuggestions($$[$0-3].types);
      }
      }
      if ($$[$0].cursorAtStart) {
      if ($$[$0].cursorAtStart) {
        suggestKeywords(['SELECT']);
        suggestKeywords(['SELECT']);
@@ -712,6 +716,7 @@ case 418:
 
 
      if ($$[$0].inValueEdit) {
      if ($$[$0].inValueEdit) {
        valueExpressionSuggest($$[$0-2]);
        valueExpressionSuggest($$[$0-2]);
+       applyTypeToSuggestions($$[$0-2].types);
      }
      }
      if ($$[$0].cursorAtStart) {
      if ($$[$0].cursorAtStart) {
        suggestKeywords(['SELECT']);
        suggestKeywords(['SELECT']);
@@ -722,7 +727,7 @@ break;
 case 423:
 case 423:
 
 
      if ($$[$0-2].types[0] === $$[$0].types[0]) {
      if ($$[$0-2].types[0] === $$[$0].types[0]) {
-       applyTypeToSuggestions($$[$0-2].types)
+       applyTypeToSuggestions($$[$0-2].types);
      }
      }
      this.$ = { types: [ 'BOOLEAN' ] };
      this.$ = { types: [ 'BOOLEAN' ] };
    
    
@@ -730,7 +735,7 @@ break;
 case 424:
 case 424:
 
 
      if ($$[$0-5].types[0] === $$[$0].types[0]) {
      if ($$[$0-5].types[0] === $$[$0].types[0]) {
-       applyTypeToSuggestions($$[$0-5].types)
+       applyTypeToSuggestions($$[$0-5].types);
      }
      }
      this.$ = { types: [ 'BOOLEAN' ] };
      this.$ = { types: [ 'BOOLEAN' ] };
    
    
@@ -738,7 +743,7 @@ break;
 case 425:
 case 425:
 
 
      if ($$[$0-5].types[0] === $$[$0-2].types[0]) {
      if ($$[$0-5].types[0] === $$[$0-2].types[0]) {
-       applyTypeToSuggestions($$[$0-5].types)
+       applyTypeToSuggestions($$[$0-5].types);
      }
      }
      this.$ = { types: [ 'BOOLEAN' ] };
      this.$ = { types: [ 'BOOLEAN' ] };
    
    
@@ -772,32 +777,35 @@ break;
 case 432:
 case 432:
 
 
      valueExpressionSuggest($$[$0-4]);
      valueExpressionSuggest($$[$0-4]);
+     applyTypeToSuggestions($$[$0-4].types);
      this.$ = { types: [ 'BOOLEAN' ] };
      this.$ = { types: [ 'BOOLEAN' ] };
    
    
 break;
 break;
-case 434:
+case 434: case 442: case 443:
 
 
      valueExpressionSuggest($$[$0-2]);
      valueExpressionSuggest($$[$0-2]);
+     applyTypeToSuggestions($$[$0-2].types);
      this.$ = { types: [ 'BOOLEAN' ] };
      this.$ = { types: [ 'BOOLEAN' ] };
    
    
 break;
 break;
 case 435: case 436:
 case 435: case 436:
 
 
      applyTypeToSuggestions($$[$0-2].types);
      applyTypeToSuggestions($$[$0-2].types);
+     addColRefIfExists($$[$0-2]);
      this.$ = { types: [ 'BOOLEAN' ] }
      this.$ = { types: [ 'BOOLEAN' ] }
    
    
 break;
 break;
 case 437: case 438: case 439:
 case 437: case 438: case 439:
 
 
      applyTypeToSuggestions(['NUMBER']);
      applyTypeToSuggestions(['NUMBER']);
+     addColRefIfExists($$[$0-2]);
      this.$ = { types: [ 'NUMBER' ] };
      this.$ = { types: [ 'NUMBER' ] };
    
    
 break;
 break;
-case 442: case 443:
+case 440: case 441:
 
 
-     valueExpressionSuggest($$[$0-2]);
-     applyTypeToSuggestions($$[$0-2].types);
-     this.$ = { types: [ 'BOOLEAN' ] };
+     addColRefIfExists($$[$0-2]);
+     this.$ = { types: [ 'BOOLEAN' ] }
    
    
 break;
 break;
 case 444: case 445: case 446:
 case 444: case 445: case 446:
@@ -816,14 +824,22 @@ break;
 case 449: case 450:
 case 449: case 450:
 
 
      applyTypeToSuggestions($$[$0].types);
      applyTypeToSuggestions($$[$0].types);
+     addColRefIfExists($$[$0]);
      this.$ = { types: [ 'BOOLEAN' ] }
      this.$ = { types: [ 'BOOLEAN' ] }
    
    
 break;
 break;
 case 451: case 452: case 453:
 case 451: case 452: case 453:
 
 
      applyTypeToSuggestions(['NUMBER']);
      applyTypeToSuggestions(['NUMBER']);
+     addColRefIfExists($$[$0]);
      this.$ = { types: [ 'NUMBER' ] }
      this.$ = { types: [ 'NUMBER' ] }
    
    
+break;
+case 454: case 455:
+
+     addColRefIfExists($$[$0]);
+     this.$ = { types: [ 'BOOLEAN' ] }
+   
 break;
 break;
 case 456: case 457:
 case 456: case 457:
 
 
@@ -1039,7 +1055,7 @@ case 517: case 518:
    
    
 break;
 break;
 case 529:
 case 529:
-this.$ = { types: ['T'], columnReference: $$[$0] };
+this.$ = { types: ['COLREF'], columnReference: $$[$0] };
 break;
 break;
 case 531:
 case 531:
 this.$ = { types: [ 'NULL' ] };
 this.$ = { types: [ 'NULL' ] };
@@ -1047,9 +1063,9 @@ break;
 case 532:
 case 532:
 
 
      if ($$[$0].suggestKeywords) {
      if ($$[$0].suggestKeywords) {
-       this.$ = { types: ['T'], columnReference: $$[$0], suggestKeywords: $$[$0].suggestKeywords };
+       this.$ = { types: ['COLREF'], columnReference: $$[$0], suggestKeywords: $$[$0].suggestKeywords };
      } else {
      } else {
-       this.$ = { types: ['T'], columnReference: $$[$0] };
+       this.$ = { types: ['COLREF'], columnReference: $$[$0] };
      }
      }
    
    
 break;
 break;
@@ -1073,7 +1089,11 @@ break;
 case 565:
 case 565:
 
 
      if ($$[$0] && $$[$0].suggestKeywords) {
      if ($$[$0] && $$[$0].suggestKeywords) {
-       this.$ = { suggestKeywords: getValueExpressionKeywords($$[$0-1], $$[$0].suggestKeywords || []) }
+       var result = getValueExpressionKeywords($$[$0-1], $$[$0].suggestKeywords || [])
+       if ($$[$0-1].columnReference) {
+         result.columnReference = $$[$0-1].columnReference;
+       }
+       this.$ = result;
      } else {
      } else {
        this.$ = $$[$0];
        this.$ = $$[$0];
      }
      }
@@ -1313,10 +1333,10 @@ case 746: case 747:
      this.$ = { types: [ 'T' ] };
      this.$ = { types: [ 'T' ] };
    
    
 break;
 break;
-case 748: case 754: case 788:
+case 748: case 754:
 this.$ = { types: findReturnTypes($$[$0-2]) };
 this.$ = { types: findReturnTypes($$[$0-2]) };
 break;
 break;
-case 750: case 755: case 787: case 792: case 797: case 801:
+case 750: case 755: case 797:
 this.$ = { types: findReturnTypes($$[$0-3]) };
 this.$ = { types: findReturnTypes($$[$0-3]) };
 break;
 break;
 case 751:
 case 751:
@@ -1363,6 +1383,7 @@ case 756:
          suggestKeywords(['DISTINCT']);
          suggestKeywords(['DISTINCT']);
        }
        }
      }
      }
+     applyArgumentTypesToSuggestions($$[$0-3], 1);
      this.$ = { types: findReturnTypes($$[$0-3]) };
      this.$ = { types: findReturnTypes($$[$0-3]) };
    
    
 break;
 break;
@@ -1377,45 +1398,101 @@ case 758:
          suggestKeywords(['DISTINCT']);
          suggestKeywords(['DISTINCT']);
        }
        }
      }
      }
+     if (parser.yy.result.suggestFunctions && !parser.yy.result.suggestFunctions.types) {
+       applyArgumentTypesToSuggestions($$[$0-3], $$[$0-1].position);
+     }
      this.$ = { types: findReturnTypes($$[$0-3]) };
      this.$ = { types: findReturnTypes($$[$0-3]) };
    
    
 break;
 break;
-case 783: case 789:
+case 783:
 
 
      valueExpressionSuggest();
      valueExpressionSuggest();
+     applyTypeToSuggestions($$[$0-2].toLowerCase() === 'from' ? ['STRING'] : ['TIMESTAMP']);
      this.$ = { types: findReturnTypes($$[$0-4]) };
      this.$ = { types: findReturnTypes($$[$0-4]) };
    
    
 break;
 break;
-case 784: case 790:
+case 784:
 
 
      valueExpressionSuggest();
      valueExpressionSuggest();
+     applyTypeToSuggestions($$[$0-1].toLowerCase() === 'from' ? ['STRING'] : ['TIMESTAMP']);
      this.$ = { types: findReturnTypes($$[$0-3]) };
      this.$ = { types: findReturnTypes($$[$0-3]) };
    
    
 break;
 break;
 case 785:
 case 785:
 
 
      valueExpressionSuggest();
      valueExpressionSuggest();
+     applyTypeToSuggestions(['STRING', 'TIMESTAMP']);
+     this.$ = { types: findReturnTypes($$[$0-2]) };
+   
+break;
+case 786:
+
+     applyTypeToSuggestions($$[$0-2].toLowerCase() === 'from' ? ['STRING'] : ['TIMESTAMP']);
+     this.$ = { types: findReturnTypes($$[$0-4]) };
+   
+break;
+case 787:
+
+     applyTypeToSuggestions($$[$0-1].toLowerCase() === 'from' ? ['STRING'] : ['TIMESTAMP']);
+     this.$ = { types: findReturnTypes($$[$0-3]) };
+   
+break;
+case 788:
+
+     applyTypeToSuggestions(['STRING', 'TIMESTAMP']);
      this.$ = { types: findReturnTypes($$[$0-2]) };
      this.$ = { types: findReturnTypes($$[$0-2]) };
    
    
 break;
 break;
-case 786: case 791:
-this.$ = { types: findReturnTypes($$[$0-4]) };
+case 789:
+
+     valueExpressionSuggest();
+     applyTypeToSuggestions($$[$0-2].toLowerCase() === 'from' ? ['TIMESTAMP'] : ['STRING']);
+     this.$ = { types: findReturnTypes($$[$0-4]) };
+   
+break;
+case 790:
+
+     valueExpressionSuggest();
+     applyTypeToSuggestions($$[$0-1].toLowerCase() === 'from' ? ['TIMESTAMP'] : ['STRING']);
+     this.$ = { types: findReturnTypes($$[$0-3]) };
+   
+break;
+case 791:
+
+     applyTypeToSuggestions($$[$0-2].toLowerCase() === 'from' ? ['TIMESTAMP'] : ['STRING']);
+     this.$ = { types: findReturnTypes($$[$0-4]) };
+   
+break;
+case 792:
+
+    applyTypeToSuggestions($$[$0-1].toLowerCase() === 'from' ? ['TIMESTAMP'] : ['STRING']);
+     this.$ = { types: findReturnTypes($$[$0-3]) };
+   
 break;
 break;
 case 793:
 case 793:
 
 
-     suggestValueExpressionKeywords($$[$0-3], [',', 'FROM']);
+     if ($$[$0-4].types[0] === 'STRING') {
+       suggestValueExpressionKeywords($$[$0-3], ['FROM']);
+     } else {
+       suggestValueExpressionKeywords($$[$0-3]);
+     }
      this.$ = { types: findReturnTypes($$[$0-4]) };
      this.$ = { types: findReturnTypes($$[$0-4]) };
    
    
 break;
 break;
 case 794:
 case 794:
 
 
-     suggestValueExpressionKeywords($$[$0-2], [',', 'FROM']);
+     if ($$[$0-3].types[0] === 'STRING') {
+       suggestValueExpressionKeywords($$[$0-2], ['FROM']);
+     } else {
+       suggestValueExpressionKeywords($$[$0-2]);
+     }
      this.$ = { types: findReturnTypes($$[$0-3]) };
      this.$ = { types: findReturnTypes($$[$0-3]) };
    
    
 break;
 break;
 case 799:
 case 799:
 
 
      valueExpressionSuggest();
      valueExpressionSuggest();
+     applyArgumentTypesToSuggestions($$[$0-3], 1);
      if (!$$[$0-2]) {
      if (!$$[$0-2]) {
        if (isImpala()) {
        if (isImpala()) {
          suggestKeywords(['ALL', 'DISTINCT']);
          suggestKeywords(['ALL', 'DISTINCT']);
@@ -1425,6 +1502,14 @@ case 799:
      }
      }
      this.$ = { types: findReturnTypes($$[$0-3]) };
      this.$ = { types: findReturnTypes($$[$0-3]) };
    
    
+break;
+case 801:
+
+     if (parser.yy.result.suggestFunctions && ! parser.yy.result.suggestFunctions.types) {
+       applyArgumentTypesToSuggestions($$[$0-3], 1);
+     }
+     this.$ = { types: findReturnTypes($$[$0-3]) };
+   
 break;
 break;
 case 802:
 case 802:
 this.$ = [{ udtf: $$[$0-2], tableAlias: $$[$0-1], columnAliases: $$[$0] }];
 this.$ = [{ udtf: $$[$0-2], tableAlias: $$[$0-1], columnAliases: $$[$0] }];
@@ -1945,7 +2030,16 @@ var mergeSuggestKeywords = function() {
 }
 }
 
 
 var suggestValueExpressionKeywords = function (valueExpression, extras) {
 var suggestValueExpressionKeywords = function (valueExpression, extras) {
-  suggestKeywords(getValueExpressionKeywords(valueExpression, extras));
+  var expressionKeywords = getValueExpressionKeywords(valueExpression, extras)
+  suggestKeywords(expressionKeywords.suggestKeywords);
+  if (expressionKeywords.suggestColRefKeywords) {
+    suggestColRefKeywords(expressionKeywords.suggestColRefKeywords);
+  }
+  if (valueExpression.lastType) {
+    addColRefIfExists(valueExpression.lastType);
+  } else {
+    addColRefIfExists(valueExpression);
+  }
 }
 }
 
 
 var getValueExpressionKeywords = function (valueExpression, extras) {
 var getValueExpressionKeywords = function (valueExpression, extras) {
@@ -1955,9 +2049,22 @@ var getValueExpressionKeywords = function (valueExpression, extras) {
   if (isHive()) {
   if (isHive()) {
     keywords.push('<=>');
     keywords.push('<=>');
   }
   }
+  if (extras) {
+    keywords = keywords.concat(extras);
+  }
   if (valueExpression.suggestKeywords) {
   if (valueExpression.suggestKeywords) {
     keywords = keywords.concat(valueExpression.suggestKeywords);
     keywords = keywords.concat(valueExpression.suggestKeywords);
   }
   }
+  if (types.length === 1 &&  types[0] === 'COLREF') {
+    return {
+      suggestKeywords: keywords,
+      suggestColRefKeywords: {
+        BOOLEAN: ['AND', 'OR'],
+        NUMBER: ['+', '-', '*', '/', '%'],
+        STRING: ['LIKE', 'NOT LIKE', 'REGEX', 'RLIKE']
+      }
+    }
+  }
   if (parser.yy.sqlFunctions.matchesType(parser.yy.activeDialect, ['BOOLEAN'], types)) {
   if (parser.yy.sqlFunctions.matchesType(parser.yy.activeDialect, ['BOOLEAN'], types)) {
     keywords = keywords.concat(['AND', 'OR']);
     keywords = keywords.concat(['AND', 'OR']);
   }
   }
@@ -1967,10 +2074,7 @@ var getValueExpressionKeywords = function (valueExpression, extras) {
   if (parser.yy.sqlFunctions.matchesType(parser.yy.activeDialect, ['STRING'], types)) {
   if (parser.yy.sqlFunctions.matchesType(parser.yy.activeDialect, ['STRING'], types)) {
     keywords = keywords.concat(['LIKE', 'NOT LIKE', 'REGEX', 'RLIKE']);
     keywords = keywords.concat(['LIKE', 'NOT LIKE', 'REGEX', 'RLIKE']);
   }
   }
-  if (extras) {
-    keywords = keywords.concat(extras);
-  }
-  return keywords;
+  return { suggestKeywords: keywords };
 }
 }
 
 
 var suggestTypeKeywords = function () {
 var suggestTypeKeywords = function () {
@@ -1983,9 +2087,16 @@ var suggestTypeKeywords = function () {
   }
   }
 }
 }
 
 
+var addColRefIfExists = function (valueExpression) {
+  if (valueExpression.columnReference) {
+    parser.yy.result.colRef = { identifierChain: valueExpression.columnReference };
+  }
+}
+
 var valueExpressionSuggest = function (oppositeValueExpression) {
 var valueExpressionSuggest = function (oppositeValueExpression) {
   if (oppositeValueExpression && oppositeValueExpression.columnReference) {
   if (oppositeValueExpression && oppositeValueExpression.columnReference) {
-    suggestValues({ identifierChain: oppositeValueExpression.columnReference });
+    suggestValues();
+    parser.yy.result.colRef = { identifierChain: oppositeValueExpression.columnReference };
   }
   }
   suggestColumns();
   suggestColumns();
   suggestFunctions();
   suggestFunctions();
@@ -2039,6 +2150,30 @@ var applyArgumentTypesToSuggestions = function (funcToken, position) {
 
 
 var prioritizeSuggestions = function () {
 var prioritizeSuggestions = function () {
   parser.yy.result.lowerCase = parser.yy.lowerCase || false;
   parser.yy.result.lowerCase = parser.yy.lowerCase || false;
+  if (typeof parser.yy.result.colRef !== 'undefined') {
+    if (typeof parser.yy.result.colRef.table === 'undefined') {
+      delete parser.yy.result.colRef;
+      if (typeof parser.yy.result.suggestColRefKeywords !== 'undefined') {
+        Object.keys(parser.yy.result.suggestColRefKeywords).forEach(function (type) {
+          parser.yy.result.suggestKeywords = parser.yy.result.suggestKeywords.concat(parser.yy.result.suggestColRefKeywords[type]);
+        });
+        delete parser.yy.result.suggestColRefKeywords;
+      }
+      if (parser.yy.result.suggestColumns && parser.yy.result.suggestColumns.types.length === 1 && parser.yy.result.suggestColumns.types[0] === 'COLREF') {
+        parser.yy.result.suggestColumns.types = ['T'];
+      }
+      delete parser.yy.result.suggestValues;
+    }
+  }
+
+  if (typeof parser.yy.result.colRef !== 'undefined') {
+    if (!parser.yy.result.suggestValues &&
+        !parser.yy.result.suggestColRefKeywords &&
+        (!parser.yy.result.suggestColumns ||
+          parser.yy.result.suggestColumns.types[0] !== 'COLREF')) {
+      delete parser.yy.result.colRef;
+    }
+  }
   if (typeof parser.yy.result.suggestIdentifiers !== 'undefined' &&  parser.yy.result.suggestIdentifiers.length > 0) {
   if (typeof parser.yy.result.suggestIdentifiers !== 'undefined' &&  parser.yy.result.suggestIdentifiers.length > 0) {
     if (!parser.yy.keepColumns) {
     if (!parser.yy.keepColumns) {
      delete parser.yy.result.suggestColumns;
      delete parser.yy.result.suggestColumns;
@@ -2271,8 +2406,8 @@ var linkTablePrimaries = function () {
    if (typeof parser.yy.result.suggestColumns !== 'undefined') {
    if (typeof parser.yy.result.suggestColumns !== 'undefined') {
      linkSuggestion(parser.yy.result.suggestColumns, true);
      linkSuggestion(parser.yy.result.suggestColumns, true);
    }
    }
-   if (typeof parser.yy.result.suggestValues !== 'undefined') {
-     linkSuggestion(parser.yy.result.suggestValues, false);
+   if (typeof parser.yy.result.colRef !== 'undefined') {
+     linkSuggestion(parser.yy.result.colRef, false);
    }
    }
 }
 }
 
 
@@ -2301,9 +2436,15 @@ var suggestDdlAndDmlKeywords = function () {
   suggestKeywords(keywords);
   suggestKeywords(keywords);
 }
 }
 
 
-var checkForKeywords = function (rule) {
-  if (rule && rule.suggestKeywords && rule.suggestKeywords.length > 0) {
-    suggestKeywords(rule.suggestKeywords);
+var checkForKeywords = function (expression) {
+  if (expression) {
+    if (expression.suggestKeywords && expression.suggestKeywords.length > 0) {
+      suggestKeywords(expression.suggestKeywords);
+    }
+    if (expression.suggestColRefKeywords) {
+      suggestColRefKeywords(expression.suggestColRefKeywords)
+      addColRefIfExists(expression);
+    }
   }
   }
 }
 }
 
 
@@ -2311,6 +2452,10 @@ var suggestKeywords = function (keywords) {
   parser.yy.result.suggestKeywords = keywords.sort();
   parser.yy.result.suggestKeywords = keywords.sort();
 }
 }
 
 
+var suggestColRefKeywords = function (colRefKeywords) {
+  parser.yy.result.suggestColRefKeywords = colRefKeywords;
+}
+
 var suggestTablesOrColumns = function (identifier) {
 var suggestTablesOrColumns = function (identifier) {
   if (typeof parser.yy.latestTablePrimaries == 'undefined') {
   if (typeof parser.yy.latestTablePrimaries == 'undefined') {
     suggestTables({ database: identifier });
     suggestTables({ database: identifier });
@@ -2356,7 +2501,7 @@ var suggestHdfs = function (details) {
 }
 }
 
 
 var suggestValues = function (details) {
 var suggestValues = function (details) {
-  parser.yy.result.suggestValues = details || {};
+  parser.yy.result.suggestValues = true;
 }
 }
 
 
 var determineCase = function (text) {
 var determineCase = function (text) {

+ 269 - 213
desktop/core/src/desktop/static/desktop/js/sqlAutocompleter2.js

@@ -41,6 +41,7 @@
     var self = this;
     var self = this;
     var parseResult = sqlParser.parseSql(beforeCursor, afterCursor, self.snippet.type(), sqlFunctions);
     var parseResult = sqlParser.parseSql(beforeCursor, afterCursor, self.snippet.type(), sqlFunctions);
 
 
+    var deferrals = [];
     var completions = [];
     var completions = [];
 
 
     if (parseResult.suggestKeywords) {
     if (parseResult.suggestKeywords) {
@@ -59,245 +60,300 @@
       });
       });
     }
     }
 
 
-    if (parseResult.suggestFunctions) {
-      sqlFunctions.suggestFunctions(self.snippet.type(), parseResult.suggestFunctions.types || ['T'], parseResult.suggestAggregateFunctions || false, completions);
+    var database = parseResult.useDatabase || self.snippet.database();
+
+    var colRefDeferral = $.Deferred();
+    deferrals.push(colRefDeferral);
+    var colRef = null;
+
+    if (parseResult.colRef) {
+      self.snippet.getApiHelper().fetchFields({
+        sourceType: self.snippet.type(),
+        databaseName: parseResult.colRef.database || database,
+        tableName: parseResult.colRef.table,
+        fields: $.map(parseResult.colRef.identifierChain, function (value) {
+          return value.name
+        }),
+        editor: editor,
+        timeout: self.timeout,
+        successCallback: function (data) {
+          colRef = data;
+          colRefDeferral.resolve();
+        },
+        silenceErrors: true,
+        errorCallback: colRefDeferral.resolve
+      });
+    } else {
+      colRefDeferral.resolve();
     }
     }
 
 
-    if (parseResult.suggestDatabases || parseResult.suggestHdfs || parseResult.suggestTables || parseResult.suggestColumns || parseResult.suggestValues) {
-      var database = parseResult.useDatabase || self.snippet.database();
+    if (parseResult.suggestFunctions) {
+      var suggestFunctionsDeferral = $.Deferred();
+      if (parseResult.suggestFunctions.types && parseResult.suggestFunctions.types[0] === 'COLREF') {
+        $.when.apply($, colRefDeferral).done(function () {
+          if (colRef !== null) {
+            sqlFunctions.suggestFunctions(self.snippet.type(), [colRef.type.toUpperCase()], parseResult.suggestAggregateFunctions || false, completions);
+          } else {
+            sqlFunctions.suggestFunctions(self.snippet.type(), ['T'], parseResult.suggestAggregateFunctions || false, completions);
+          }
+          suggestFunctionsDeferral.resolve();
+        });
+      } else {
+        sqlFunctions.suggestFunctions(self.snippet.type(), parseResult.suggestFunctions.types || ['T'], parseResult.suggestAggregateFunctions || false, completions);
+        suggestFunctionsDeferral.resolve();
+      }
+      deferrals.push(suggestFunctionsDeferral);
+    }
 
 
-      var deferrals = [];
+    if (parseResult.suggestValues) {
+      var suggestValuesDeferral = $.Deferred();
+      $.when.apply($, colRefDeferral).done(function () {
+        if (colRef !== null) {
+          self.addValues(colRef, completions);
+        }
+        suggestValuesDeferral.resolve();
+      });
+      deferrals.push(suggestValuesDeferral);
+    }
 
 
-      if (parseResult.suggestDatabases) {
-        var prefix = parseResult.suggestDatabases.prependQuestionMark ? '? ' : '';
-        if (parseResult.suggestDatabases.prependFrom) {
-          prefix += parseResult.lowerCase ? 'from ' : 'FROM ';
+    if (parseResult.suggestColRefKeywords) {
+      var suggestColRefKeywordsDeferral = $.Deferred();
+      $.when.apply($, colRefDeferral).done(function () {
+        if (colRef !== null) {
+          self.addColRefKeywords(parseResult, colRef.type, completions);
         }
         }
+        suggestColRefKeywordsDeferral.resolve();
+      });
+      deferrals.push(suggestColRefKeywordsDeferral);
+    }
 
 
-        var databaseDeferred = $.Deferred();
-        deferrals.push(databaseDeferred);
+    if (parseResult.suggestColumns) {
+      var suggestColumnsDeferral =  $.Deferred();
+      if (parseResult.suggestColumns.types && parseResult.suggestColumns.types[0] === 'COLREF') {
+        $.when.apply($, colRefDeferral).done(function () {
+          if (colRef !== null) {
+            deferrals.push(self.addColumns(parseResult, editor, database, [colRef.type.toUpperCase()], completions));
+          } else {
+            deferrals.push(self.addColumns(parseResult, editor, database, ['T'], completions));
+          }
+          suggestColumnsDeferral.resolve();
+        });
+      } else {
+        deferrals.push(self.addColumns(parseResult, editor, database, parseResult.suggestColumns.types || ['T'], completions));
+        suggestColumnsDeferral.resolve();
+      }
+      deferrals.push(suggestColumnsDeferral);
+    }
 
 
-        self.snippet.getApiHelper().loadDatabases({
-          sourceType: self.snippet.type(),
-          successCallback: function (data) {
-            data.forEach(function (db) {
-              completions.push({
-                value: prefix + db + (parseResult.suggestDatabases.appendDot ? '.' : ''),
-                meta: 'database',
-                type: 'database'
-              });
-            });
-            databaseDeferred.resolve();
+    if (parseResult.suggestDatabases) {
+      deferrals.push(self.addDatabases(parseResult, completions));
+    }
 
 
-          },
-          silenceErrors: true,
-          errorCallback: databaseDeferred.resolve
-        })
+    if (parseResult.suggestHdfs) {
+      deferrals.push(self.addHdfs(parseResult, completions));
+    }
 
 
-      }
+    if (parseResult.suggestTables) {
+      deferrals.push(self.addTables(parseResult, editor, database, completions))
+    }
 
 
-      if (parseResult.suggestHdfs) {
-        var parts = parseResult.suggestHdfs.path.split('/');
-        // Drop the first " or '
-        parts.shift();
-        // Last one is either partial name or empty
-        parts.pop();
-
-        var hdfsDeferred = $.Deferred();
-        deferrals.push(hdfsDeferred);
-
-        self.snippet.getApiHelper().fetchHdfsPath({
-          pathParts: parts,
-          successCallback: function (data) {
-            if (!data.error) {
-              data.files.forEach(function (file) {
-                if (file.name !== '..' && file.name !== '.') {
-                  completions.push({
-                    value: parseResult.suggestHdfs.path === '' ? '/' + file.name : file.name,
-                    meta: file.type,
-                    type: 'HDFS'
-                  });
-                }
-              });
-            }
-            hdfsDeferred.resolve();
-          },
-          silenceErrors: true,
-          errorCallback: hdfsDeferred.resolve,
-          editor: editor,
-          timeout: self.timeout
-        });
+    $.when.apply($, deferrals).done(function () {
+      self.finalizeCompletions(completions, callback, editor);
+    });
+  };
+
+  SqlAutocompleter2.prototype.addValues = function (columnReference, completions) {
+    if (columnReference.sample) {
+      var isString = columnReference.type === "string";
+      columnReference.sample.forEach(function (sample) {
+        completions.push({meta: 'value', value: isString ? "'" + sample + "'" : new String(sample), type: 'sample'})
+      });
+    }
+  };
+
+  SqlAutocompleter2.prototype.addColRefKeywords = function (parseResult, type, completions) {
+    var self = this;
+    Object.keys(parseResult.suggestColRefKeywords).forEach(function (typeForKeywords) {
+      if (sqlFunctions.matchesType(self.snippet.type(), [typeForKeywords], [type.toUpperCase()])) {
+        parseResult.suggestColRefKeywords[typeForKeywords].forEach(function (keyword) {
+          completions.push({
+            value: parseResult.lowerCase ? keyword.toLowerCase() : keyword,
+            meta: 'keyword',
+            type: 'keyword'
+          });
+        })
       }
       }
+    });
+  };
 
 
-      if (parseResult.suggestTables) {
-        var prefix = parseResult.suggestTables.prependQuestionMark ? '? ' : '';
-        if (parseResult.suggestTables.prependFrom) {
-          prefix += parseResult.lowerCase ? 'from ' : 'FROM ';
-        }
+  SqlAutocompleter2.prototype.addTables = function (parseResult, editor, database, completions) {
+    var self = this;
+    var tableDeferred = $.Deferred();
+    var prefix = parseResult.suggestTables.prependQuestionMark ? '? ' : '';
+    if (parseResult.suggestTables.prependFrom) {
+      prefix += parseResult.lowerCase ? 'from ' : 'FROM ';
+    }
 
 
-        var tableDeferred = $.Deferred();
-        deferrals.push(tableDeferred);
-        self.snippet.getApiHelper().fetchTables({
-          sourceType: self.snippet.type(),
-          databaseName: parseResult.suggestTables.database || database,
-          successCallback: function (data) {
-            data.tables_meta.forEach(function (tablesMeta) {
-              completions.push({
-                value: prefix + tablesMeta.name,
-                meta: tablesMeta.type.toLowerCase(),
-                type: 'table'
-              })
-            });
-            tableDeferred.resolve();
-          },
-          silenceErrors: true,
-          errorCallback: tableDeferred.resolve,
-          editor: editor,
-          timeout: self.timeout
+    self.snippet.getApiHelper().fetchTables({
+      sourceType: self.snippet.type(),
+      databaseName: parseResult.suggestTables.database || database,
+      successCallback: function (data) {
+        data.tables_meta.forEach(function (tablesMeta) {
+          completions.push({
+            value: prefix + tablesMeta.name,
+            meta: tablesMeta.type.toLowerCase(),
+            type: 'table'
+          })
         });
         });
-      }
+        tableDeferred.resolve();
+      },
+      silenceErrors: true,
+      errorCallback: tableDeferred.resolve,
+      editor: editor,
+      timeout: self.timeout
+    });
+    return tableDeferred;
+  }
 
 
-      if (parseResult.suggestColumns) {
-        var columnsDeferred = $.Deferred();
-        deferrals.push(columnsDeferred);
+  SqlAutocompleter2.prototype.addColumns = function (parseResult, editor, database, types, completions) {
+    var self = this;
+    var addColumnsDeferred = $.Deferred();
+    var fields = [];
+    if (parseResult.suggestColumns.identifierChain) {
+      parseResult.suggestColumns.identifierChain.forEach(function (identifier) {
+        var field = identifier.name;
+        if (identifier.key) {
+          field += '[' + identifier.key + ']';
+        }
+        fields.push(field);
+      });
+    }
 
 
-        var fields = [];
-        if (parseResult.suggestColumns.identifierChain) {
-          parseResult.suggestColumns.identifierChain.forEach(function (identifier) {
-            var field = identifier.name;
-            if (identifier.key) {
-              field += '[' + identifier.key + ']';
+    self.snippet.getApiHelper().fetchFields({
+      sourceType: self.snippet.type(),
+      databaseName: parseResult.suggestColumns.database || database,
+      tableName: parseResult.suggestColumns.table,
+      fields: fields,
+      editor: editor,
+      timeout: self.timeout,
+      successCallback: function (data) {
+        if (data.extended_columns) {
+          data.extended_columns.forEach(function (column) {
+            if (column.type.indexOf('map') === 0 && self.snippet.type() === 'hive') {
+              completions.push({value: column.name + '[]', meta: 'map', type: 'column'})
+            } else if (column.type.indexOf('map') === 0) {
+              completions.push({value: column.name, meta: 'map', type: 'column'})
+            } else if (column.type.indexOf('struct') === 0) {
+              completions.push({value: column.name, meta: 'struct', type: 'column'})
+            } else if (column.type.indexOf('array') === 0 && self.snippet.type() === 'hive') {
+              completions.push({value: column.name + '[]', meta: 'array', type: 'column'})
+            } else if (column.type.indexOf('array') === 0) {
+              completions.push({value: column.name, meta: 'array', type: 'column'})
+            } else if (sqlFunctions.matchesType(self.snippet.type(), types, [column.type.toUpperCase()]) ||
+                sqlFunctions.matchesType(self.snippet.type(), [column.type.toUpperCase()], types)) {
+              completions.push({value: column.name, meta: column.type, type: 'column'})
             }
             }
-            fields.push(field);
+          });
+        } else if (data.columns) {
+          data.columns.forEach(function (column) {
+            completions.push({value: column, meta: 'column', type: 'column'})
           });
           });
         }
         }
-
-        self.snippet.getApiHelper().fetchFields({
-          sourceType: self.snippet.type(),
-          databaseName: parseResult.suggestColumns.database || database,
-          tableName: parseResult.suggestColumns.table,
-          fields: fields,
-          editor: editor,
-          timeout: self.timeout,
-          successCallback: function (data) {
-            if (data.extended_columns) {
-              data.extended_columns.forEach(function (column) {
-                if (column.type.indexOf('map') === 0 && self.snippet.type() === 'hive') {
-                  completions.push({value: column.name + '[]', meta: 'map', type: 'column'})
-                } else if (column.type.indexOf('map') === 0) {
-                  completions.push({value: column.name, meta: 'map', type: 'column'})
-                } else if (column.type.indexOf('struct') === 0) {
-                  completions.push({value: column.name, meta: 'struct', type: 'column'})
-                } else if (column.type.indexOf('array') === 0 && self.snippet.type() === 'hive') {
-                  completions.push({value: column.name + '[]', meta: 'array', type: 'column'})
-                } else if (column.type.indexOf('array') === 0) {
-                  completions.push({value: column.name, meta: 'array', type: 'column'})
-                } else if (sqlFunctions.matchesType(self.snippet.type(), parseResult.suggestColumns.types || ['T'], [column.type.toUpperCase()])) {
-                  completions.push({value: column.name, meta: column.type, type: 'column'})
-                }
-              });
-            } else if (data.columns) {
-              data.columns.forEach(function (column) {
-                completions.push({value: column, meta: 'column', type: 'column'})
-              });
-            }
-            if (data.type === 'map' && self.snippet.type() === 'impala') {
-              completions.push({value: 'key', meta: 'key', type: 'column'});
-              completions.push({value: 'value', meta: 'value', type: 'column'});
-            }
-            if (data.type === 'struct') {
-              data.fields.forEach(function (field) {
-                completions.push({value: field.name, meta: 'struct', type: 'column'})
-              });
-            } else if (data.type === 'map' && (data.value && data.value.fields)) {
-              data.value.fields.forEach(function (field) {
-                if (sqlFunctions.matchesType(self.snippet.type(), parseResult.suggestColumns.types || ['T'], [field.type.toUpperCase()])) {
-                  completions.push({value: field.name, meta: field.type, type: 'column'});
-                }
-              });
-            } else if (data.type === 'array' && (data.item && data.item.fields)) {
-              data.item.fields.forEach(function (field) {
-                if ((field.type === 'array' || field.type === 'map')) {
-                  if (self.snippet.type() === 'hive') {
-                    completions.push({value: field.name + '[]', meta: field.type, type: 'column'});
-                  } else {
-                    completions.push({value: field.name, meta: field.type, type: 'column'});
-                  }
-                } else if (sqlFunctions.matchesType(self.snippet.type(), parseResult.suggestColumns.types || ['T'], [field.type.toUpperCase()])) {
-                  completions.push({value: field.name, meta: field.type, type: 'column'});
-                }
-              });
+        if (data.type === 'map' && self.snippet.type() === 'impala') {
+          completions.push({value: 'key', meta: 'key', type: 'column'});
+          completions.push({value: 'value', meta: 'value', type: 'column'});
+        }
+        if (data.type === 'struct') {
+          data.fields.forEach(function (field) {
+            completions.push({value: field.name, meta: 'struct', type: 'column'})
+          });
+        } else if (data.type === 'map' && (data.value && data.value.fields)) {
+          data.value.fields.forEach(function (field) {
+            if (sqlFunctions.matchesType(self.snippet.type(), types, [field.type.toUpperCase()]) ||
+                sqlFunctions.matchesType(self.snippet.type(), [column.type.toUpperCase()], types)) {
+              completions.push({value: field.name, meta: field.type, type: 'column'});
             }
             }
-            columnsDeferred.resolve();
-          },
-          silenceErrors: true,
-          errorCallback: columnsDeferred.resolve
-        });
-      }
-
-      if (parseResult.suggestValues) {
-        if (self.snippet.type() === 'impala') {
-          var impalaValuesDeferred = $.Deferred();
-          deferrals.push(impalaValuesDeferred);
-          // TODO: Fetch for each identifier in the chain, we need to add key or value for impala
-          //       select a.key from customers c, c.addresses a WHERE a.zip_code = |
-          // Same goes for Hive
-          //       SELECT orders[].items[].| FROM customers
-          self.snippet.getApiHelper().fetchFields({
-            sourceType: self.snippet.type(),
-            databaseName: parseResult.suggestValues.database || database,
-            tableName: parseResult.suggestValues.table,
-            fields: $.map(parseResult.suggestValues.identifierChain, function (value) {
-              return value.name
-            }),
-            editor: editor,
-            timeout: self.timeout,
-            successCallback: function (data) {
-              if (data.sample) {
-                var isString = data.type === "string";
-                data.sample.forEach(function (sample) {
-                  completions.push({meta: 'value', value: isString ? "'" + sample + "'" : new String(sample), type: 'sample'})
-                });
-              }
-              impalaValuesDeferred.resolve();
-            },
-            silenceErrors: true,
-            errorCallback: impalaValuesDeferred.resolve
           });
           });
-        } else {
-          var valuesDeferred = $.Deferred();
-          deferrals.push(valuesDeferred);
-          self.snippet.getApiHelper().fetchTableSample({
-            sourceType: self.snippet.type(),
-            databaseName: parseResult.suggestValues.database || database,
-            tableName: parseResult.suggestValues.table,
-            columnName: parseResult.suggestValues.identifierChain[0].name,
-            editor: editor,
-            timeout: self.timeout,
-            successCallback: function (data) {
-              if (data.status === 0 && data.headers.length === 1) {
-                data.rows.forEach(function (row) {
-                  completions.push({
-                    value: typeof row[0] === 'string' ? "'" + row[0] + "'" : '' + row[0],
-                    meta: 'sample',
-                    type: 'sample'
-                  });
-                });
+        } else if (data.type === 'array' && (data.item && data.item.fields)) {
+          data.item.fields.forEach(function (field) {
+            if ((field.type === 'array' || field.type === 'map')) {
+              if (self.snippet.type() === 'hive') {
+                completions.push({value: field.name + '[]', meta: field.type, type: 'column'});
+              } else {
+                completions.push({value: field.name, meta: field.type, type: 'column'});
               }
               }
-              valuesDeferred.resolve();
-            },
-            silenceErrors: true,
-            errorCallback: valuesDeferred.resolve
+            } else if (sqlFunctions.matchesType(self.snippet.type(), types, [field.type.toUpperCase()]) ||
+                sqlFunctions.matchesType(self.snippet.type(), [column.type.toUpperCase()], types)) {
+              completions.push({value: field.name, meta: field.type, type: 'column'});
+            }
           });
           });
         }
         }
-      }
+        addColumnsDeferred.resolve();
+      },
+      silenceErrors: true,
+      errorCallback: addColumnsDeferred.resolve
+    });
 
 
-      $.when.apply($, deferrals).done(function () {
-        self.finalizeCompletions(completions, callback, editor);
-      });
-    } else {
-      self.finalizeCompletions(completions, callback, editor);
+    return addColumnsDeferred;
+  };
+
+  SqlAutocompleter2.prototype.addDatabases = function (parseResult, completions) {
+    var self = this;
+    var databasesDeferred = $.Deferred();
+    var prefix = parseResult.suggestDatabases.prependQuestionMark ? '? ' : '';
+    if (parseResult.suggestDatabases.prependFrom) {
+      prefix += parseResult.lowerCase ? 'from ' : 'FROM ';
     }
     }
+    self.snippet.getApiHelper().loadDatabases({
+      sourceType: self.snippet.type(),
+      successCallback: function (data) {
+        data.forEach(function (db) {
+          completions.push({
+            value: prefix + db + (parseResult.suggestDatabases.appendDot ? '.' : ''),
+            meta: 'database',
+            type: 'database'
+          });
+        });
+        databasesDeferred.resolve();
+      },
+      silenceErrors: true,
+      errorCallback: databasesDeferred.resolve
+    });
+    return databasesDeferred;
+  };
+
+  SqlAutocompleter2.prototype.addHdfs = function (parseResult, completions) {
+    var self = this;
+    var hdfsDeferred = $.Deferred();
+    var parts = parseResult.suggestHdfs.path.split('/');
+    // Drop the first " or '
+    parts.shift();
+    // Last one is either partial name or empty
+    parts.pop();
+
+    self.snippet.getApiHelper().fetchHdfsPath({
+      pathParts: parts,
+      successCallback: function (data) {
+        if (!data.error) {
+          data.files.forEach(function (file) {
+            if (file.name !== '..' && file.name !== '.') {
+              completions.push({
+                value: parseResult.suggestHdfs.path === '' ? '/' + file.name : file.name,
+                meta: file.type,
+                type: 'HDFS'
+              });
+            }
+          });
+        }
+        hdfsDeferred.resolve();
+      },
+      silenceErrors: true,
+      errorCallback: hdfsDeferred.resolve,
+      editor: editor,
+      timeout: self.timeout
+    });
+
+    return hdfsDeferred;
   };
   };
 
 
   SqlAutocompleter2.prototype.finalizeCompletions = function (completions, callback, editor) {
   SqlAutocompleter2.prototype.finalizeCompletions = function (completions, callback, editor) {

+ 163 - 159
desktop/core/src/desktop/static/desktop/spec/autocomplete/sqlSpecSelect.js

@@ -542,9 +542,11 @@ define([
         assertAutoComplete({
         assertAutoComplete({
           beforeCursor: 'SELECT a ',
           beforeCursor: 'SELECT a ',
           afterCursor: ' FROM tableA;',
           afterCursor: ' FROM tableA;',
-          containsKeywords: ['AS', '+'],
+          containsKeywords: ['AS', '='],
+          containsColRefKeywords: true,
           expectedResult: {
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            colRef: { identifierChain: [{ name: 'a' }], table: 'tableA' }
           }
           }
         });
         });
       });
       });
@@ -553,9 +555,11 @@ define([
         assertAutoComplete({
         assertAutoComplete({
           beforeCursor: 'SELECT a ',
           beforeCursor: 'SELECT a ',
           afterCursor: ', FROM tableA;',
           afterCursor: ', FROM tableA;',
-          containsKeywords: ['AS', '+'],
+          containsKeywords: ['AS', '='],
+          containsColRefKeywords: true,
           expectedResult: {
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            colRef: { identifierChain: [{ name: 'a' }], table: 'tableA' }
           }
           }
         });
         });
       });
       });
@@ -564,9 +568,11 @@ define([
         assertAutoComplete({
         assertAutoComplete({
           beforeCursor: 'SELECT a, b ',
           beforeCursor: 'SELECT a, b ',
           afterCursor: ' FROM tableA;',
           afterCursor: ' FROM tableA;',
-          containsKeywords: ['AS', '+'],
+          containsKeywords: ['AS', '='],
+          containsColRefKeywords: true,
           expectedResult: {
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            colRef: { identifierChain: [{ name: 'b' }], table: 'tableA' }
           }
           }
         });
         });
       });
       });
@@ -575,9 +581,11 @@ define([
         assertAutoComplete({
         assertAutoComplete({
           beforeCursor: 'SELECT a ',
           beforeCursor: 'SELECT a ',
           afterCursor: ', b, c AS foo, d FROM tableA;',
           afterCursor: ', b, c AS foo, d FROM tableA;',
-          containsKeywords: ['AS', '+'],
+          containsKeywords: ['AS', '='],
+          containsColRefKeywords: true,
           expectedResult: {
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            colRef: { identifierChain: [{ name: 'a' }], table: 'tableA' }
           }
           }
         });
         });
       });
       });
@@ -682,9 +690,10 @@ define([
           afterCursor: ',bla) FROM bar;',
           afterCursor: ',bla) FROM bar;',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: { types: ['T'] },
-            suggestColumns: { types: ['T'], table: 'bar' },
-            suggestValues: { identifierChain: [ {name: 'bl' }], table: 'bar' }
+            suggestFunctions: { types: ['COLREF'] },
+            suggestColumns: { types: ['COLREF'], table: 'bar' },
+            suggestValues: true,
+            colRef: { identifierChain: [ {name: 'bl' }], table: 'bar' }
           }
           }
         });
         });
       });
       });
@@ -787,9 +796,11 @@ define([
         assertAutoComplete({
         assertAutoComplete({
           beforeCursor: 'SELECT CAST(bla ',
           beforeCursor: 'SELECT CAST(bla ',
           afterCursor: ' FROM bar;',
           afterCursor: ' FROM bar;',
-          containsKeywords: ['AS', 'AND'],
+          containsKeywords: ['AS', '='],
+          containsColRefKeywords: true,
           expectedResult: {
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            colRef: { identifierChain: [{ name: 'bla'}], table: 'bar'}
           }
           }
         });
         });
       });
       });
@@ -838,15 +849,15 @@ define([
         });
         });
       });
       });
 
 
-      it('should suggest columns for "SELECT extract(bla FROM |  FROM bar;"', function() {
+      it('should suggest columns for "SELECT extract(bla FROM | FROM bar;"', function() {
         assertAutoComplete({
         assertAutoComplete({
           beforeCursor: 'SELECT extract(bla FROM ',
           beforeCursor: 'SELECT extract(bla FROM ',
           afterCursor: ' FROM bar;',
           afterCursor: ' FROM bar;',
           dialect: 'impala',
           dialect: 'impala',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: {},
-            suggestColumns: { table: 'bar' }
+            suggestFunctions: { types: ['TIMESTAMP'] },
+            suggestColumns: { types: ['TIMESTAMP'], table: 'bar' }
           }
           }
         });
         });
       });
       });
@@ -858,8 +869,8 @@ define([
           dialect: 'impala',
           dialect: 'impala',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: {},
-            suggestColumns: { table: 'bar' }
+            suggestFunctions: { types: ['STRING'] },
+            suggestColumns: { types: ['STRING'], table: 'bar' }
           }
           }
         });
         });
       });
       });
@@ -871,8 +882,8 @@ define([
           dialect: 'impala',
           dialect: 'impala',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: {},
-            suggestColumns: { table: 'bar' }
+            suggestFunctions: { types: ['TIMESTAMP'] },
+            suggestColumns: { types: ['TIMESTAMP'], table: 'bar' }
           }
           }
         });
         });
       });
       });
@@ -884,8 +895,8 @@ define([
           dialect: 'impala',
           dialect: 'impala',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: {},
-            suggestColumns: { table: 'bar' }
+            suggestFunctions: { types: ['STRING'] },
+            suggestColumns: { types: ['STRING'], table: 'bar' }
           }
           }
         });
         });
       });
       });
@@ -902,7 +913,7 @@ define([
           { name: 'COUNT', dialect: 'generic', suggestKeywords: ['*', 'DISTINCT'] },
           { name: 'COUNT', dialect: 'generic', suggestKeywords: ['*', 'DISTINCT'] },
           { name: 'COUNT', dialect: 'hive', suggestKeywords: ['*', 'DISTINCT'] },
           { name: 'COUNT', dialect: 'hive', suggestKeywords: ['*', 'DISTINCT'] },
           { name: 'COUNT', dialect: 'impala', suggestKeywords: ['*', 'ALL', 'DISTINCT'] },
           { name: 'COUNT', dialect: 'impala', suggestKeywords: ['*', 'ALL', 'DISTINCT'] },
-          { name: 'GROUP_CONCAT', dialect: 'impala', suggestKeywords: ['ALL'] },
+          { name: 'GROUP_CONCAT', dialect: 'impala', suggestKeywords: ['ALL'], types: ['STRING'] },
           { name: 'stddev', dialect: 'impala', suggestKeywords: ['ALL', 'DISTINCT'] },
           { name: 'stddev', dialect: 'impala', suggestKeywords: ['ALL', 'DISTINCT'] },
           { name: 'STDDEV_POP', dialect: 'generic', suggestKeywords: ['DISTINCT'] },
           { name: 'STDDEV_POP', dialect: 'generic', suggestKeywords: ['DISTINCT'] },
           { name: 'STDDEV_POP', dialect: 'hive', suggestKeywords: ['DISTINCT'] },
           { name: 'STDDEV_POP', dialect: 'hive', suggestKeywords: ['DISTINCT'] },
@@ -946,8 +957,9 @@ define([
           } else {
           } else {
             var expectedResult = {
             var expectedResult = {
               lowerCase: false,
               lowerCase: false,
-              suggestFunctions: {},
+              suggestFunctions: { types: aggregateFunction.types || ['T'] },
               suggestColumns: {
               suggestColumns: {
+                types: aggregateFunction.types || ['T'],
                 table: 'testTable'
                 table: 'testTable'
               }
               }
             };
             };
@@ -978,8 +990,9 @@ define([
             dialect: binaryFunction.dialect,
             dialect: binaryFunction.dialect,
             expectedResult: {
             expectedResult: {
               lowerCase: false,
               lowerCase: false,
-              suggestFunctions: {},
+              suggestFunctions: { types: ['T'] },
               suggestColumns: {
               suggestColumns: {
+                types: ['T'],
                 table: 'testTable'
                 table: 'testTable'
               },
               },
               suggestKeywords: ['DISTINCT']
               suggestKeywords: ['DISTINCT']
@@ -1002,8 +1015,9 @@ define([
             dialect: binaryFunction.dialect,
             dialect: binaryFunction.dialect,
             expectedResult: {
             expectedResult: {
               lowerCase: false,
               lowerCase: false,
-              suggestFunctions: {},
+              suggestFunctions: { types: ['T'] },
               suggestColumns: {
               suggestColumns: {
+                types: ['T'],
                 table: 'testTable'
                 table: 'testTable'
               }
               }
             }
             }
@@ -1025,8 +1039,9 @@ define([
             dialect: binaryFunction.dialect,
             dialect: binaryFunction.dialect,
             expectedResult: {
             expectedResult: {
               lowerCase: false,
               lowerCase: false,
-              suggestFunctions: {},
+              suggestFunctions: { types: ['T'] },
               suggestColumns: {
               suggestColumns: {
+                types: ['T'],
                 table: 'testTable'
                 table: 'testTable'
               },
               },
               suggestKeywords: ['DISTINCT']
               suggestKeywords: ['DISTINCT']
@@ -1138,12 +1153,13 @@ define([
           afterCursor: ' WHEN c THEN d END FROM testTable',
           afterCursor: ' WHEN c THEN d END FROM testTable',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: { types: ['T'] },
+            suggestFunctions: { types: ['COLREF'] },
             suggestColumns: {
             suggestColumns: {
-              types: ['T'],
+              types: ['COLREF'],
               table: 'testTable'
               table: 'testTable'
             },
             },
-            suggestValues: { identifierChain: [{ name: 'a' }], table: 'testTable' }
+            suggestValues: true,
+            colRef: { identifierChain: [{ name: 'a' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -1154,12 +1170,13 @@ define([
           afterCursor: ' WHEN c THEN d ELSE e END FROM testTable',
           afterCursor: ' WHEN c THEN d ELSE e END FROM testTable',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: { types: ['T'] },
+            suggestFunctions: { types: ['COLREF'] },
             suggestColumns: {
             suggestColumns: {
-              types: ['T'],
+              types: ['COLREF'],
               table: 'testTable'
               table: 'testTable'
             },
             },
-            suggestValues: { identifierChain: [{ name: 'a' }], table: 'testTable' }
+            suggestValues: true,
+            colRef: { identifierChain: [{ name: 'a' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -1168,9 +1185,11 @@ define([
         assertAutoComplete({
         assertAutoComplete({
           beforeCursor: 'SELECT CASE a = c WHEN c THEN d ',
           beforeCursor: 'SELECT CASE a = c WHEN c THEN d ',
           afterCursor: ' FROM testTable',
           afterCursor: ' FROM testTable',
-          containsKeywords: ['OR', 'END', '<>'],
+          containsKeywords: ['END', '<>'],
+          containsColRefKeywords: true,
           expectedResult: {
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            colRef: { identifierChain: [{ name: 'd' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -1181,12 +1200,13 @@ define([
           afterCursor: ' ELSE FROM testTable',
           afterCursor: ' ELSE FROM testTable',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: { types: ['T'] },
+            suggestFunctions: { types: ['COLREF'] },
             suggestColumns: {
             suggestColumns: {
-              types: ['T'],
+              types: ['COLREF'],
               table: 'testTable'
               table: 'testTable'
             },
             },
-            suggestValues: { identifierChain: [{ name: 'd' }], table: 'testTable' }
+            suggestValues: true,
+            colRef: { identifierChain: [{ name: 'd' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -1289,9 +1309,11 @@ define([
         assertAutoComplete({
         assertAutoComplete({
           beforeCursor: 'SELECT CASE a = c WHEN c THEN d ELSE e ',
           beforeCursor: 'SELECT CASE a = c WHEN c THEN d ELSE e ',
           afterCursor: ' FROM testTable',
           afterCursor: ' FROM testTable',
-          containsKeywords: ['OR', 'END', '='],
+          containsKeywords: ['END', '='],
+          containsColRefKeywords: true,
           expectedResult: {
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            colRef: { identifierChain: [{ name: 'e' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -1411,9 +1433,11 @@ define([
         assertAutoComplete({
         assertAutoComplete({
           beforeCursor: 'SELECT CASE a WHEN b THEN c ',
           beforeCursor: 'SELECT CASE a WHEN b THEN c ',
           afterCursor: ' WHEN d THEN e END FROM testTable',
           afterCursor: ' WHEN d THEN e END FROM testTable',
-          containsKeywords: ['WHEN', 'AND', '<'],
+          containsKeywords: ['WHEN', '<'],
+          containsColRefKeywords: true,
           expectedResult: {
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            colRef: { identifierChain: [{ name: 'c' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -1422,9 +1446,11 @@ define([
         assertAutoComplete({
         assertAutoComplete({
           beforeCursor: 'SELECT CASE a WHEN b THEN c ',
           beforeCursor: 'SELECT CASE a WHEN b THEN c ',
           afterCursor: ' FROM testTable',
           afterCursor: ' FROM testTable',
-          containsKeywords: ['WHEN', 'AND', '>'],
+          containsKeywords: ['WHEN', '>'],
+          containsColRefKeywords: true,
           expectedResult: {
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            colRef: { identifierChain: [{ name: 'c' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -1449,12 +1475,13 @@ define([
           afterCursor: ' = a FROM testTable',
           afterCursor: ' = a FROM testTable',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: { types: ['T'] },
+            suggestFunctions: { types: ['COLREF'] },
             suggestColumns: {
             suggestColumns: {
-              types: ['T'],
+              types: ['COLREF'],
               table: 'testTable'
               table: 'testTable'
             },
             },
-            suggestValues: { identifierChain :[{ name :'a'}], table: 'testTable' }
+            suggestValues: true,
+            colRef: { identifierChain :[{ name :'a'}], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -1521,12 +1548,13 @@ define([
           afterCursor: ' FROM testTable',
           afterCursor: ' FROM testTable',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: { types: ['T'] },
+            suggestFunctions: { types: ['COLREF'] },
             suggestColumns: {
             suggestColumns: {
-              types: ['T'],
+              types: ['COLREF'],
               table: 'testTable'
               table: 'testTable'
             },
             },
-            suggestValues: { identifierChain: [{ name: 'a' }], table: 'testTable' }
+            suggestValues: true,
+            colRef: { identifierChain: [{ name: 'a' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -1546,9 +1574,11 @@ define([
         assertAutoComplete({
         assertAutoComplete({
           beforeCursor: 'SELECT CASE a = c WHEN c ',
           beforeCursor: 'SELECT CASE a = c WHEN c ',
           afterCursor: ' d FROM testTable',
           afterCursor: ' d FROM testTable',
-          containsKeywords: ['THEN', 'OR', '>'],
+          containsKeywords: ['THEN', '>'],
+          containsColRefKeywords: true,
           expectedResult: {
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            colRef: { identifierChain: [{ name: 'c' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -2320,12 +2350,10 @@ define([
           dialect: 'impala',
           dialect: 'impala',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: { types: ['T'] },
-            suggestValues: {
-              table: 'testTable',
-              identifierChain: [{ name: 'testMap' }, { name: 'key' }]
-            },
-            suggestIdentifiers : [{ name: 't.', type: 'alias' }, { name: 'tm.', type: 'alias' }]
+            suggestFunctions: { types: ['COLREF'] },
+            suggestValues: true,
+            suggestIdentifiers : [{ name: 't.', type: 'alias' }, { name: 'tm.', type: 'alias' }],
+            colRef: { identifierChain: [{ name: 'testMap' }, { name: 'key' }],  table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -2337,12 +2365,10 @@ define([
           dialect: 'impala',
           dialect: 'impala',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: { types: ['T'] },
-            suggestValues: {
-              table: 'testTable',
-              identifierChain: [{ name: 'testMap' }, { name: 'field' }]
-            },
-            suggestIdentifiers : [{ name: 't.', type: 'alias' }, { name: 'm.', type: 'alias' }]
+            suggestFunctions: { types: ['COLREF'] },
+            suggestValues: true,
+            suggestIdentifiers : [{ name: 't.', type: 'alias' }, { name: 'm.', type: 'alias' }],
+            colRef: { identifierChain: [{ name: 'testMap' }, { name: 'field' }], table: 'testTable' }
           }
           }
         });
         });
       })
       })
@@ -2429,12 +2455,10 @@ define([
           afterCursor: '',
           afterCursor: '',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: { types: ['T'] },
-            suggestValues: {
-              table: 'testTable',
-              identifierChain: [{ name: 'id' }]
-            },
-            suggestColumns: { types: ['T'], table: 'testTable' }
+            suggestFunctions: { types: ['COLREF'] },
+            suggestValues: true,
+            suggestColumns: { types: ['COLREF'], table: 'testTable' },
+            colRef: { identifierChain: [{ name: 'id' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -2482,11 +2506,9 @@ define([
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
             suggestFunctions: { types: ['NUMBER'] },
             suggestFunctions: { types: ['NUMBER'] },
-            suggestValues: {
-              table: 'testTable',
-              identifierChain: [{ name: 'id' }]
-            },
-            suggestColumns: { types: ['NUMBER'], table: 'testTable' }
+            suggestValues: true,
+            suggestColumns: { types: ['NUMBER'], table: 'testTable' },
+            colRef: { identifierChain: [{ name: 'id' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -2811,9 +2833,10 @@ define([
           afterCursor: ' > id FROM testTable',
           afterCursor: ' > id FROM testTable',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: { types: ['T'] },
-            suggestColumns: { types: ['T'], table: 'testTable' },
-            suggestValues: { identifierChain: [{ name: 'id' }], table: 'testTable' }
+            suggestFunctions: { types: ['COLREF'] },
+            suggestColumns: { types: ['COLREF'], table: 'testTable' },
+            suggestValues: true,
+            colRef: { identifierChain: [{ name: 'id' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -2824,12 +2847,10 @@ define([
           afterCursor: ' = id',
           afterCursor: ' = id',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: { types: ['T'] },
-            suggestValues: {
-              table: 'testTable',
-              identifierChain: [{ name: 'id' }]
-            },
-            suggestColumns: { types: ['T'], table: 'testTable' }
+            suggestFunctions: { types: ['COLREF'] },
+            suggestValues: true,
+            suggestColumns: { types: ['COLREF'], table: 'testTable' },
+            colRef: { identifierChain: [{ name: 'id' }], table: 'testTable'}
           }
           }
         });
         });
       });
       });
@@ -2840,12 +2861,10 @@ define([
           afterCursor: '',
           afterCursor: '',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: { types: ['T'] },
-            suggestValues: {
-              table: 'testTable',
-              identifierChain: [{ name: 'd' }]
-            },
-            suggestColumns: { types: ['T'], table: 'testTable' }
+            suggestFunctions: { types: ['COLREF'] },
+            suggestValues: true,
+            suggestColumns: { types: ['COLREF'], table: 'testTable' },
+            colRef: { identifierChain: [{ name: 'd' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -2856,12 +2875,10 @@ define([
           afterCursor: '',
           afterCursor: '',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: { types: ['T'] },
-            suggestValues: {
-              table: 'testTable',
-              identifierChain: [{ name: 'd' }]
-            },
-            suggestColumns: { types: ['T'], table: 'testTable' }
+            suggestFunctions: { types: ['COLREF'] },
+            suggestValues: true,
+            suggestColumns: { types: ['COLREF'], table: 'testTable' },
+            colRef: { identifierChain: [{ name: 'd' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -2872,12 +2889,10 @@ define([
           afterCursor: '',
           afterCursor: '',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: { types: ['T'] },
-            suggestValues: {
-              table: 'testTable',
-              identifierChain: [{ name: 'd' }]
-            },
-            suggestColumns: { types: ['T'], table: 'testTable' }
+            suggestFunctions: { types: ['COLREF'] },
+            suggestValues: true,
+            suggestColumns: { types: ['COLREF'], table: 'testTable' },
+            colRef: { identifierChain: [{ name: 'd' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -2888,12 +2903,10 @@ define([
           afterCursor: '',
           afterCursor: '',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: { types: ['T'] },
-            suggestValues: {
-              table: 'testTable',
-              identifierChain: [{ name: 'd' }]
-            },
-            suggestColumns: { types: ['T'], table: 'testTable' }
+            suggestFunctions: { types: ['COLREF'] },
+            suggestValues: true,
+            suggestColumns: { types: ['COLREF'], table: 'testTable' },
+            colRef: { identifierChain: [{ name: 'd' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -2904,12 +2917,10 @@ define([
           afterCursor: '',
           afterCursor: '',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: { types: ['T'] },
-            suggestValues: {
-              table: 'testTable',
-              identifierChain: [{ name: 'd' }]
-            },
-            suggestColumns: { types: ['T'], table: 'testTable' }
+            suggestFunctions: { types: ['COLREF'] },
+            suggestValues: true,
+            suggestColumns: { types: ['COLREF'], table: 'testTable' },
+            colRef: { identifierChain: [{ name: 'd' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -2920,12 +2931,10 @@ define([
           afterCursor: '',
           afterCursor: '',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: { types: ['T']},
-            suggestValues: {
-              table: 'testTable',
-              identifierChain: [{ name: 'd' }]
-            },
-            suggestColumns: { types: ['T'], table: 'testTable' }
+            suggestFunctions: { types: ['COLREF'] },
+            suggestValues: true,
+            suggestColumns: { types: ['COLREF'], table: 'testTable' },
+            colRef: { identifierChain: [{ name: 'd' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -2936,12 +2945,10 @@ define([
           afterCursor: '',
           afterCursor: '',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: { types: ['T'] },
-            suggestValues: {
-              table: 'testTable',
-              identifierChain: [{ name: 'd' }]
-            },
-            suggestColumns: { types: ['T'], table: 'testTable' }
+            suggestFunctions: { types: ['COLREF'] },
+            suggestValues: true,
+            suggestColumns: { types: ['COLREF'], table: 'testTable' },
+            colRef: { identifierChain: [{ name: 'd' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -2952,12 +2959,10 @@ define([
           afterCursor: '',
           afterCursor: '',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: { types: ['T'] },
-            suggestValues: {
-              table: 'testTable',
-              identifierChain: [{ name: 'd' }]
-            },
-            suggestColumns: { types: ['T'], table: 'testTable' }
+            suggestFunctions: { types: ['COLREF'] },
+            suggestValues: true,
+            suggestColumns: { types: ['COLREF'], table: 'testTable' },
+            colRef: { identifierChain: [{ name: 'd' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -3406,9 +3411,11 @@ define([
         assertAutoComplete({
         assertAutoComplete({
           beforeCursor: 'SELECT a ',
           beforeCursor: 'SELECT a ',
           afterCursor: ', b, c FROM testTable',
           afterCursor: ', b, c FROM testTable',
-          containsKeywords: ['AS', '>', 'AND'],
+          containsKeywords: ['AS', '>'],
+          containsColRefKeywords: true,
           expectedResult: {
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            colRef: { identifierChain: [{ name: 'a' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -3430,19 +3437,10 @@ define([
           beforeCursor: 'SELECT * FROM testTable WHERE a ',
           beforeCursor: 'SELECT * FROM testTable WHERE a ',
           afterCursor: '',
           afterCursor: '',
           containsKeywords: ['BETWEEN', 'NOT BETWEEN'],
           containsKeywords: ['BETWEEN', 'NOT BETWEEN'],
+          containsColRefKeywords: true,
           expectedResult: {
           expectedResult: {
-            lowerCase: false
-          }
-        });
-      });
-
-      it('should suggest keywords for "SELECT * FROM testTable WHERE a |"', function() {
-        assertAutoComplete({
-          beforeCursor: 'SELECT * FROM testTable WHERE a ',
-          afterCursor: '',
-          containsKeywords: ['LIKE', 'RLIKE', 'REGEX', 'NOT LIKE'],
-          expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            colRef: { identifierChain: [{ name: 'a'}], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -3464,9 +3462,10 @@ define([
           afterCursor: '',
           afterCursor: '',
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
-            suggestFunctions: {},
-            suggestValues: { table: 'testTable', identifierChain: [{ name: 'a' }] },
-            suggestColumns: { table: 'testTable' }
+            suggestFunctions: { types: ['COLREF'] },
+            suggestValues: true,
+            suggestColumns: { types: ['COLREF'], table: 'testTable' },
+            colRef: { identifierChain: [{ name: 'a' }], table: 'testTable' }
           }
           }
         });
         });
       });
       });
@@ -4178,8 +4177,10 @@ define([
           afterCursor: '',
           afterCursor: '',
           dialect: 'generic',
           dialect: 'generic',
           containsKeywords: ['IN', 'NOT IN'],
           containsKeywords: ['IN', 'NOT IN'],
+          containsColRefKeywords: true,
           expectedResult: {
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            colRef: { identifierChain: [{ name: 'bar' }], table: 'foo' }
           }
           }
         });
         });
       });
       });
@@ -4202,9 +4203,10 @@ define([
           expectedResult: {
           expectedResult: {
             lowerCase: false,
             lowerCase: false,
             suggestKeywords: ['SELECT'],
             suggestKeywords: ['SELECT'],
-            suggestFunctions: {},
-            suggestValues: { identifierChain: [{name: 'bar'}], table: 'foo' },
-            suggestColumns: { table: 'foo' }
+            suggestFunctions: { types: ['COLREF'] },
+            suggestValues: true,
+            suggestColumns: { types: ['COLREF'], table: 'foo' },
+            colRef: { identifierChain: [{ name: 'bar'}], table: 'foo' }
           }
           }
         });
         });
       });
       });
@@ -4216,9 +4218,10 @@ define([
           expectedResult: {
           expectedResult: {
             lowerCase: true,
             lowerCase: true,
             suggestKeywords: ['SELECT'],
             suggestKeywords: ['SELECT'],
-            suggestFunctions: {},
-            suggestValues: { identifierChain: [{name: 'bla'}], table: 'bar' },
-            suggestIdentifiers: [{ name: 'foo.', type: 'table'}, { name: 'bar.', type: 'table'}]
+            suggestFunctions: { types: ['COLREF'] },
+            suggestValues: true,
+            suggestIdentifiers: [{ name: 'foo.', type: 'table'}, { name: 'bar.', type: 'table'}],
+            colRef: { identifierChain: [{name: 'bla'}], table: 'bar' }
           }
           }
         });
         });
       });
       });
@@ -4229,9 +4232,10 @@ define([
           afterCursor: '',
           afterCursor: '',
           expectedResult: {
           expectedResult: {
             lowerCase: true,
             lowerCase: true,
-            suggestFunctions: {},
-            suggestValues: { identifierChain: [{name: 'bla'}], table: 'bar' },
-            suggestIdentifiers: [{ name: 'foo.', type: 'table'}, { name: 'bar.', type: 'table'}]
+            suggestFunctions: { types: ['COLREF'] },
+            suggestValues: true,
+            suggestIdentifiers: [{ name: 'foo.', type: 'table'}, { name: 'bar.', type: 'table'}],
+            colRef:  { identifierChain: [{name: 'bla'}], table: 'bar' }
           }
           }
         });
         });
       });
       });

+ 4 - 3
desktop/core/src/desktop/static/desktop/spec/autocomplete/sqlSpecUpdate.js

@@ -178,13 +178,14 @@ define([
         afterCursor: '',
         afterCursor: '',
         expectedResult: {
         expectedResult: {
           lowerCase: false,
           lowerCase: false,
-          suggestFunctions: { types: ['T'] },
-          suggestValues: {
+          suggestFunctions: { types: ['COLREF'] },
+          suggestValues: true,
+          colRef: {
             database: 'bar',
             database: 'bar',
             table: 'foo',
             table: 'foo',
             identifierChain: [{ name: 'id' }]
             identifierChain: [{ name: 'id' }]
           },
           },
-          suggestColumns : { types: ['T'] , database: 'bar', table: 'foo' }
+          suggestColumns : { types: ['COLREF'] , database: 'bar', table: 'foo' }
         }
         }
       });
       });
     });
     });

+ 13 - 0
desktop/core/src/desktop/static/desktop/spec/autocompleterTestUtils.js

@@ -45,6 +45,19 @@ define([
               delete actualResponse.error;
               delete actualResponse.error;
             }
             }
             var deleteKeywords = false;
             var deleteKeywords = false;
+            if (testDefinition.containsColRefKeywords) {
+              if (typeof actualResponse.suggestColRefKeywords == 'undefined') {
+                return {
+                  pass: false,
+                  message: '\nStatement: ' + testDefinition.beforeCursor + '|' + testDefinition.afterCursor + '\n' +
+                  '  Dialect: ' + testDefinition.dialect + '\n' +
+                  '           No colRef keywords found'
+                }
+              } else {
+                delete actualResponse.suggestColRefKeywords;
+              }
+            }
+
             if (typeof testDefinition.containsKeywords !== 'undefined') {
             if (typeof testDefinition.containsKeywords !== 'undefined') {
               var keywords = actualResponse.suggestKeywords;
               var keywords = actualResponse.suggestKeywords;
               var contains = true;
               var contains = true;

+ 41 - 39
desktop/core/src/desktop/static/desktop/spec/sqlFunctionsSpec.js

@@ -23,8 +23,9 @@ define([
 
 
       expect(completions.length).not.toEqual(0);
       expect(completions.length).not.toEqual(0);
 
 
+      console.log(completions);
       var completionsWithCorrectType = completions.filter(function (completion) {
       var completionsWithCorrectType = completions.filter(function (completion) {
-        return completion.meta === 'BOOLEAN' || completion.meta === 'T';
+        return completion.meta === 'BOOLEAN' || completion.meta === 'T' || completion.meta === 'ARRAY' || completion.meta === 'MAP' || completion.meta === 'STRUCT';
       });
       });
 
 
       expect(completionsWithCorrectType.length).toEqual(completions.length);
       expect(completionsWithCorrectType.length).toEqual(completions.length);
@@ -117,54 +118,55 @@ define([
     });
     });
 
 
     it('should give the expected argument types at a specific position', function () {
     it('should give the expected argument types at a specific position', function () {
-      expect(sqlFunctions.getArgumentTypes('hive', 'cos', 1)).toEqual([{ type:'DECIMAL' }, { type:'DOUBLE' }]);
+      console.log(sqlFunctions.getArgumentTypes('hive', 'cos', 1));
+      expect(sqlFunctions.getArgumentTypes('hive', 'cos', 1)).toEqual(['DECIMAL', 'DOUBLE']);
       expect(sqlFunctions.getArgumentTypes('hive', 'cos', 2)).toEqual([]);
       expect(sqlFunctions.getArgumentTypes('hive', 'cos', 2)).toEqual([]);
-      expect(sqlFunctions.getArgumentTypes('impala', 'cos', 1)).toEqual([{ type:'DOUBLE' }]);
+      expect(sqlFunctions.getArgumentTypes('impala', 'cos', 1)).toEqual(['DOUBLE']);
       expect(sqlFunctions.getArgumentTypes('impala', 'cos', 2)).toEqual([]);
       expect(sqlFunctions.getArgumentTypes('impala', 'cos', 2)).toEqual([]);
 
 
-      expect(sqlFunctions.getArgumentTypes('hive', 'greatest', 1)).toEqual([{ type:'T', multiple: true }]);
-      expect(sqlFunctions.getArgumentTypes('hive', 'greatest', 200)).toEqual([{ type:'T', multiple: true }]);
-      expect(sqlFunctions.getArgumentTypes('impala', 'greatest', 1)).toEqual([{ type:'T', multiple: true }]);
-      expect(sqlFunctions.getArgumentTypes('impala', 'greatest', 200)).toEqual([{ type:'T', multiple: true }]);
-
-      expect(sqlFunctions.getArgumentTypes('hive', 'strleft', 1)).toEqual([{ type:'T' }]);
-      expect(sqlFunctions.getArgumentTypes('hive', 'strleft', 2)).toEqual([{ type:'T' }]);
-      expect(sqlFunctions.getArgumentTypes('hive', 'strleft', 3)).toEqual([{ type:'T' }]);
-      expect(sqlFunctions.getArgumentTypes('hive', 'strleft', 200)).toEqual([{ type:'T' }]);
-      expect(sqlFunctions.getArgumentTypes('impala', 'strleft', 1)).toEqual([{ type:'STRING' }]);
-      expect(sqlFunctions.getArgumentTypes('impala', 'strleft', 2)).toEqual([{ type:'INT' }]);
+      expect(sqlFunctions.getArgumentTypes('hive', 'greatest', 1)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('hive', 'greatest', 200)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('impala', 'greatest', 1)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('impala', 'greatest', 200)).toEqual(['T']);
+
+      expect(sqlFunctions.getArgumentTypes('hive', 'strleft', 1)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('hive', 'strleft', 2)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('hive', 'strleft', 3)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('hive', 'strleft', 200)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('impala', 'strleft', 1)).toEqual(['STRING']);
+      expect(sqlFunctions.getArgumentTypes('impala', 'strleft', 2)).toEqual(['INT']);
       expect(sqlFunctions.getArgumentTypes('impala', 'strleft', 3)).toEqual([]);
       expect(sqlFunctions.getArgumentTypes('impala', 'strleft', 3)).toEqual([]);
       expect(sqlFunctions.getArgumentTypes('impala', 'strleft', 200)).toEqual([]);
       expect(sqlFunctions.getArgumentTypes('impala', 'strleft', 200)).toEqual([]);
 
 
-      expect(sqlFunctions.getArgumentTypes('hive', 'substring_index', 1)).toEqual([{ type:'STRING' }]);
-      expect(sqlFunctions.getArgumentTypes('hive', 'substring_index', 2)).toEqual([{ type:'STRING' }]);
-      expect(sqlFunctions.getArgumentTypes('hive', 'substring_index', 3)).toEqual([{ type:'INT' }]);
+      expect(sqlFunctions.getArgumentTypes('hive', 'substring_index', 1)).toEqual(['STRING']);
+      expect(sqlFunctions.getArgumentTypes('hive', 'substring_index', 2)).toEqual(['STRING']);
+      expect(sqlFunctions.getArgumentTypes('hive', 'substring_index', 3)).toEqual(['INT']);
       expect(sqlFunctions.getArgumentTypes('hive', 'substring_index', 200)).toEqual([]);
       expect(sqlFunctions.getArgumentTypes('hive', 'substring_index', 200)).toEqual([]);
-      expect(sqlFunctions.getArgumentTypes('impala', 'substring_index', 1)).toEqual([{ type:'T' }]);
-      expect(sqlFunctions.getArgumentTypes('impala', 'substring_index', 2)).toEqual([{ type:'T' }]);
-      expect(sqlFunctions.getArgumentTypes('impala', 'substring_index', 3)).toEqual([{ type:'T' }]);
-      expect(sqlFunctions.getArgumentTypes('impala', 'substring_index', 200)).toEqual([{ type:'T' }]);
-
-      expect(sqlFunctions.getArgumentTypes('hive', 'weeks_add', 1)).toEqual([{ type:'T' }]);
-      expect(sqlFunctions.getArgumentTypes('hive', 'weeks_add', 2)).toEqual([{ type:'T' }]);
-      expect(sqlFunctions.getArgumentTypes('hive', 'weeks_add', 3)).toEqual([{ type:'T' }]);
-      expect(sqlFunctions.getArgumentTypes('hive', 'weeks_add', 200)).toEqual([{ type:'T' }]);
-      expect(sqlFunctions.getArgumentTypes('impala', 'weeks_add', 1)).toEqual([{ type:'TIMESTAMP' }]);
-      expect(sqlFunctions.getArgumentTypes('impala', 'weeks_add', 2)).toEqual([{ type:'BIGINT'}, { type:'INT' }]);
+      expect(sqlFunctions.getArgumentTypes('impala', 'substring_index', 1)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('impala', 'substring_index', 2)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('impala', 'substring_index', 3)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('impala', 'substring_index', 200)).toEqual(['T']);
+
+      expect(sqlFunctions.getArgumentTypes('hive', 'weeks_add', 1)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('hive', 'weeks_add', 2)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('hive', 'weeks_add', 3)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('hive', 'weeks_add', 200)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('impala', 'weeks_add', 1)).toEqual(['TIMESTAMP']);
+      expect(sqlFunctions.getArgumentTypes('impala', 'weeks_add', 2)).toEqual(['BIGINT', 'INT']);
       expect(sqlFunctions.getArgumentTypes('impala', 'weeks_add', 3)).toEqual([]);
       expect(sqlFunctions.getArgumentTypes('impala', 'weeks_add', 3)).toEqual([]);
       expect(sqlFunctions.getArgumentTypes('impala', 'weeks_add', 200)).toEqual([]);
       expect(sqlFunctions.getArgumentTypes('impala', 'weeks_add', 200)).toEqual([]);
 
 
-      expect(sqlFunctions.getArgumentTypes('hive', 'reflect', 1)).toEqual([{ type:'STRING' }]);
-      expect(sqlFunctions.getArgumentTypes('hive', 'reflect', 2)).toEqual([{ type:'STRING' }]);
-      expect(sqlFunctions.getArgumentTypes('hive', 'reflect', 3)).toEqual([{ type:'T', multiple: true, optional: true }]);
-      expect(sqlFunctions.getArgumentTypes('hive', 'reflect', 200)).toEqual([{ type:'T', multiple: true, optional: true }]);
-      expect(sqlFunctions.getArgumentTypes('impala', 'reflect', 1)).toEqual([{ type:'T' }]);
-      expect(sqlFunctions.getArgumentTypes('impala', 'reflect', 200)).toEqual([{ type:'T' }]);
-
-      expect(sqlFunctions.getArgumentTypes('hive', 'blabla', 2)).toEqual([{ type:'T' }]);
-      expect(sqlFunctions.getArgumentTypes('hive', 'blabla', 200)).toEqual([{ type:'T' }]);
-      expect(sqlFunctions.getArgumentTypes('impala', 'blabla', 2)).toEqual([{ type:'T' }]);
-      expect(sqlFunctions.getArgumentTypes('impala', 'blabla', 200)).toEqual([{ type:'T' }]);
+      expect(sqlFunctions.getArgumentTypes('hive', 'reflect', 1)).toEqual(['STRING']);
+      expect(sqlFunctions.getArgumentTypes('hive', 'reflect', 2)).toEqual(['STRING']);
+      expect(sqlFunctions.getArgumentTypes('hive', 'reflect', 3)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('hive', 'reflect', 200)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('impala', 'reflect', 1)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('impala', 'reflect', 200)).toEqual(['T']);
+
+      expect(sqlFunctions.getArgumentTypes('hive', 'blabla', 2)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('hive', 'blabla', 200)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('impala', 'blabla', 2)).toEqual(['T']);
+      expect(sqlFunctions.getArgumentTypes('impala', 'blabla', 200)).toEqual(['T']);
     });
     });
   });
   });
 });
 });