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

HUE-5344 [editor] The autocomplete parser should suggest complete order by, group by and filter

Johan Ahlen пре 9 година
родитељ
комит
781eef2

+ 57 - 4
desktop/core/src/desktop/static/desktop/js/autocomplete/jison/sql_main.jison

@@ -1435,6 +1435,16 @@ TableExpression_EDIT
        keywords = keywords.concat(createWeightedKeywords($3.suggestKeywords, 2));
      }
 
+     if ($3.suggestFilters) {
+       suggestFilters($3.suggestFilters);
+     }
+     if ($3.suggestGroupBys) {
+       suggestGroupBys($3.suggestGroupBys);
+     }
+     if ($3.suggestOrderBys) {
+       suggestOrderBys($3.suggestOrderBys);
+     }
+
      if ($3.empty) {
        keywords.push({ value: 'UNION', weight: 2.11 });
      }
@@ -1525,8 +1535,21 @@ OptionalSelectConditions
        [$1, $2, $3, $4, $5, $6, $6, $7, $8],
        [{ value: 'WHERE', weight: 9 }, { value: 'GROUP BY', weight: 8 }, { value: 'HAVING', weight: 7 }, { value: 'WINDOW', weight: 6 }, { value: 'ORDER BY', weight: 5 }, [{ value: 'CLUSTER BY', weight: 4 }, { value: 'DISTRIBUTE BY', weight: 4 }], { value: 'SORT BY', weight: 4 }, { value: 'LIMIT', weight: 3 }, { value: 'OFFSET', weight: 2 }],
        [true, true, true, isHive(), true, isHive(), isHive() && !$5, true, isImpala()]);
+
      if (keywords.length > 0) {
-       $$ = { suggestKeywords: keywords, empty: !$1 && !$2 && !$3 && !$4 && !$5 && !$6 && !$7 && !$8 }
+       $$ = { suggestKeywords: keywords, empty: !$1 && !$2 && !$3 && !$4 && !$5 && !$6 && !$7 && !$8 };
+     } else {
+       $$ = {};
+     }
+
+     if (!$1 && !$2 && !$3 && !$4 && !$5 && !$6 && !$7 && !$8) {
+       $$.suggestFilters = { prefix: 'WHERE', tablePrimaries: parser.yy.latestTablePrimaries.concat() };
+     }
+     if (!$2 && !$3 && !$4 && !$5 && !$6 && !$7 && !$8) {
+       $$.suggestGroupBys = { prefix: 'GROUP BY', tablePrimaries: parser.yy.latestTablePrimaries.concat() };
+     }
+     if (!$5 && !$6 && !$7 && !$8) {
+       $$.suggestOrderBys = { prefix: 'ORDER BY', tablePrimaries: parser.yy.latestTablePrimaries.concat() };
      }
    }
  ;
@@ -1537,6 +1560,9 @@ OptionalSelectConditions_EDIT
      if (parser.yy.result.suggestColumns) {
        parser.yy.result.suggestColumns.source = 'where';
      }
+     if ($1.emptyFilter) {
+       suggestFilters({ tablePrimaries: parser.yy.latestTablePrimaries.concat() });
+     }
    }
  | OptionalWhereClause GroupByClause_EDIT OptionalHavingClause OptionalWindowClause OptionalOrderByClause OptionalClusterOrDistributeBy OptionalLimitClause OptionalOffsetClause
    {
@@ -1572,6 +1598,12 @@ OptionalSelectConditions_EDIT
      if ($1.columnReference) {
        $$.columnReference = $1.columnReference;
      }
+     if (!$3) {
+       suggestGroupBys({ prefix: 'GROUP BY', tablePrimaries: parser.yy.latestTablePrimaries.concat() });
+     }
+     if (!$3 && !$4 && !$5 && !$6) {
+       suggestOrderBys({ prefix: 'ORDER BY', tablePrimaries: parser.yy.latestTablePrimaries.concat() });
+     }
    }
  | OptionalWhereClause GroupByClause 'CURSOR' OptionalHavingClause OptionalWindowClause OptionalOrderByClause OptionalClusterOrDistributeBy OptionalLimitClause OptionalOffsetClause
    {
@@ -1587,6 +1619,9 @@ OptionalSelectConditions_EDIT
        $$.columnReference = $2.columnReference;
      }
      $$.cursorAtEnd = !$4 && !$5 && !$6 && !$7 && !$8 && !$9;
+     if (!$4 && !$5 && !$6) {
+       suggestOrderBys({ prefix: 'ORDER BY', tablePrimaries: parser.yy.latestTablePrimaries.concat() });
+     }
    }
  | OptionalWhereClause OptionalGroupByClause HavingClause 'CURSOR' OptionalWindowClause OptionalOrderByClause OptionalClusterOrDistributeBy OptionalLimitClause OptionalOffsetClause
    {
@@ -1595,11 +1630,18 @@ OptionalSelectConditions_EDIT
        [{ value: 'WINDOW', weight: 6 }, { value: 'ORDER BY', weight: 5 }, [{ value: 'CLUSTER BY', weight: 4 }, { value: 'DISTRIBUTE BY', weight: 4 }], { value: 'SORT BY', weight: 4 }, { value: 'LIMIT', weight: 3 }, { value: 'OFFSET', weight: 2 }],
        [isHive(), true, isHive(), isHive() && !$6, true, isImpala()]);
      $$ = { suggestKeywords: keywords, cursorAtEnd: !$5 && !$6 && !$7 && !$8 && !$9 };
+     if (!$5 && !$6) {
+       suggestOrderBys({ prefix: 'ORDER BY', tablePrimaries: parser.yy.latestTablePrimaries.concat() });
+     }
    }
  | OptionalWhereClause OptionalGroupByClause OptionalHavingClause WindowClause 'CURSOR' OptionalOrderByClause OptionalClusterOrDistributeBy OptionalLimitClause OptionalOffsetClause
    {
      var keywords = getKeywordsForOptionalsLR([$6, $7, $8, $9], [{ value: 'ORDER BY', weight: 5 }, [{ value: 'CLUSTER BY', weight: 4 }, { value: 'DISTRIBUTE BY', weight: 4 }, { value: 'SORT BY', weight: 4 }], { value: 'LIMIT', weight: 3 }, { value: 'OFFSET', weight: 2 }], [true, isHive(), true, isImpala()]);
      $$ = { suggestKeywords: keywords, cursorAtEnd: !$6 && !$7 && !$8 && !$9 };
+     if (!$6) {
+       suggestOrderBys({ prefix: 'ORDER BY', tablePrimaries: parser.yy.latestTablePrimaries.concat() });
+     }
+
    }
  | OptionalWhereClause OptionalGroupByClause OptionalHavingClause OptionalWindowClause OrderByClause 'CURSOR' OptionalClusterOrDistributeBy OptionalLimitClause OptionalOffsetClause
    {
@@ -1640,6 +1682,7 @@ WhereClause_EDIT
      suggestFunctions();
      suggestColumns();
      suggestKeywords(['EXISTS', 'NOT EXISTS']);
+     $$ = { emptyFilter: true }
    }
  ;
 
@@ -1674,10 +1717,12 @@ GroupByClause_EDIT
    {
      valueExpressionSuggest();
      suggestSelectListAliases();
+     suggestGroupBys({ tablePrimaries: parser.yy.latestTablePrimaries.concat() });
    }
  | AnyGroup 'CURSOR'
    {
      suggestKeywords(['BY']);
+     suggestGroupBys({ prefix: 'BY', tablePrimaries: parser.yy.latestTablePrimaries.concat() });
    }
  | AnyGroup 'BY' GroupByColumnList OptionalHiveGroupingSetsCubeOrRollup_EDIT
  ;
@@ -1778,9 +1823,15 @@ OrderByClause
 
 OrderByClause_EDIT
  : 'ORDER' 'BY' OrderByColumnList_EDIT
+   {
+     if ($3.emptyOrderBy) {
+       suggestOrderBys({ tablePrimaries: parser.yy.latestTablePrimaries.concat() });
+     }
+   }
  | 'ORDER' 'CURSOR'
    {
      suggestKeywords(['BY']);
+     suggestOrderBys({ prefix: 'BY', tablePrimaries: parser.yy.latestTablePrimaries.concat() });
    }
  ;
 
@@ -1793,13 +1844,14 @@ OrderByColumnList_EDIT
  : OrderByIdentifier_EDIT
  | 'CURSOR' OrderByIdentifier
    {
+     $$ = { emptyOrderBy: false }
      valueExpressionSuggest();
      suggestAnalyticFunctions();
      suggestSelectListAliases();
    }
- | OrderByColumnList ',' OrderByIdentifier_EDIT
- | OrderByColumnList ',' OrderByIdentifier_EDIT ','
- | OrderByColumnList ',' OrderByIdentifier_EDIT ',' OrderByColumnList
+ | OrderByColumnList ',' OrderByIdentifier_EDIT                        -> { emptyOrderBy: false }
+ | OrderByColumnList ',' OrderByIdentifier_EDIT ','                    -> { emptyOrderBy: false }
+ | OrderByColumnList ',' OrderByIdentifier_EDIT ',' OrderByColumnList  -> { emptyOrderBy: false }
  ;
 
 OrderByIdentifier
@@ -1814,6 +1866,7 @@ OrderByIdentifier_EDIT
  | ValueExpression OptionalAscOrDesc OptionalImpalaNullsFirstOrLast_EDIT
  | AnyCursor OptionalAscOrDesc OptionalImpalaNullsFirstOrLast
    {
+     $$ = { emptyOrderBy: true }
      valueExpressionSuggest();
      suggestAnalyticFunctions();
      suggestSelectListAliases();

+ 121 - 19
desktop/core/src/desktop/static/desktop/js/autocomplete/sql.js

@@ -660,6 +660,16 @@ case 792:
        keywords = keywords.concat(createWeightedKeywords($$[$0-1].suggestKeywords, 2));
      }
 
+     if ($$[$0-1].suggestFilters) {
+       suggestFilters($$[$0-1].suggestFilters);
+     }
+     if ($$[$0-1].suggestGroupBys) {
+       suggestGroupBys($$[$0-1].suggestGroupBys);
+     }
+     if ($$[$0-1].suggestOrderBys) {
+       suggestOrderBys($$[$0-1].suggestOrderBys);
+     }
+
      if ($$[$0-1].empty) {
        keywords.push({ value: 'UNION', weight: 2.11 });
      }
@@ -739,8 +749,21 @@ case 801:
        [$$[$0-7], $$[$0-6], $$[$0-5], $$[$0-4], $$[$0-3], $$[$0-2], $$[$0-2], $$[$0-1], $$[$0]],
        [{ value: 'WHERE', weight: 9 }, { value: 'GROUP BY', weight: 8 }, { value: 'HAVING', weight: 7 }, { value: 'WINDOW', weight: 6 }, { value: 'ORDER BY', weight: 5 }, [{ value: 'CLUSTER BY', weight: 4 }, { value: 'DISTRIBUTE BY', weight: 4 }], { value: 'SORT BY', weight: 4 }, { value: 'LIMIT', weight: 3 }, { value: 'OFFSET', weight: 2 }],
        [true, true, true, isHive(), true, isHive(), isHive() && !$$[$0-3], true, isImpala()]);
+
      if (keywords.length > 0) {
-       this.$ = { suggestKeywords: keywords, empty: !$$[$0-7] && !$$[$0-6] && !$$[$0-5] && !$$[$0-4] && !$$[$0-3] && !$$[$0-2] && !$$[$0-1] && !$$[$0] }
+       this.$ = { suggestKeywords: keywords, empty: !$$[$0-7] && !$$[$0-6] && !$$[$0-5] && !$$[$0-4] && !$$[$0-3] && !$$[$0-2] && !$$[$0-1] && !$$[$0] };
+     } else {
+       this.$ = {};
+     }
+
+     if (!$$[$0-7] && !$$[$0-6] && !$$[$0-5] && !$$[$0-4] && !$$[$0-3] && !$$[$0-2] && !$$[$0-1] && !$$[$0]) {
+       this.$.suggestFilters = { prefix: 'WHERE', tablePrimaries: parser.yy.latestTablePrimaries.concat() };
+     }
+     if (!$$[$0-6] && !$$[$0-5] && !$$[$0-4] && !$$[$0-3] && !$$[$0-2] && !$$[$0-1] && !$$[$0]) {
+       this.$.suggestGroupBys = { prefix: 'GROUP BY', tablePrimaries: parser.yy.latestTablePrimaries.concat() };
+     }
+     if (!$$[$0-3] && !$$[$0-2] && !$$[$0-1] && !$$[$0]) {
+       this.$.suggestOrderBys = { prefix: 'ORDER BY', tablePrimaries: parser.yy.latestTablePrimaries.concat() };
      }
    
 break;
@@ -749,6 +772,9 @@ case 802:
      if (parser.yy.result.suggestColumns) {
        parser.yy.result.suggestColumns.source = 'where';
      }
+     if ($$[$0-7].emptyFilter) {
+       suggestFilters({ tablePrimaries: parser.yy.latestTablePrimaries.concat() });
+     }
    
 break;
 case 803:
@@ -779,6 +805,12 @@ case 810:
      if ($$[$0-8].columnReference) {
        this.$.columnReference = $$[$0-8].columnReference;
      }
+     if (!$$[$0-6]) {
+       suggestGroupBys({ prefix: 'GROUP BY', tablePrimaries: parser.yy.latestTablePrimaries.concat() });
+     }
+     if (!$$[$0-6] && !$$[$0-5] && !$$[$0-4] && !$$[$0-3]) {
+       suggestOrderBys({ prefix: 'ORDER BY', tablePrimaries: parser.yy.latestTablePrimaries.concat() });
+     }
    
 break;
 case 811:
@@ -795,6 +827,9 @@ case 811:
        this.$.columnReference = $$[$0-7].columnReference;
      }
      this.$.cursorAtEnd = !$$[$0-5] && !$$[$0-4] && !$$[$0-3] && !$$[$0-2] && !$$[$0-1] && !$$[$0];
+     if (!$$[$0-5] && !$$[$0-4] && !$$[$0-3]) {
+       suggestOrderBys({ prefix: 'ORDER BY', tablePrimaries: parser.yy.latestTablePrimaries.concat() });
+     }
    
 break;
 case 812:
@@ -804,12 +839,19 @@ case 812:
        [{ value: 'WINDOW', weight: 6 }, { value: 'ORDER BY', weight: 5 }, [{ value: 'CLUSTER BY', weight: 4 }, { value: 'DISTRIBUTE BY', weight: 4 }], { value: 'SORT BY', weight: 4 }, { value: 'LIMIT', weight: 3 }, { value: 'OFFSET', weight: 2 }],
        [isHive(), true, isHive(), isHive() && !$$[$0-3], true, isImpala()]);
      this.$ = { suggestKeywords: keywords, cursorAtEnd: !$$[$0-4] && !$$[$0-3] && !$$[$0-2] && !$$[$0-1] && !$$[$0] };
+     if (!$$[$0-4] && !$$[$0-3]) {
+       suggestOrderBys({ prefix: 'ORDER BY', tablePrimaries: parser.yy.latestTablePrimaries.concat() });
+     }
    
 break;
 case 813:
 
      var keywords = getKeywordsForOptionalsLR([$$[$0-3], $$[$0-2], $$[$0-1], $$[$0]], [{ value: 'ORDER BY', weight: 5 }, [{ value: 'CLUSTER BY', weight: 4 }, { value: 'DISTRIBUTE BY', weight: 4 }, { value: 'SORT BY', weight: 4 }], { value: 'LIMIT', weight: 3 }, { value: 'OFFSET', weight: 2 }], [true, isHive(), true, isImpala()]);
      this.$ = { suggestKeywords: keywords, cursorAtEnd: !$$[$0-3] && !$$[$0-2] && !$$[$0-1] && !$$[$0] };
+     if (!$$[$0-3]) {
+       suggestOrderBys({ prefix: 'ORDER BY', tablePrimaries: parser.yy.latestTablePrimaries.concat() });
+     }
+
    
 break;
 case 814:
@@ -889,6 +931,7 @@ case 852:
      suggestFunctions();
      suggestColumns();
      suggestKeywords(['EXISTS', 'NOT EXISTS']);
+     this.$ = { emptyFilter: true }
    
 break;
 case 855:
@@ -915,11 +958,13 @@ case 857:
 
      valueExpressionSuggest();
      suggestSelectListAliases();
+     suggestGroupBys({ tablePrimaries: parser.yy.latestTablePrimaries.concat() });
    
 break;
-case 858: case 896: case 1325: case 1600: case 1949: case 1950: case 1956: case 1965: case 1983: case 2037: case 2038: case 2043: case 2048: case 2052:
+case 858:
 
      suggestKeywords(['BY']);
+     suggestGroupBys({ prefix: 'BY', tablePrimaries: parser.yy.latestTablePrimaries.concat() });
    
 break;
 case 865:
@@ -934,15 +979,40 @@ case 867:
      suggestKeywords(['SETS']);
    
 break;
-case 900: case 907:
+case 895:
+
+     if ($$[$0].emptyOrderBy) {
+       suggestOrderBys({ tablePrimaries: parser.yy.latestTablePrimaries.concat() });
+     }
+   
+break;
+case 896:
+
+     suggestKeywords(['BY']);
+     suggestOrderBys({ prefix: 'BY', tablePrimaries: parser.yy.latestTablePrimaries.concat() });
+   
+break;
+case 900:
 
+     this.$ = { emptyOrderBy: false }
      valueExpressionSuggest();
      suggestAnalyticFunctions();
      suggestSelectListAliases();
    
+break;
+case 901: case 902: case 903:
+this.$ = { emptyOrderBy: false };
 break;
 case 904:
 this.$ = mergeSuggestKeywords($$[$0-1], $$[$0]);
+break;
+case 907:
+
+     this.$ = { emptyOrderBy: true }
+     valueExpressionSuggest();
+     suggestAnalyticFunctions();
+     suggestSelectListAliases();
+   
 break;
 case 908:
 
@@ -1898,6 +1968,11 @@ case 1321:
         suggestValueExpressionKeywords($$[$0-3]);
       }
     
+break;
+case 1325: case 1600: case 1949: case 1950: case 1956: case 1965: case 1983: case 2037: case 2038: case 2043: case 2048: case 2052:
+
+     suggestKeywords(['BY']);
+   
 break;
 case 1330:
 
@@ -4167,16 +4242,18 @@ _handle_error:
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+var SIMPLE_TABLE_REF_SUGGESTIONS = ['suggestJoinConditions', 'suggestFilters', 'suggestGroupBys', 'suggestOrderBys'];
+
 var prepareNewStatement = function () {
   linkTablePrimaries();
   commitLocations();
 
   delete parser.yy.lateralViews;
-  delete parser.yy.latestTablePrimaries;
   delete parser.yy.latestCommonTableExpressions;
   delete parser.yy.correlatedSubQuery;
   parser.yy.subQueries = [];
   parser.yy.selectListAliases = [];
+  parser.yy.latestTablePrimaries = [];
 
   parser.parseError = function (message, error) {
     parser.yy.errors.push(error);
@@ -4533,6 +4610,13 @@ var commitLocations = function () {
 
 var prioritizeSuggestions = function () {
   parser.yy.result.lowerCase = parser.yy.lowerCase || false;
+
+  SIMPLE_TABLE_REF_SUGGESTIONS.forEach(function (suggestionType) {
+    if (typeof parser.yy.result[suggestionType] !== 'undefined' && parser.yy.result[suggestionType].tables.length === 0) {
+      delete parser.yy.result[suggestionType];
+    }
+  });
+
   if (typeof parser.yy.result.colRef !== 'undefined') {
     if (!parser.yy.result.colRef.linked || typeof parser.yy.result.colRef.identifierChain === 'undefined' || parser.yy.result.colRef.identifierChain.length === 0) {
       delete parser.yy.result.colRef;
@@ -4940,19 +5024,21 @@ var linkTablePrimaries = function () {
     return;
   }
 
-  var tablePrimaries = parser.yy.latestTablePrimaries;
-  if (typeof parser.yy.result.suggestJoinConditions !== 'undefined' && parser.yy.result.suggestJoinConditions.tablePrimaries && !parser.yy.result.suggestJoinConditions.linked) {
-    parser.yy.result.suggestJoinConditions.tables = [];
-    parser.yy.result.suggestJoinConditions.tablePrimaries.forEach(function (tablePrimary) {
-      if (!tablePrimary.subQueryAlias) {
-        parser.yy.result.suggestJoinConditions.tables.push(tablePrimary.alias ? { identifierChain: tablePrimary.identifierChain.concat(), alias: tablePrimary.alias } : { identifierChain: tablePrimary.identifierChain.concat() });
-      }
-    });
-    delete parser.yy.result.suggestJoinConditions.tablePrimaries;
-    parser.yy.result.suggestJoinConditions.linked = true;
-  }
+  SIMPLE_TABLE_REF_SUGGESTIONS.forEach(function (suggestionType) {
+    if (typeof parser.yy.result[suggestionType] !== 'undefined' && parser.yy.result[suggestionType].tablePrimaries && !parser.yy.result[suggestionType].linked) {
+      parser.yy.result[suggestionType].tables = [];
+      parser.yy.result[suggestionType].tablePrimaries.forEach(function (tablePrimary) {
+        if (!tablePrimary.subQueryAlias) {
+          parser.yy.result[suggestionType].tables.push(tablePrimary.alias ? { identifierChain: tablePrimary.identifierChain.concat(), alias: tablePrimary.alias } : { identifierChain: tablePrimary.identifierChain.concat() });
+        }
+      });
+      delete parser.yy.result[suggestionType].tablePrimaries;
+      parser.yy.result[suggestionType].linked = true;
+    }
+  });
+
   if (typeof parser.yy.result.suggestColumns !== 'undefined' && !parser.yy.result.suggestColumns.linked) {
-    tablePrimaries = filterTablePrimariesForOwner(parser.yy.result.suggestColumns.owner);
+    var tablePrimaries = filterTablePrimariesForOwner(parser.yy.result.suggestColumns.owner);
     if (!parser.yy.result.suggestColumns.tables) {
       parser.yy.result.suggestColumns.tables = [];
     }
@@ -5195,6 +5281,18 @@ var suggestColumns = function (details) {
   parser.yy.result.suggestColumns = details;
 };
 
+var suggestGroupBys = function (details) {
+  parser.yy.result.suggestGroupBys = details || {};
+};
+
+var suggestOrderBys = function (details) {
+  parser.yy.result.suggestOrderBys = details || {};
+};
+
+var suggestFilters = function (details) {
+  parser.yy.result.suggestFilters = details || {};
+};
+
 var suggestKeyValues = function (details) {
   parser.yy.result.suggestKeyValues = details || {};
 };
@@ -5449,9 +5547,13 @@ parser.parseSql = function (beforeCursor, afterCursor, dialect, debug) {
   if (typeof parser.yy.result.suggestColumns !== 'undefined') {
     delete parser.yy.result.suggestColumns.linked;
   }
-  if (typeof parser.yy.result.suggestJoinConditions !== 'undefined') {
-    delete parser.yy.result.suggestJoinConditions.linked;
-  }
+
+  SIMPLE_TABLE_REF_SUGGESTIONS.forEach(function (suggestionType) {
+    if (typeof parser.yy.result[suggestionType] !== 'undefined') {
+      delete parser.yy.result[suggestionType].linked;
+    }
+  });
+  
   if (typeof parser.yy.result.colRef !== 'undefined') {
     delete parser.yy.result.colRef.linked;
   }

+ 43 - 16
desktop/core/src/desktop/static/desktop/js/autocomplete/sql_support.js

@@ -14,16 +14,18 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+var SIMPLE_TABLE_REF_SUGGESTIONS = ['suggestJoinConditions', 'suggestFilters', 'suggestGroupBys', 'suggestOrderBys'];
+
 var prepareNewStatement = function () {
   linkTablePrimaries();
   commitLocations();
 
   delete parser.yy.lateralViews;
-  delete parser.yy.latestTablePrimaries;
   delete parser.yy.latestCommonTableExpressions;
   delete parser.yy.correlatedSubQuery;
   parser.yy.subQueries = [];
   parser.yy.selectListAliases = [];
+  parser.yy.latestTablePrimaries = [];
 
   parser.parseError = function (message, error) {
     parser.yy.errors.push(error);
@@ -380,6 +382,13 @@ var commitLocations = function () {
 
 var prioritizeSuggestions = function () {
   parser.yy.result.lowerCase = parser.yy.lowerCase || false;
+
+  SIMPLE_TABLE_REF_SUGGESTIONS.forEach(function (suggestionType) {
+    if (typeof parser.yy.result[suggestionType] !== 'undefined' && parser.yy.result[suggestionType].tables.length === 0) {
+      delete parser.yy.result[suggestionType];
+    }
+  });
+
   if (typeof parser.yy.result.colRef !== 'undefined') {
     if (!parser.yy.result.colRef.linked || typeof parser.yy.result.colRef.identifierChain === 'undefined' || parser.yy.result.colRef.identifierChain.length === 0) {
       delete parser.yy.result.colRef;
@@ -787,19 +796,21 @@ var linkTablePrimaries = function () {
     return;
   }
 
-  var tablePrimaries = parser.yy.latestTablePrimaries;
-  if (typeof parser.yy.result.suggestJoinConditions !== 'undefined' && parser.yy.result.suggestJoinConditions.tablePrimaries && !parser.yy.result.suggestJoinConditions.linked) {
-    parser.yy.result.suggestJoinConditions.tables = [];
-    parser.yy.result.suggestJoinConditions.tablePrimaries.forEach(function (tablePrimary) {
-      if (!tablePrimary.subQueryAlias) {
-        parser.yy.result.suggestJoinConditions.tables.push(tablePrimary.alias ? { identifierChain: tablePrimary.identifierChain.concat(), alias: tablePrimary.alias } : { identifierChain: tablePrimary.identifierChain.concat() });
-      }
-    });
-    delete parser.yy.result.suggestJoinConditions.tablePrimaries;
-    parser.yy.result.suggestJoinConditions.linked = true;
-  }
+  SIMPLE_TABLE_REF_SUGGESTIONS.forEach(function (suggestionType) {
+    if (typeof parser.yy.result[suggestionType] !== 'undefined' && parser.yy.result[suggestionType].tablePrimaries && !parser.yy.result[suggestionType].linked) {
+      parser.yy.result[suggestionType].tables = [];
+      parser.yy.result[suggestionType].tablePrimaries.forEach(function (tablePrimary) {
+        if (!tablePrimary.subQueryAlias) {
+          parser.yy.result[suggestionType].tables.push(tablePrimary.alias ? { identifierChain: tablePrimary.identifierChain.concat(), alias: tablePrimary.alias } : { identifierChain: tablePrimary.identifierChain.concat() });
+        }
+      });
+      delete parser.yy.result[suggestionType].tablePrimaries;
+      parser.yy.result[suggestionType].linked = true;
+    }
+  });
+
   if (typeof parser.yy.result.suggestColumns !== 'undefined' && !parser.yy.result.suggestColumns.linked) {
-    tablePrimaries = filterTablePrimariesForOwner(parser.yy.result.suggestColumns.owner);
+    var tablePrimaries = filterTablePrimariesForOwner(parser.yy.result.suggestColumns.owner);
     if (!parser.yy.result.suggestColumns.tables) {
       parser.yy.result.suggestColumns.tables = [];
     }
@@ -1042,6 +1053,18 @@ var suggestColumns = function (details) {
   parser.yy.result.suggestColumns = details;
 };
 
+var suggestGroupBys = function (details) {
+  parser.yy.result.suggestGroupBys = details || {};
+};
+
+var suggestOrderBys = function (details) {
+  parser.yy.result.suggestOrderBys = details || {};
+};
+
+var suggestFilters = function (details) {
+  parser.yy.result.suggestFilters = details || {};
+};
+
 var suggestKeyValues = function (details) {
   parser.yy.result.suggestKeyValues = details || {};
 };
@@ -1296,9 +1319,13 @@ parser.parseSql = function (beforeCursor, afterCursor, dialect, debug) {
   if (typeof parser.yy.result.suggestColumns !== 'undefined') {
     delete parser.yy.result.suggestColumns.linked;
   }
-  if (typeof parser.yy.result.suggestJoinConditions !== 'undefined') {
-    delete parser.yy.result.suggestJoinConditions.linked;
-  }
+
+  SIMPLE_TABLE_REF_SUGGESTIONS.forEach(function (suggestionType) {
+    if (typeof parser.yy.result[suggestionType] !== 'undefined') {
+      delete parser.yy.result[suggestionType].linked;
+    }
+  });
+
   if (typeof parser.yy.result.colRef !== 'undefined') {
     delete parser.yy.result.colRef.linked;
   }

+ 4 - 2
desktop/core/src/desktop/static/desktop/spec/autocomplete/sqlSpecError.js

@@ -90,7 +90,8 @@
         hasLocations: true,
         expectedResult: {
           lowerCase: false,
-          suggestKeywords: ['BY']
+          suggestKeywords: ['BY'],
+          suggestGroupBys: { prefix: 'BY', tables: [{ identifierChain: [{ name: 'testTable' }] }] }
         }
       });
     });
@@ -102,7 +103,8 @@
         hasLocations: true,
         expectedResult: {
           lowerCase: false,
-          suggestKeywords: ['BY']
+          suggestKeywords: ['BY'],
+          suggestOrderBys: { prefix: 'BY', tables: [{ identifierChain: [{ name: 'testTable' }] }] }
         }
       });
     });

+ 105 - 28
desktop/core/src/desktop/static/desktop/spec/autocomplete/sqlSpecSelect.js

@@ -124,9 +124,12 @@
         dialect: 'generic',
         hasLocations: true,
         expectedResult: {
-          lowerCase: false,
           suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'testTableB' }] }] },
-          suggestKeywords: ['AS', 'WHERE', 'GROUP BY', 'HAVING', 'ORDER BY', 'LIMIT', 'UNION', 'FULL JOIN', 'FULL OUTER JOIN', 'INNER JOIN', 'JOIN', 'LEFT JOIN', 'LEFT OUTER JOIN', 'RIGHT JOIN', 'RIGHT OUTER JOIN']
+          suggestFilters: { prefix: 'WHERE', tables: [{ identifierChain: [{ name: 'testTableA' }], alias: 'tta' }, { identifierChain: [{ name: 'testTableB' }] }] },
+          suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'testTableA' }], alias: 'tta' }, { identifierChain: [{ name: 'testTableB' }] }] },
+          suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'testTableA' }], alias: 'tta' }, { identifierChain: [{ name: 'testTableB' }] }] },
+          suggestKeywords: ['AS', 'WHERE', 'GROUP BY', 'HAVING', 'ORDER BY', 'LIMIT', 'UNION', 'FULL JOIN', 'FULL OUTER JOIN', 'INNER JOIN', 'JOIN', 'LEFT JOIN', 'LEFT OUTER JOIN', 'RIGHT JOIN', 'RIGHT OUTER JOIN'],
+          lowerCase: false
         }
       });
     });
@@ -872,6 +875,8 @@
           containsColRefKeywords: true,
           expectedResult: {
             lowerCase: false,
+            suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'testTable' }] }] },
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'testTable' }] }] },
             colRef: { identifierChain:[{ name: 'testTable' }, { name: '${some_variable}' }]}
           }
         });
@@ -1301,7 +1306,10 @@
             containsKeywords: ['WINDOW'],
             expectedResult: {
               lowerCase: false,
-              suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'customers' }] }] }
+              suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'customers' }] }] },
+              suggestFilters: { prefix: 'WHERE', tables: [{ identifierChain: [{ name: 'customers' }] }] },
+              suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'customers' }] }] },
+              suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'customers' }] }] }
             }
           });
         });
@@ -2492,6 +2500,9 @@
           expectedResult: {
             lowerCase: false,
             suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'foo' }] }] },
+            suggestFilters: { prefix: 'WHERE', tables: [{ identifierChain: [{ name: 'foo' }] }] },
+            suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'foo' }] }] },
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'foo' }] }] },
             suggestKeywords: ['TABLESAMPLE', 'AS', 'LATERAL VIEW', 'WHERE', 'GROUP BY', 'HAVING', 'WINDOW', 'ORDER BY', 'CLUSTER BY', 'DISTRIBUTE BY', 'SORT BY', 'LIMIT', 'UNION', 'CROSS JOIN', 'FULL JOIN', 'FULL OUTER JOIN', 'JOIN', 'LEFT JOIN', 'LEFT OUTER JOIN', 'LEFT SEMI JOIN', 'RIGHT JOIN', 'RIGHT OUTER JOIN']
           }
         });
@@ -2506,6 +2517,9 @@
           expectedResult: {
             lowerCase: false,
             suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'db' }, { name: 'foo' }], alias: 'f' }] },
+            suggestFilters: { prefix: 'WHERE', tables: [{ identifierChain: [{ name: 'db' }, { name: 'foo' }], alias: 'f' }] },
+            suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'db' }, { name: 'foo' }], alias: 'f' }] },
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'db' }, { name: 'foo' }], alias: 'f' }] },
             suggestKeywords: ['LATERAL VIEW', 'WHERE', 'GROUP BY', 'HAVING', 'WINDOW', 'ORDER BY', 'CLUSTER BY', 'DISTRIBUTE BY', 'SORT BY', 'LIMIT', 'UNION', 'CROSS JOIN', 'FULL JOIN', 'FULL OUTER JOIN', 'JOIN', 'LEFT JOIN', 'LEFT OUTER JOIN', 'LEFT SEMI JOIN', 'RIGHT JOIN', 'RIGHT OUTER JOIN']
           }
         });
@@ -2520,6 +2534,9 @@
           containsKeywords: ['ON'],
           expectedResult: {
             lowerCase: false,
+            suggestFilters: { prefix: 'WHERE', tables: [{ identifierChain: [{ name: 'foo' }] }, { identifierChain: [{ name: 'baz' }] }] },
+            suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'foo' }] }, { identifierChain: [{ name: 'baz' }] }] },
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'foo' }] }, { identifierChain: [{ name: 'baz' }] }] },
             suggestJoinConditions: { prependOn: true, tables: [{ identifierChain: [{ name: 'foo' }] }, { identifierChain: [{ name: 'baz' }] }] }
           }
         });
@@ -2548,7 +2565,10 @@
           containsKeywords: ['WHERE', 'HAVING'],
           expectedResult: {
             lowerCase: false,
-            suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'foo' }] }] }
+            suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'foo' }] }] },
+            suggestFilters: { prefix: 'WHERE', tables: [{ identifierChain: [{ name: 'foo' }] }] },
+            suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'foo' }] }] },
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'foo' }] }] }
           }
         });
       });
@@ -2614,7 +2634,10 @@
           containsKeywords: ['AS', 'WHERE'],
           expectedResult: {
             lowerCase: false,
-            suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'foo' }] }] }
+            suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'foo' }] }] },
+            suggestFilters: { prefix: 'WHERE', tables: [{ identifierChain: [{ name: 'foo' }] }] },
+            suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'foo' }] }] },
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'foo' }] }] }
           }
         });
       });
@@ -3061,6 +3084,9 @@
           expectedResult: {
             lowerCase: false,
             suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'testTableB' }] }] },
+            suggestFilters: { prefix: 'WHERE', tables: [{ identifierChain: [{ name: 'testTableA' }], alias: 'tta' }, { identifierChain: [{ name: 'testTableB' }] }] },
+            suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'testTableA' }], alias: 'tta' }, { identifierChain: [{ name: 'testTableB' }] }] },
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'testTableA' }], alias: 'tta' }, { identifierChain: [{ name: 'testTableB' }] }] },
             suggestKeywords: ['AS', 'WHERE', 'GROUP BY', 'HAVING', 'ORDER BY', 'LIMIT', 'OFFSET', 'UNION', 'FULL JOIN', 'FULL OUTER JOIN', 'INNER JOIN', 'JOIN', 'LEFT ANTI JOIN', 'LEFT JOIN', 'LEFT OUTER JOIN', 'LEFT SEMI JOIN', 'RIGHT ANTI JOIN', 'RIGHT JOIN', 'RIGHT OUTER JOIN', 'RIGHT SEMI JOIN']
           }
         });
@@ -4344,7 +4370,8 @@
         });
       });
 
-      it('should suggest columns for "SELECT a, b, c FROM testTable WHERE | RLIKE \'bla bla\'"', function() {
+      // TODO: This one causes an unrecoverable error after the cursor, we should suggest group by etc.
+      it('should suggest columns for "SELECT a, b, c FROM testTable WHERE d | RLIKE \'bla bla\'"', function() {
         assertAutoComplete({
           beforeCursor: 'SELECT a, b, c FROM testTable WHERE d ',
           afterCursor: ' RLIKE \'bla bla\'',
@@ -4353,7 +4380,10 @@
           containsColRefKeywords: true,
           expectedResult: {
             lowerCase: false,
-            colRef: { identifierChain: [{ name: 'testTable' }, { name: 'd'}] }
+            colRef: { identifierChain: [{ name: 'testTable' }, { name: 'd'}] },
+            suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'testTable' }] }] },
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'testTable' }] }] },
+            colRef: { identifierChain: [{ name: 'testTable' }, { name: 'd' }] }
           }
         });
       });
@@ -4366,6 +4396,8 @@
           hasLocations: true,
           expectedResult: {
             lowerCase: false,
+            suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'foo' }] }] },
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'foo' }] }] },
             suggestKeywords: ['GROUP BY', 'HAVING', 'ORDER BY', 'LIMIT', 'UNION', '<', '<=', '<>', '=', '>', '>=', 'AND', 'BETWEEN', 'IN', 'IS NOT NULL', 'IS NULL', 'NOT BETWEEN', 'NOT IN', 'OR']
           }
         });
@@ -4379,6 +4411,8 @@
           hasLocations: true,
           expectedResult: {
             lowerCase: false,
+            suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'foo' }] }] },
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'foo' }] }] },
             suggestKeywords: ['GROUP BY', 'HAVING', 'ORDER BY', 'LIMIT', 'UNION', '<', '<=', '<>', '=', '>', '>=', 'AND', 'BETWEEN', 'IN', 'IS NOT NULL', 'IS NULL', 'NOT BETWEEN', 'NOT IN', 'OR']
           }
         });
@@ -4455,6 +4489,7 @@
           hasLocations: true,
           expectedResult: {
             lowerCase: false,
+            suggestGroupBys: {prefix: 'BY', tables: [{identifierChain: [{name: 'foo'}]}]},
             suggestKeywords: ['BY']
           }
         });
@@ -4467,7 +4502,9 @@
           hasLocations: true,
           containsKeywords: ['AND'],
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'foo' }] }] },
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'foo' }] }] }
           }
         });
       });
@@ -4596,6 +4633,8 @@
             hasLocations: true,
             expectedResult: {
               lowerCase: false,
+              suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'foo' }] }] },
+              suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'foo' }] }] },
               suggestKeywords: ['GROUP BY', 'HAVING', 'WINDOW', 'ORDER BY', 'CLUSTER BY', 'DISTRIBUTE BY', 'SORT BY', 'LIMIT', 'UNION', '<', '<=', '<=>', '<>', '=', '>', '>=', 'AND', 'BETWEEN',  'IN', 'IS NOT NULL', 'IS NULL',  'NOT BETWEEN', 'NOT IN', 'OR']
             }
           });
@@ -4613,7 +4652,8 @@
             lowerCase: false,
             suggestFunctions: {},
             suggestColumns: { source: 'where',  tables: [{ identifierChain: [{ name: 'testTable' }] }] },
-            suggestKeywords: ['EXISTS', 'NOT EXISTS']
+            suggestKeywords: ['EXISTS', 'NOT EXISTS'],
+            suggestFilters: { tables: [{ identifierChain: [{ name: 'testTable' }] }] }
           }
         });
       });
@@ -4627,7 +4667,8 @@
             lowerCase: false,
             suggestFunctions: {},
             suggestColumns: { source: 'where',  tables: [{ identifierChain: [{ name: 'testTable' }] }] },
-            suggestKeywords: ['EXISTS', 'NOT EXISTS']
+            suggestKeywords: ['EXISTS', 'NOT EXISTS'],
+            suggestFilters: { tables: [{ identifierChain: [{ name: 'testTable' }] }] }
           }
         });
       });
@@ -4653,7 +4694,9 @@
           hasLocations: true,
           containsKeywords: ['AND', '<'],
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'testTable' }] }] },
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'testTable' }] }] }
           }
         });
       });
@@ -4665,7 +4708,9 @@
           hasLocations: true,
           containsKeywords: ['AND', '='],
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'tableOne' }] }] },
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'tableOne' }] }] }
           }
         });
       });
@@ -4769,7 +4814,9 @@
           hasLocations: true,
           expectedResult: {
             lowerCase: false,
-            colRef: { identifierChain: [{ name: 'testTable' }, { name: 'a'}] }
+            colRef: { identifierChain: [{ name: 'testTable' }, { name: 'a'}] },
+            suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'testTable' }] }] },
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'testTable' }] }] }
           }
         });
       });
@@ -4926,6 +4973,7 @@
           containsColRefKeywords: true,
           expectedResult: {
             lowerCase: false,
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'testTable' }] }] },
             colRef: { identifierChain: [{ name: 'testTable' }, { name: 'a' }] }
           }
         });
@@ -4938,7 +4986,8 @@
           hasLocations: true,
           expectedResult: {
             lowerCase: false,
-            suggestKeywords: ['BY']
+            suggestKeywords: ['BY'],
+            suggestOrderBys: { prefix: 'BY', tables: [{ identifierChain: [{ name: 'testTable' }] }] }
           }
         });
       });
@@ -4953,7 +5002,8 @@
             lowerCase: false,
             suggestFunctions: {},
             suggestAnalyticFunctions: true,
-            suggestColumns: { source: 'order by', tables: [{ identifierChain: [{ name: 'testTable' }] }] }
+            suggestColumns: { source: 'order by', tables: [{ identifierChain: [{ name: 'testTable' }] }] },
+            suggestOrderBys: { tables: [{ identifierChain: [{ name: 'testTable' }] }] }
           }
         });
       });
@@ -4965,10 +5015,11 @@
           hasLocations: true,
           containsKeywords: ['CASE'],
           expectedResult: {
-            lowerCase: false,
+            suggestColumns: { source: 'order by', tables: [{ identifierChain: [{ name: 'database_two' }, { name: 'testTable' }] }] },
             suggestFunctions: {},
             suggestAnalyticFunctions: true,
-            suggestColumns: { source: 'order by', tables: [{ identifierChain: [{ name: 'database_two' }, { name: 'testTable' }] }] }
+            suggestOrderBys: { tables: [{ identifierChain: [{ name: 'database_two' }, { name: 'testTable' }] }] },
+            lowerCase: false
           }
         });
       });
@@ -5233,7 +5284,8 @@
           hasLocations: true,
           expectedResult: {
             lowerCase: false,
-            suggestKeywords: ['BY']
+            suggestKeywords: ['BY'],
+            suggestGroupBys: { prefix: 'BY', tables: [{ identifierChain: [{ name: 'testTable' }] }] }
           }
         });
       });
@@ -5249,7 +5301,8 @@
             lowerCase: false,
             suggestFunctions: {},
             suggestColumns: { source: 'group by', tables: [{ identifierChain: [{ name: 'testTableA' }], alias: 'tta' }, { identifierChain: [{ name: 'testTableB' }] }] },
-            suggestIdentifiers: [{ name: 'tta.', type: 'alias' }, { name: 'testTableB.', type: 'table' }]
+            suggestIdentifiers: [{ name: 'tta.', type: 'alias' }, { name: 'testTableB.', type: 'table' }],
+            suggestGroupBys: { tables: [{ identifierChain: [{ name: 'testTableA' }], alias: 'tta' }, { identifierChain: [{ name: 'testTableB' }] }] }
           }
         });
       });
@@ -5311,7 +5364,8 @@
           expectedResult: {
             lowerCase: false,
             suggestFunctions: {},
-            suggestColumns: { source: 'group by', tables: [{ identifierChain: [{ name: 'testTable' }] }] }
+            suggestColumns: { source: 'group by', tables: [{ identifierChain: [{ name: 'testTable' }] }] },
+            suggestGroupBys: { tables: [{ identifierChain: [{ name: 'testTable' }] }] }
           }
         });
       });
@@ -5325,7 +5379,8 @@
           expectedResult: {
             lowerCase: false,
             suggestFunctions: {},
-            suggestColumns: { source: 'group by', tables: [{ identifierChain: [{ name: 'database_two' }, { name: 'testTable' }] }] }
+            suggestColumns: { source: 'group by', tables: [{ identifierChain: [{ name: 'database_two' }, { name: 'testTable' }] }] },
+            suggestGroupBys: { tables: [{ identifierChain: [{ name: 'database_two' }, { name: 'testTable' }] }] }
           }
         });
       });
@@ -5383,6 +5438,7 @@
             containsColRefKeywords: true,
             expectedResult: {
               lowerCase: false,
+              suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'database_two' }, { name: 'testTable' }] }] },
               colRef: { identifierChain: [{ name: 'database_two' }, { name: 'testTable' }, { name: 'b'}] }
             }
           });
@@ -5598,7 +5654,10 @@
           containsKeywords: ['TABLESAMPLE', 'AS'],
           expectedResult: {
             lowerCase: false,
-            suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'boo' }] }] }
+            suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'boo' }] }] },
+            suggestFilters: { prefix: 'WHERE', tables: [{ identifierChain: [{ name: 'boo' }] }] },
+            suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'boo' }] }] },
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'boo' }] }] }
           }
         });
       });
@@ -5612,7 +5671,10 @@
           containsKeywords: ['TABLESAMPLE', 'AS'],
           expectedResult: {
             lowerCase: false,
-            suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'boo' }] }] }
+            suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'boo' }] }] },
+            suggestFilters: { prefix: 'WHERE', tables: [{ identifierChain: [{ name: 'boo' }] }] },
+            suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'boo' }] }] },
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'boo' }] }] }
           }
         });
       });
@@ -6040,7 +6102,10 @@
           containsKeywords: ['ON'],
           expectedResult: {
             lowerCase: false,
-            suggestJoinConditions: { prependOn: true, tables: [{ identifierChain: [{ name: 'testTable1' }] }, { identifierChain: [{ name: 'testTable2' }] }] }
+            suggestJoinConditions: { prependOn: true, tables: [{ identifierChain: [{ name: 'testTable1' }] }, { identifierChain: [{ name: 'testTable2' }] }] },
+            suggestFilters: { prefix: 'WHERE', tables: [{ identifierChain: [{ name: 'testTable1' }] }, { identifierChain: [{ name: 'testTable2' }] }] },
+            suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'testTable1' }] }, { identifierChain: [{ name: 'testTable2' }] }] },
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'testTable1' }] }, { identifierChain: [{ name: 'testTable2' }] }] }
           }
         });
       });
@@ -6317,7 +6382,10 @@
           containsKeywords: ['JOIN'],
           expectedResult: {
             lowerCase: false,
-            suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'testTable1' }] }] }
+            suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'testTable1' }] }] },
+            suggestFilters: { prefix: 'WHERE', tables: [{ identifierChain: [{ name: 'testTable1' }] }] },
+            suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'testTable1' }] }] },
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'testTable1' }] }] }
           }
         });
       });
@@ -6332,7 +6400,10 @@
             containsKeywords: ['LEFT SEMI JOIN', 'CROSS JOIN'], // Tested in full above
             expectedResult: {
               lowerCase: false,
-              suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'table1' }], alias: 't1' }] }
+              suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'table1' }], alias: 't1' }] },
+              suggestFilters: { prefix: 'WHERE', tables: [{ identifierChain: [{ name: 'table1' }], alias: 't1' }] },
+              suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'table1' }], alias: 't1' }] },
+              suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'table1' }], alias: 't1' }] }
             }
           });
         });
@@ -6499,6 +6570,7 @@
               suggestFunctions: {},
               suggestKeywords: ['EXISTS', 'NOT EXISTS'],
               suggestColumns: { source: 'where', tables: [{ identifierChain: [{ name: 'table1' }], alias: 't1'}, { identifierChain: [{ name: 'table2' }], alias: 't2' }] },
+              suggestFilters: { tables: [{ identifierChain: [{ name: 'table1' }], alias: 't1' }, { identifierChain: [{ name: 'table2' }], alias: 't2' }] },
               suggestIdentifiers: [{ name: 't1.', type: 'alias' }, { name: 't2.', type: 'alias' }]
             }
           });
@@ -6513,7 +6585,10 @@
             hasLocations: true,
             expectedResult: {
               lowerCase: false,
-              suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'table1' }], alias: 't1' }] }
+              suggestJoins: { prependJoin: true, tables: [{ identifierChain: [{ name: 'table1' }], alias: 't1' }] },
+              suggestFilters: { prefix: 'WHERE', tables: [{ identifierChain: [{ name: 'table1' }], alias: 't1' }] },
+              suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'table1' }], alias: 't1' }] },
+              suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'table1' }], alias: 't1' }] }
             }
           });
         });
@@ -6698,7 +6773,9 @@
           hasLocations: true,
           expectedResult: {
             lowerCase: false,
-            colRef: { identifierChain: [{ name: 'foo' }, { name: 'bar' }] }
+            colRef: { identifierChain: [{ name: 'foo' }, { name: 'bar' }] },
+            suggestGroupBys: { prefix: 'GROUP BY', tables: [{ identifierChain: [{ name: 'foo' }] }] },
+            suggestOrderBys: { prefix: 'ORDER BY', tables: [{ identifierChain: [{ name: 'foo' }] }] }
           }
         });
       });

+ 9 - 2
desktop/core/src/desktop/static/desktop/spec/autocompleterTestUtils.js

@@ -15,6 +15,13 @@
 // limitations under the License.
 
 var SqlTestUtils = (function() {
+
+  var jsonStringToJsString = function (jsonString) {
+    return jsonString.replace(/'([a-zA-Z]+)':/g, function (all, group) {
+      return group + ':';
+    }).replace(/([:{,])/g, function (all, group) { return group + ' ' }).replace(/[}]/g, ' }');
+  };
+
   return {
     autocompleteMatcher : {
       toEqualAutocompleteValues : function() {
@@ -142,8 +149,8 @@ var SqlTestUtils = (function() {
               pass: jasmine.matchersUtil.equals(actualResponse, testDefinition.expectedResult),
               message: '\n        Statement: ' + testDefinition.beforeCursor + '|' + testDefinition.afterCursor + '\n' +
                          '          Dialect: ' + testDefinition.dialect + '\n' +
-                         'Expected response: ' + JSON.stringify(testDefinition.expectedResult).replace(/["]/g, '\'') + '\n' +
-                         '  Parser response: ' + JSON.stringify(actualResponse).replace(/["]/g, '\'') +   '\n'
+                         'Expected response: ' + jsonStringToJsString(JSON.stringify(testDefinition.expectedResult).replace(/["]/g, '\'') + '\n') +
+                         '  Parser response: ' + jsonStringToJsString(JSON.stringify(actualResponse).replace(/["]/g, '\'') +   '\n')
             };
           }
         }