Browse Source

HUE-4032 [editor] Identify locations of databases, tables, columns and functions

This also takes care of a couple of smaller issues related to structs in Impala and Hive
Johan Ahlen 9 years ago
parent
commit
b302a5add3

+ 252 - 69
desktop/core/src/desktop/static/desktop/js/autocomplete/sql.jison

@@ -24,8 +24,8 @@
 '--'.*                              { /* skip comments */ }
 [/][*][^*]*[*]+([^/*][^*]*[*]+)*[/] { /* skip comments */ }
 
-'\u2020'                            { parser.yy.cursorFound = true; return 'CURSOR'; }
-'\u2021'                            { parser.yy.cursorFound = true; return 'PARTIAL_CURSOR'; }
+'\u2020'                            { parser.yy.partialCursor = false; parser.yy.cursorFound = yylloc; return 'CURSOR'; }
+'\u2021'                            { parser.yy.partialCursor = true; parser.yy.cursorFound = yylloc; return 'PARTIAL_CURSOR'; }
 
 <hive>'AS'                          { return '<hive>AS'; }
 <hive>'ALL'                         { return '<hive>ALL'; }
@@ -154,6 +154,7 @@
 'JOIN'                              { return 'JOIN'; }
 'LEFT'                              { return 'LEFT'; }
 'LIKE'                              { return 'LIKE'; }
+'LIMIT'                             { return 'LIMIT'; }
 'NOT'                               { return 'NOT'; }
 'NULL'                              { return 'NULL'; }
 'ON'                                { return 'ON'; }
@@ -181,33 +182,33 @@
 'WITHIN'                            { return 'WITHIN'; }
 
 // --- UDFs ---
-'AVG('                              { return 'AVG('; }
-'CAST('                             { return 'CAST('; }
-'COUNT('                            { return 'COUNT('; }
-'MAX('                              { return 'MAX('; }
-'MIN('                              { return 'MIN('; }
-'STDDEV_POP('                       { return 'STDDEV_POP('; }
-'STDDEV_SAMP('                      { return 'STDDEV_SAMP('; }
-'SUM('                              { return 'SUM('; }
-'VARIANCE('                         { return 'VARIANCE('; }
-'VAR_POP('                          { return 'VAR_POP('; }
-'VAR_SAMP('                         { return 'VAR_SAMP('; }
-<hive>'COLLECT_SET('                { return '<hive>COLLECT_SET('; }
-<hive>'COLLECT_LIST('               { return '<hive>COLLECT_LIST('; }
-<hive>'CORR('                       { return '<hive>CORR('; }
-<hive>'COVAR_POP('                  { return '<hive>COVAR_POP('; }
-<hive>'COVAR_SAMP('                 { return '<hive>COVAR_SAMP('; }
-<hive>'HISTOGRAM_NUMERIC('          { return '<hive>HISTOGRAM_NUMERIC('; }
-<hive>'NTILE('                      { return '<hive>NTILE('; }
-<hive>'PERCENTILE('                 { return '<hive>PERCENTILE('; }
-<hive>'PERCENTILE_APPROX('          { return '<hive>PERCENTILE_APPROX('; }
-<impala>'APPX_MEDIAN('              { return '<impala>APPX_MEDIAN('; }
-<impala>'EXTRACT('                  { return '<impala>EXTRACT('; }
-<impala>'GROUP_CONCAT('             { return '<impala>GROUP_CONCAT('; }
-<impala>'STDDEV('                   { return '<impala>STDDEV('; }
-<impala>'VARIANCE_POP('             { return '<impala>VARIANCE_POP('; }
-<impala>'VARIANCE_SAMP('            { return '<impala>VARIANCE_SAMP('; }
-[A-Za-z][A-Za-z0-9_]*\(             { return 'UDF('; }
+'AVG('                              { addFunctionLocation(yylloc, 'avg'); return 'AVG('; }
+'CAST('                             { addFunctionLocation(yylloc, 'cast');return 'CAST('; }
+'COUNT('                            { addFunctionLocation(yylloc, 'count');return 'COUNT('; }
+'MAX('                              { addFunctionLocation(yylloc, 'max');return 'MAX('; }
+'MIN('                              { addFunctionLocation(yylloc, 'min');return 'MIN('; }
+'STDDEV_POP('                       { addFunctionLocation(yylloc, 'stddev_pop');return 'STDDEV_POP('; }
+'STDDEV_SAMP('                      { addFunctionLocation(yylloc, 'stddev_samp');return 'STDDEV_SAMP('; }
+'SUM('                              { addFunctionLocation(yylloc, 'sum');return 'SUM('; }
+'VARIANCE('                         { addFunctionLocation(yylloc, 'variance');return 'VARIANCE('; }
+'VAR_POP('                          { addFunctionLocation(yylloc, 'var_pop');return 'VAR_POP('; }
+'VAR_SAMP('                         { addFunctionLocation(yylloc, 'var_samp');return 'VAR_SAMP('; }
+<hive>'COLLECT_SET('                { addFunctionLocation(yylloc, 'collect_set');return '<hive>COLLECT_SET('; }
+<hive>'COLLECT_LIST('               { addFunctionLocation(yylloc, 'collect_list');return '<hive>COLLECT_LIST('; }
+<hive>'CORR('                       { addFunctionLocation(yylloc, 'corr');return '<hive>CORR('; }
+<hive>'COVAR_POP('                  { addFunctionLocation(yylloc, 'covar_pop');return '<hive>COVAR_POP('; }
+<hive>'COVAR_SAMP('                 { addFunctionLocation(yylloc, 'covar_samp');return '<hive>COVAR_SAMP('; }
+<hive>'HISTOGRAM_NUMERIC('          { addFunctionLocation(yylloc, 'histogram_numeric');return '<hive>HISTOGRAM_NUMERIC('; }
+<hive>'NTILE('                      { addFunctionLocation(yylloc, 'ntile');return '<hive>NTILE('; }
+<hive>'PERCENTILE('                 { addFunctionLocation(yylloc, 'percentile');return '<hive>PERCENTILE('; }
+<hive>'PERCENTILE_APPROX('          { addFunctionLocation(yylloc, 'percentile_approx');return '<hive>PERCENTILE_APPROX('; }
+<impala>'APPX_MEDIAN('              { addFunctionLocation(yylloc, 'appx_median');return '<impala>APPX_MEDIAN('; }
+<impala>'EXTRACT('                  { addFunctionLocation(yylloc, 'extract');return '<impala>EXTRACT('; }
+<impala>'GROUP_CONCAT('             { addFunctionLocation(yylloc, 'group_concat');return '<impala>GROUP_CONCAT('; }
+<impala>'STDDEV('                   { addFunctionLocation(yylloc, 'stddev');return '<impala>STDDEV('; }
+<impala>'VARIANCE_POP('             { addFunctionLocation(yylloc, 'variance_pop');return '<impala>VARIANCE_POP('; }
+<impala>'VARIANCE_SAMP('            { addFunctionLocation(yylloc, 'variance_samp');return '<impala>VARIANCE_SAMP('; }
+[A-Za-z][A-Za-z0-9_]*\(             { addFunctionLocation(yylloc, yytext.substring(0, yytext.length - 1)); return 'UDF('; }
 
 [0-9]+                              { return 'UNSIGNED_INTEGER'; }
 [0-9]+E                             { return 'UNSIGNED_INTEGER_E'; }
@@ -306,7 +307,6 @@ RegularIdentifier
 InitResults
  : /* empty */
    {
-     parser.yy.result = {};
      parser.yy.cursorFound = false;
 
      // TODO: Move these below before token or use $$ instead
@@ -342,8 +342,17 @@ Sql
 SqlStatements
  :
  | DataDefinition
+   {
+     linkLocations();
+   }
  | DataManipulation
+   {
+     linkLocations();
+   }
  | QuerySpecification
+   {
+     linkLocations();
+   }
  | SqlStatements ';' SqlStatements
  ;
 
@@ -353,9 +362,16 @@ SqlStatements_EDIT
      suggestDdlAndDmlKeywords();
    }
  | DataDefinition_EDIT
+   {
+     linkLocations();
+   }
  | DataManipulation_EDIT
+   {
+     linkLocations();
+   }
  | QuerySpecification_EDIT
    {
+     linkLocations();
      linkTablePrimaries();
    }
  | SqlStatements_EDIT ';' SqlStatements
@@ -661,8 +677,16 @@ RightParenthesisOrError
  ;
 
 SchemaQualifiedTableIdentifier
- : RegularOrBacktickedIdentifier                                       -> { identifierChain: [{ name: $1 }] }
- | RegularOrBacktickedIdentifier AnyDot RegularOrBacktickedIdentifier  -> { identifierChain: [{ name: $1 }, { name: $3 }] }
+ : RegularOrBacktickedIdentifier
+   {
+     addTableLocation(@1, [ { name: $1 } ]);
+     $$ = { identifierChain: [ { name: $1 } ] };
+   }
+ | RegularOrBacktickedIdentifier AnyDot RegularOrBacktickedIdentifier
+   {
+     addTableLocation(@3, [ { name: $1 }, { name: $3 } ]);
+     $$ = { identifierChain: [ { name: $1 }, { name: $3 } ] };
+   }
  ;
 
 SchemaQualifiedTableIdentifier_EDIT
@@ -709,9 +733,18 @@ RegularOrBacktickedIdentifier
  | 'BACKTICK' 'VALUE' 'BACKTICK'  -> $2
  ;
 
+// TODO: Same as SchemaQualifiedTableIdentifier?
 RegularOrBackTickedSchemaQualifiedName
- : RegularOrBacktickedIdentifier                                       -> { identifierChain: [ { name: $1 } ] }
- | RegularOrBacktickedIdentifier AnyDot RegularOrBacktickedIdentifier  -> { identifierChain: [ { name: $1 }, { name: $3 } ] }
+ : RegularOrBacktickedIdentifier
+   {
+     addTableLocation(@1, [ { name: $1 } ]);
+     $$ = { identifierChain: [ { name: $1 } ] };
+   }
+ | RegularOrBacktickedIdentifier AnyDot RegularOrBacktickedIdentifier
+   {
+     addTableLocation(@3, [ { name: $1 }, { name: $3 } ]);
+     $$ = { identifierChain: [ { name: $1 }, { name: $3 } ] };
+   }
  ;
 
 RegularOrBackTickedSchemaQualifiedName_EDIT
@@ -744,7 +777,13 @@ ColumnReferenceList
 
 ColumnReference
  : BasicIdentifierChain
+   {
+     addColumnLocation(@1, $1);
+   }
  | BasicIdentifierChain AnyDot '*'
+   {
+     addColumnLocation(@1, $1);
+   }
  ;
 
 ColumnReference_EDIT
@@ -1153,8 +1192,18 @@ DescribeStatement_EDIT
 
 HiveDescribeStatement
  : '<hive>DESCRIBE' OptionalExtendedOrFormatted SchemaQualifiedTableIdentifier DerivedColumnChain
+   {
+     addTablePrimary($3);
+     addColumnLocation(@4, $4);
+   }
  | '<hive>DESCRIBE' OptionalExtendedOrFormatted SchemaQualifiedTableIdentifier
+   {
+     addTablePrimary($3);
+   }
  | '<hive>DESCRIBE' DatabaseOrSchema OptionalExtended DatabaseIdentifier
+   {
+     addDatabaseLocation(@4, $4);
+   }
  | '<hive>DESCRIBE' '<hive>FUNCTION' OptionalExtended RegularIdentifier
  ;
 
@@ -1205,12 +1254,16 @@ HiveDescribeStatement_EDIT
 
 ImpalaDescribeStatement
  : '<impala>DESCRIBE' OptionalFormatted SchemaQualifiedTableIdentifier
+   {
+     addTablePrimary($3);
+   }
  ;
 
 ImpalaDescribeStatement_EDIT
  : '<impala>DESCRIBE' OptionalFormatted SchemaQualifiedTableIdentifier_EDIT
  | '<impala>DESCRIBE' OptionalFormatted 'CURSOR' SchemaQualifiedTableIdentifier
    {
+     addTablePrimary($4);
      if (!$2) {
        suggestKeywords(['FORMATTED']);
      }
@@ -2488,7 +2541,6 @@ SelectList_EDIT
    {
      suggestFunctions();
      suggestColumns();
-     suggestFunctions();
      $$ = { suggestAggregateFunctions: true, suggestKeywords: ['*'] };
    }
  | SelectList ',' SelectListPartTwo_EDIT                 -> $3
@@ -2502,6 +2554,7 @@ SelectListPartTwo_EDIT
    {
      suggestFunctions();
      suggestColumns();
+     // TODO: Only if there's no FROM
      suggestTables({ prependQuestionMark: true, prependFrom: true });
      suggestDatabases({ prependQuestionMark: true, prependFrom: true, appendDot: true });
      $$ = { suggestKeywords: ['*'], suggestAggregateFunctions: true };
@@ -2510,8 +2563,17 @@ SelectListPartTwo_EDIT
 
 DerivedColumn_TWO
  : ColumnIdentifier
+   {
+     addColumnLocation(@1, [$1]);
+   }
  | ColumnIdentifier AnyDot '*'
+   {
+     addColumnLocation(@1, [$1]);
+   }
  | ColumnIdentifier AnyDot DerivedColumnChain
+   {
+     addColumnLocation(@2, [$1].concat($3));
+   }
  ;
 
 DerivedColumn_EDIT_TWO
@@ -2832,7 +2894,7 @@ PushQueryState
      parser.yy.primariesStack.push(parser.yy.latestTablePrimaries);
      parser.yy.resultStack.push(parser.yy.result);
 
-     parser.yy.result = {};
+     parser.yy.result = { locations: [] };
      if (parser.yy.correlatedSubquery) {
        parser.yy.latestTablePrimaries = parser.yy.latestTablePrimaries.concat();
      } else {
@@ -2844,9 +2906,15 @@ PushQueryState
 PopQueryState
  :
    {
-     if (Object.keys(parser.yy.result).length === 0) {
+     linkLocations();
+     var locations = parser.yy.result.locations;
+     if (Object.keys(parser.yy.result).length === 1) {
        parser.yy.result = parser.yy.resultStack.pop();
+       parser.yy.result.locations = parser.yy.result.locations.concat(locations);
        parser.yy.latestTablePrimaries = parser.yy.primariesStack.pop();
+     } else {
+       var oldResult = parser.yy.resultStack.pop();
+       parser.yy.result.locations = oldResult.concat(locations);
      }
    }
  ;
@@ -2963,8 +3031,16 @@ OptionalLateralViews_EDIT
 
 // TODO: '<hive>[pos]explode' '(' 'CURSOR' possible?
 UserDefinedTableGeneratingFunction
- : '<hive>EXPLODE(' DerivedColumnChain ')'     -> { function: $1.substring(0, $1.length - 1), expression: $2 }
- | '<hive>POSEXPLODE(' DerivedColumnChain ')'  -> { function: $1.substring(0, $1.length - 1), expression: $2 }
+ : '<hive>EXPLODE(' DerivedColumnChain ')'
+   {
+     addColumnLocation(@2, $2);
+     $$ = { function: $1.substring(0, $1.length - 1), expression: $2 }
+   }
+ | '<hive>POSEXPLODE(' DerivedColumnChain ')'
+   {
+     addColumnLocation(@2, $2);
+     $$ = { function: $1.substring(0, $1.length - 1), expression: $2 }
+   }
  ;
 
 UserDefinedTableGeneratingFunction_EDIT
@@ -3399,6 +3475,7 @@ ShowStatement_EDIT
    }
  | 'SHOW' 'CURSOR' RegularOrBackTickedSchemaQualifiedName
    {
+     addTablePrimary($3);
      if (isImpala()) {
        suggestKeywords(['COLUMN STATS', 'CREATE TABLE', 'PARTITIONS', 'TABLE STATS']);
      }
@@ -3429,6 +3506,9 @@ ShowStatement_EDIT
 
 ShowColumnStatsStatement
  : 'SHOW' '<impala>COLUMN' '<impala>STATS' RegularOrBackTickedSchemaQualifiedName
+   {
+     addTablePrimary($4);
+   }
  ;
 
 ShowColumnStatsStatement_EDIT
@@ -3496,6 +3576,9 @@ ShowConfStatement
 
 ShowCreateTableStatement
  : 'SHOW' HiveOrImpalaCreate AnyTable RegularOrBackTickedSchemaQualifiedName
+   {
+     addTablePrimary($4);
+   }
  ;
 
 ShowCreateTableStatement_EDIT
@@ -3513,6 +3596,7 @@ ShowCreateTableStatement_EDIT
  | 'SHOW' HiveOrImpalaCreate AnyTable RegularOrBackTickedSchemaQualifiedName_EDIT
  | 'SHOW' HiveOrImpalaCreate 'CURSOR' RegularOrBackTickedSchemaQualifiedName
    {
+     addTablePrimary($4);
      suggestKeywords(['TABLE']);
    }
  ;
@@ -3671,9 +3755,21 @@ ShowIndexStatement_EDIT
 
 ShowLocksStatement
  : 'SHOW' '<hive>LOCKS' RegularOrBackTickedSchemaQualifiedName
+   {
+     addTablePrimary($3);
+   }
  | 'SHOW' '<hive>LOCKS' RegularOrBackTickedSchemaQualifiedName '<hive>EXTENDED'
+   {
+     addTablePrimary($3);
+   }
  | 'SHOW' '<hive>LOCKS' RegularOrBackTickedSchemaQualifiedName '<hive>PARTITION' '(' PartitionSpecList ')'
+   {
+     addTablePrimary($3);
+   }
  | 'SHOW' '<hive>LOCKS' RegularOrBackTickedSchemaQualifiedName '<hive>PARTITION' '(' PartitionSpecList ')' '<hive>EXTENDED'
+   {
+     addTablePrimary($3);
+   }
  | 'SHOW' '<hive>LOCKS' DatabaseOrSchema RegularOrBacktickedIdentifier
  ;
 
@@ -3689,12 +3785,14 @@ ShowLocksStatement_EDIT
  | 'SHOW' '<hive>LOCKS' RegularOrBackTickedSchemaQualifiedName_EDIT
  | 'SHOW' '<hive>LOCKS' RegularOrBackTickedSchemaQualifiedName 'CURSOR'
     {
+      addTablePrimary($3);
       suggestKeywords(['EXTENDED', 'PARTITION']);
     }
  | 'SHOW' '<hive>LOCKS' RegularOrBackTickedSchemaQualifiedName_EDIT '<hive>EXTENDED'
  | 'SHOW' '<hive>LOCKS' RegularOrBackTickedSchemaQualifiedName_EDIT '<hive>PARTITION' '(' PartitionSpecList ')'
  | 'SHOW' '<hive>LOCKS' RegularOrBackTickedSchemaQualifiedName '<hive>PARTITION' '(' PartitionSpecList ')' 'CURSOR'
    {
+     addTablePrimary($3);
      suggestKeywords(['EXTENDED']);
    }
  | 'SHOW' '<hive>LOCKS' RegularOrBackTickedSchemaQualifiedName_EDIT '<hive>PARTITION' '(' PartitionSpecList ')' '<hive>EXTENDED'
@@ -3706,8 +3804,17 @@ ShowLocksStatement_EDIT
 
 ShowPartitionsStatement
  : 'SHOW' '<hive>PARTITIONS' RegularOrBackTickedSchemaQualifiedName
+   {
+     addTablePrimary($3);
+   }
  | 'SHOW' '<hive>PARTITIONS' RegularOrBackTickedSchemaQualifiedName '<hive>PARTITION' PartitionSpecList
+   {
+     addTablePrimary($3);
+   }
  | 'SHOW' '<impala>PARTITIONS' RegularOrBackTickedSchemaQualifiedName
+   {
+     addTablePrimary($3);
+   }
  ;
 
 ShowPartitionsStatement_EDIT
@@ -3721,6 +3828,7 @@ ShowPartitionsStatement_EDIT
  | 'SHOW' '<hive>PARTITIONS' RegularOrBackTickedSchemaQualifiedName_EDIT
  | 'SHOW' '<hive>PARTITIONS' RegularOrBackTickedSchemaQualifiedName 'CURSOR'
    {
+     addTablePrimary($3);
      suggestKeywords(['PARTITION']);
    }
  | 'SHOW' '<hive>PARTITIONS' RegularOrBackTickedSchemaQualifiedName_EDIT '<hive>PARTITION' PartitionSpecList
@@ -3830,6 +3938,9 @@ ShowTableStatement_EDIT
      });
    }
  | 'SHOW' '<impala>TABLE' '<impala>STATS' RegularOrBackTickedSchemaQualifiedName
+    {
+      addTablePrimary($4);
+    }
  | 'SHOW' '<impala>TABLE' '<impala>STATS' RegularOrBackTickedSchemaQualifiedName_EDIT
  ;
 
@@ -3852,6 +3963,9 @@ ShowTablesStatement_EDIT
 
 ShowTblPropertiesStatement
  : 'SHOW' '<hive>TBLPROPERTIES' RegularOrBackTickedSchemaQualifiedName
+   {
+     addTablePrimary($3);
+   }
  ;
 
 ShowTblPropertiesStatement_EDIT
@@ -3947,7 +4061,7 @@ SetClause_EDIT
  ;
 
 SetTarget
- : RegularIdentifier
+ : ColumnReference
  ;
 
 UpdateSource
@@ -4140,6 +4254,14 @@ var prioritizeSuggestions = function () {
     }
   }
 
+  var i = parser.yy.result.locations.length;
+  while (i--) {
+    var location = parser.yy.result.locations[i];
+    if (location.type === 'column' && (typeof location.table === 'undefined' || typeof location.identifierChain === 'undefined')) {
+      parser.yy.result.locations.splice(i, 1);
+    }
+  }
+
   if (typeof parser.yy.result.colRef !== 'undefined') {
     if (!parser.yy.result.suggestValues &&
         !parser.yy.result.suggestColRefKeywords &&
@@ -4188,25 +4310,30 @@ var prioritizeSuggestions = function () {
  *
  * [ { name: 't' }, { name: 'someMap', keySet: true }, { name: 'bar' } ]
  */
-parser.expandImpalaIdentifierChain = function (tablePrimaries, identifierChain) {
+parser.expandImpalaIdentifierChain = function (tablePrimaries, originalIdentifierChain) {
+  var identifierChain = originalIdentifierChain.concat(); // Clone in case it's called multiple times.
   if (typeof identifierChain === 'undefined' || identifierChain.length === 0) {
     return identifierChain;
   }
   var firstIdentifier = identifierChain[0].name;
 
-  foundPrimary = tablePrimaries.filter(function (tablePrimary) {
+  var foundPrimary = tablePrimaries.filter(function (tablePrimary) {
     return tablePrimary.alias === firstIdentifier;
   });
 
   if (foundPrimary.length === 1) {
     var firstPart = foundPrimary[0].identifierChain.concat();
     var secondPart = identifierChain.slice(1);
+    var lastFromFirst = firstPart.pop();
     if (typeof identifierChain[0].keySet !== 'undefined') {
-      var lastFromFirst = firstPart.pop();
       firstPart.push({
         name: lastFromFirst.name,
         keySet: identifierChain[0].keySet
       });
+    } else {
+      firstPart.push({
+        name: lastFromFirst.name
+      });
     }
     return firstPart.concat(secondPart);
   }
@@ -4220,12 +4347,12 @@ parser.identifyPartials = function (beforeCursor, afterCursor) {
   return { left: beforeMatch ? beforeMatch[0].length : 0, right: afterMatch ? afterMatch[0].length : 0};
 };
 
-parser.expandLateralViews = function (tablePrimaries, identifierChain) {
+parser.expandLateralViews = function (tablePrimaries, originalIdentifierChain) {
+  var identifierChain = originalIdentifierChain.concat(); // Clone in case it's re-used
   var firstIdentifier = identifierChain[0];
-  var identifierChainParts = [];
   tablePrimaries.forEach(function (tablePrimary) {
     if (typeof tablePrimary.lateralViews !== 'undefined') {
-      tablePrimary.lateralViews.reverse().forEach(function (lateralView) {
+      tablePrimary.lateralViews.concat().reverse().forEach(function (lateralView) {
         if (!lateralView.udtf.expression.columnReference) {
           return;
         }
@@ -4260,13 +4387,13 @@ parser.expandLateralViews = function (tablePrimaries, identifierChain) {
 };
 
 var linkSuggestion = function (suggestion, isColumnSuggestion) {
-  var identifierChain = suggestion.identifierChain;
-  var tablePrimaries = parser.yy.latestTablePrimaries;
-
-  if (typeof identifierChain === 'undefined' || typeof tablePrimaries === 'undefined') {
+  if (typeof suggestion.identifierChain === 'undefined' || typeof parser.yy.latestTablePrimaries === 'undefined') {
     return;
   }
 
+  var identifierChain = suggestion.identifierChain.concat();
+  var tablePrimaries = parser.yy.latestTablePrimaries;
+
   // Impala can have references to maps or array, i.e. FROM table t, t.map m
   // We need to replace those in the identifierChain
   if (isImpala()) {
@@ -4312,7 +4439,7 @@ var linkSuggestion = function (suggestion, isColumnSuggestion) {
     var dbAndTable = false;
     if (foundTable.length === 0) {
       foundTable = tablePrimaries.filter(function (tablePrimary) {
-        if (identifierChain[0].name === tablePrimary.identifierChain[0].name) {
+        if (tablePrimary.identifierChain && identifierChain[0].name === tablePrimary.identifierChain[0].name) {
           if (identifierChain.length > 1 && tablePrimary.identifierChain.length > 1) {
             dbAndTable = identifierChain[1].name === tablePrimary.identifierChain[1].name;
           }
@@ -4328,6 +4455,7 @@ var linkSuggestion = function (suggestion, isColumnSuggestion) {
       if (dbAndTable) {
         identifierChain.shift();
       }
+      suggestion.identifierChain = identifierChain;
     }
   }
 
@@ -4373,19 +4501,32 @@ var suggestTablePrimariesAsIdentifiers = function () {
   }
 }
 
+var linkLocations = function () {
+  parser.yy.result.locations.forEach(function (location) {
+    if (location.type === 'column' || location.type === 'table') {
+      linkSuggestion(location);
+      // Impala can have references to previous tables after FROM, i.e. FROM testTable t, t.testArray
+      // In this testArray would be marked a type table so we need to switch it to column.
+      if (location.type === 'table' && typeof location.identifierChain !== 'undefined' && location.identifierChain.length > 0) {
+        location.type = 'column';
+      }
+    }
+  });
+}
+
 var linkTablePrimaries = function () {
-   if (!parser.yy.cursorFound) {
-     return;
-   }
-   if (typeof parser.yy.result.suggestColumns !== 'undefined') {
-     linkSuggestion(parser.yy.result.suggestColumns, true);
-   }
-   if (typeof parser.yy.result.colRef !== 'undefined') {
-     linkSuggestion(parser.yy.result.colRef, false);
-   }
-   if (typeof parser.yy.result.suggestKeyValues !== 'undefined') {
-     linkSuggestion(parser.yy.result.suggestKeyValues, true);
-   }
+  if (!parser.yy.cursorFound) {
+    return;
+  }
+  if (typeof parser.yy.result.suggestColumns !== 'undefined') {
+    linkSuggestion(parser.yy.result.suggestColumns, true);
+  }
+  if (typeof parser.yy.result.colRef !== 'undefined') {
+    linkSuggestion(parser.yy.result.colRef, false);
+  }
+  if (typeof parser.yy.result.suggestKeyValues !== 'undefined') {
+    linkSuggestion(parser.yy.result.suggestKeyValues, true);
+  }
 }
 
 var addTablePrimary = function (ref) {
@@ -4473,6 +4614,48 @@ var suggestTables = function (details) {
   parser.yy.result.suggestTables = details || {};
 }
 
+var adjustLocationForCursor = function (location) {
+   // columns are 0-based and lines not, so add 1 to cols
+   var newLocation = {
+     first_line: location.first_line,
+     last_line: location.last_line,
+     first_column: location.first_column + 1,
+     last_column: location.last_column + 1
+   };
+   if (parser.yy.cursorFound) {
+     if (parser.yy.cursorFound.first_line === newLocation.first_line && parser.yy.cursorFound.last_column <= newLocation.first_column) {
+       var additionalSpace = parser.yy.partialLengths.left + parser.yy.partialLengths.right;
+       additionalSpace -= parser.yy.partialCursor ? 1 : 3; // For some reason the normal cursor eats 3 positions.
+       newLocation.first_column = newLocation.first_column + additionalSpace;
+       newLocation.last_column = newLocation.last_column + additionalSpace;
+     }
+   }
+   return newLocation;
+};
+
+var addFunctionLocation = function (location, functionName) {
+  // Remove trailing '(' from location
+  var adjustedLocation = {
+    first_line: location.first_line,
+    last_line: location.last_line,
+    first_column: location.first_column,
+    last_column: location.last_column - 1
+  }
+  parser.yy.result.locations.push({ type: 'function', location: adjustLocationForCursor(adjustedLocation), function: functionName });
+}
+
+var addDatabaseLocation = function (location, database) {
+  parser.yy.result.locations.push({ type: 'database', location: adjustLocationForCursor(location), database: database });
+}
+
+var addTableLocation = function (location, identifierChain) {
+  parser.yy.result.locations.push({ type: 'table', location: adjustLocationForCursor(location), identifierChain: identifierChain });
+}
+
+var addColumnLocation = function (location, identifierChain) {
+  parser.yy.result.locations.push({ type: 'column', location: adjustLocationForCursor(location), identifierChain: identifierChain });
+}
+
 var suggestDatabases = function (details) {
   parser.yy.result.suggestDatabases = details || {};
 }
@@ -4500,17 +4683,17 @@ parser.parseSql = function(beforeCursor, afterCursor, dialect, sqlFunctions, deb
   }
   parser.yy.sqlFunctions = sqlFunctions;
   parser.yy.activeDialect = dialect;
-  parser.yy.result = {};
+  parser.yy.result = { locations: [] };
   parser.yy.lowerCase = false;
 
-  var partialLengths = parser.identifyPartials(beforeCursor, afterCursor);
+  parser.yy.partialLengths = parser.identifyPartials(beforeCursor, afterCursor);
 
-  if (partialLengths.left > 0) {
-    beforeCursor = beforeCursor.substring(0, beforeCursor.length - partialLengths.left);
+  if (parser.yy.partialLengths.left > 0) {
+    beforeCursor = beforeCursor.substring(0, beforeCursor.length - parser.yy.partialLengths.left);
   }
 
-  if (partialLengths.right > 0) {
-    afterCursor = afterCursor.substring(partialLengths.right);
+  if (parser.yy.partialLengths.right > 0) {
+    afterCursor = afterCursor.substring(parser.yy.partialLengths.right);
   }
 
   // Hack to set the inital state of the lexer without first having to hit a token

File diff suppressed because it is too large
+ 0 - 0
desktop/core/src/desktop/static/desktop/js/autocomplete/sql.js


+ 6 - 0
desktop/core/src/desktop/static/desktop/js/sqlAutocompleter2.js

@@ -190,6 +190,12 @@
       identifierChain = identifierChain.slice(1);
     }
 
+    // Parser sometimes knows if it's a map or array.
+    if (identifierChain.length > 0 && (identifierChain[0].name === 'item' || identifierChain[0].name === 'value')) {
+      fetchedFields.push(identifierChain[0].name);
+      identifierChain = identifierChain.slice(1);
+    }
+
     self.snippet.getApiHelper().fetchFields({
       sourceType: self.snippet.type(),
       databaseName: databaseName,

+ 57 - 2
desktop/core/src/desktop/static/desktop/spec/autocomplete/sqlSpec.js

@@ -120,7 +120,8 @@ define([
         afterCursor: '\n-- other line comment',
         containsKeywords: ['SELECT'],
         expectedResult: {
-          lowerCase: false
+          lowerCase: false,
+          locations: [{ type: 'table', location: { first_line:2, last_line:2, first_column:15, last_column:25 }, table: 'testTable1' }]
         }
       });
     });
@@ -131,7 +132,8 @@ define([
         afterCursor: '',
         containsKeywords: ['SELECT'],
         expectedResult: {
-          lowerCase: false
+          lowerCase: false,
+          locations: [{ type: 'table', location: { first_line:4, last_line:4, first_column:15, last_column:25 }, table: 'testTable1' }]
         }
       });
     });
@@ -174,7 +176,9 @@ define([
 
         var identifierChain = [{ name: 'explodedTable' }, { name: 'testItem' }];
 
+        var tablePrimariesBefore = tablePrimaries.concat();
         var result = sql.expandLateralViews(tablePrimaries, identifierChain);
+        expect(tablePrimariesBefore).toEqual(tablePrimaries);
         expect(result).toEqual([{ name: 'testArray' }, { name: 'item' }]);
       });
 
@@ -193,14 +197,18 @@ define([
 
         var identifierChain = [{ name: 'explodedMap' }, { name: 'testMapValue' }];
 
+        var tablePrimariesBefore = tablePrimaries.concat();
         var result = sql.expandLateralViews(tablePrimaries, identifierChain);
+        expect(tablePrimariesBefore).toEqual(tablePrimaries);
         expect(result).toEqual([{ name: 'testMap' }, { name: 'value' }]);
       });
 
       it('should expand 3', function () {
         var tablePrimaries = [{ identifierChain: [{ name: 'testTable' }] }];
         var identifierChain = [{ name: 'testMap', keySet: true }];
+        var tablePrimariesBefore = tablePrimaries.concat();
         var result = sql.expandLateralViews(tablePrimaries, identifierChain);
+        expect(tablePrimariesBefore).toEqual(tablePrimaries);
         expect(result).toEqual([{ name: 'testMap', keySet: true }]);
       });
 
@@ -217,7 +225,9 @@ define([
           }]
         }];
         var identifierChain = [{ name: 'testItem' }];
+        var tablePrimariesBefore = tablePrimaries.concat();
         var result = sql.expandLateralViews(tablePrimaries, identifierChain);
+        expect(tablePrimariesBefore).toEqual(tablePrimaries);
         expect(result).toEqual([{ name: 'testArray' }, { name: 'item' }]);
       });
 
@@ -241,7 +251,9 @@ define([
           }]
         }];
         var identifierChain = [{ name: 'testItemA' }];
+        var tablePrimariesBefore = tablePrimaries.concat();
         var result = sql.expandLateralViews(tablePrimaries, identifierChain);
+        expect(tablePrimariesBefore).toEqual(tablePrimaries);
         expect(result).toEqual([{ name: 'testArrayA' }, { name: 'item' }]);
       });
 
@@ -266,7 +278,9 @@ define([
           }]
         }];
         var identifierChain = [{ name: 'testItemB' }];
+        var tablePrimariesBefore = tablePrimaries.concat();
         var result = sql.expandLateralViews(tablePrimaries, identifierChain);
+        expect(tablePrimariesBefore).toEqual(tablePrimaries);
         expect(result).toEqual([{ name: 'tt2' }, { name: 'testArrayB' }, { name: 'item' }]);
       });
 
@@ -293,7 +307,9 @@ define([
 
         var identifierChain = [{ name: 'ta2_exp' }];
 
+        var tablePrimariesBefore = tablePrimaries.concat();
         var result = sql.expandLateralViews(tablePrimaries, identifierChain);
+        expect(tablePrimariesBefore).toEqual(tablePrimaries);
         expect(result).toEqual([{ name: 'tt' }, { name: 'testArray1' }, { name: 'item' }, { name: 'testArray2' }, { name: 'item' }]);
       });
 
@@ -312,7 +328,9 @@ define([
 
         var identifierChain = [{ name: 'testValue' }];
 
+        var tablePrimariesBefore = tablePrimaries.concat();
         var result = sql.expandLateralViews(tablePrimaries, identifierChain);
+        expect(tablePrimariesBefore).toEqual(tablePrimaries);
         expect(result).toEqual([{ name: 'testArray' }, { name: 'item' }]);
       });
 
@@ -331,10 +349,34 @@ define([
 
         var identifierChain = [{ name: 'testMapValue' }];
 
+        var tablePrimariesBefore = tablePrimaries.concat();
         var result = sql.expandLateralViews(tablePrimaries, identifierChain);
+        expect(tablePrimariesBefore).toEqual(tablePrimaries);
         expect(result).toEqual([{ name: 'testMap' }, { name: 'value' }]);
       });
 
+
+      it('should expand 10', function () {
+        var tablePrimaries = [{
+          identifierChain: [{ name: 'testTable' }],
+          lateralViews: [{
+            columnAliases: [ 'testItem' ],
+            tableAlias: 'explodedTable',
+            udtf: {
+              expression: { columnReference: [{ name: 'testArray' }] },
+              function: 'explode'
+            }
+          }]
+        }];
+
+        var identifierChain = [{ name: 'testItem' }];
+
+        var tablePrimariesBefore = tablePrimaries.concat();
+        var result = sql.expandLateralViews(tablePrimaries, identifierChain);
+        expect(tablePrimariesBefore).toEqual(tablePrimaries);
+        expect(result).toEqual([{ name: 'testArray' }, { name: 'item' }]);
+      });
+
       it('should expand a simple map reference', function () {
         var tablePrimaries = [
           { alias: 't', identifierChain: [{ name: 'someDb' }, { name: 'someTable' }] },
@@ -348,6 +390,19 @@ define([
         expect(actual).toEqual([{ name: 't' }, { name: 'someMap', keySet: true }, { name: 'bar' }]);
       });
 
+      it('should expand a simple map reference 2', function () {
+        var tablePrimaries = [
+          { alias: 't', identifierChain: [{ name: 'testTable' }] },
+          { alias: 'tm', identifierChain: [{ name: 't' }, { name: 'testMap' }] }
+        ];
+
+        var identifierChain = [{ name: 'tm' }];
+
+        var actual = sql.expandImpalaIdentifierChain(tablePrimaries, identifierChain);
+
+        expect(actual).toEqual([{ name: 't' }, { name: 'testMap' }]);
+      });
+
       it('should expand without map reference', function () {
         var tablePrimaries = [
           { alias: 't1', identifierChain: [{ name: 'databaseTwo' }, { name: 'testTable1' }] },

+ 34 - 6
desktop/core/src/desktop/static/desktop/spec/autocomplete/sqlSpecDescribe.js

@@ -39,19 +39,26 @@ define([
           dialect: 'hive',
           containsKeywords: ['SELECT'],
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            locations: [
+              {type: 'table', location: { first_line: 1, last_line: 1, first_column: 10, last_column: 13 }, table:'tbl' }
+            ]
           }
         });
       });
 
-      it('should handle "DESCRIBE tbl.col.field;|"', function() {
+      it('should handle "DESCRIBE tbl col.field;|"', function() {
         assertAutoComplete({
           beforeCursor: 'DESCRIBE tbl col.field;',
           afterCursor: '',
           dialect: 'hive',
           containsKeywords: ['SELECT'],
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            locations: [
+              {type: 'table', location: { first_line: 1, last_line: 1, first_column: 10, last_column: 13 }, table:'tbl' },
+              {type: 'column', location: { first_line: 1, last_line: 1, first_column: 14, last_column: 23 }, identifierChain: [{ name: 'col' }, { name: 'field'}], table:'tbl' }
+            ]
           }
         });
       });
@@ -63,7 +70,10 @@ define([
           dialect: 'hive',
           containsKeywords: ['SELECT'],
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            locations: [
+              {type: 'table', location: { first_line: 1, last_line: 1, first_column: 19, last_column: 22 }, table:'tbl' }
+            ]
           }
         });
       });
@@ -73,6 +83,7 @@ define([
           beforeCursor: 'DESCRIBE EXTENDED tbl col.field;',
           afterCursor: '',
           dialect: 'hive',
+          hasLocations: true,
           containsKeywords: ['SELECT'],
           expectedResult: {
             lowerCase: false
@@ -85,6 +96,7 @@ define([
           beforeCursor: 'DESCRIBE FORMATTED tbl;',
           afterCursor: '',
           dialect: 'hive',
+          hasLocations: true,
           containsKeywords: ['SELECT'],
           expectedResult: {
             lowerCase: false
@@ -97,6 +109,7 @@ define([
           beforeCursor: 'DESCRIBE FORMATTED tbl col.field;',
           afterCursor: '',
           dialect: 'hive',
+          hasLocations: true,
           containsKeywords: ['SELECT'],
           expectedResult: {
             lowerCase: false
@@ -133,6 +146,7 @@ define([
           beforeCursor: 'DESCRIBE DATABASE db;',
           afterCursor: '',
           dialect: 'hive',
+          hasLocations: true,
           containsKeywords: ['SELECT'],
           expectedResult: {
             lowerCase: false
@@ -145,6 +159,7 @@ define([
           beforeCursor: 'DESCRIBE DATABASE EXTENDED db;',
           afterCursor: '',
           dialect: 'hive',
+          hasLocations: true,
           containsKeywords: ['SELECT'],
           expectedResult: {
             lowerCase: false
@@ -157,6 +172,7 @@ define([
           beforeCursor: 'DESCRIBE SCHEMA db;',
           afterCursor: '',
           dialect: 'hive',
+          hasLocations: true,
           containsKeywords: ['SELECT'],
           expectedResult: {
             lowerCase: false
@@ -169,6 +185,7 @@ define([
           beforeCursor: 'DESCRIBE SCHEMA EXTENDED db;',
           afterCursor: '',
           dialect: 'hive',
+          hasLocations: true,
           containsKeywords: ['SELECT'],
           expectedResult: {
             lowerCase: false
@@ -221,6 +238,7 @@ define([
           beforeCursor: 'DESCRIBE db.tbl ',
           afterCursor: '',
           dialect: 'hive',
+          hasLocations: true,
           expectedResult: {
             lowerCase: false,
             suggestColumns: {
@@ -361,6 +379,7 @@ define([
           beforeCursor: 'DESCRIBE EXTENDED db.tbl ',
           afterCursor: '',
           dialect: 'hive',
+          hasLocations: true,
           expectedResult: {
             lowerCase: false,
             suggestColumns: {
@@ -403,6 +422,7 @@ define([
           beforeCursor: 'DESCRIBE FORMATTED db.tbl ',
           afterCursor: '',
           dialect: 'hive',
+          hasLocations: true,
           expectedResult: {
             lowerCase: false,
             suggestColumns: {
@@ -418,6 +438,7 @@ define([
           beforeCursor: 'DESCRIBE FORMATTED db.tbl col.',
           afterCursor: '',
           dialect: 'hive',
+          hasLocations: true,
           expectedResult: {
             lowerCase: false,
             suggestColumns: {
@@ -438,7 +459,10 @@ define([
           dialect: 'impala',
           containsKeywords: ['SELECT'],
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            locations: [
+              {type: 'table', location: { first_line: 1, last_line: 1, first_column: 10, last_column: 13}, table:'tbl' }
+            ]
           }
         });
       });
@@ -450,7 +474,10 @@ define([
           dialect: 'impala',
           containsKeywords: ['SELECT'],
           expectedResult: {
-            lowerCase: false
+            lowerCase: false,
+            locations: [
+              {type: 'table', location: { first_line: 1, last_line: 1, first_column: 13, last_column: 16}, database:'db', table:'tbl' }
+            ]
           }
         });
       });
@@ -460,6 +487,7 @@ define([
           beforeCursor: 'DESCRIBE FORMATTED db.tbl;',
           afterCursor: '',
           dialect: 'impala',
+          hasLocations: true,
           containsKeywords: ['SELECT'],
           expectedResult: {
             lowerCase: false

+ 4 - 1
desktop/core/src/desktop/static/desktop/spec/autocomplete/sqlSpecDrop.js

@@ -220,7 +220,10 @@ define([
             dialect: 'hive',
             expectedResult: {
               lowerCase: false,
-              suggestKeywords: ['PURGE']
+              suggestKeywords: ['PURGE'],
+              locations: [
+                {type: 'table', location: { first_line: 1, last_line: 1, first_column: 12, last_column: 15}, table: 'foo'}
+              ]
             }
           });
         });

File diff suppressed because it is too large
+ 366 - 13
desktop/core/src/desktop/static/desktop/spec/autocomplete/sqlSpecSelect.js


+ 3 - 0
desktop/core/src/desktop/static/desktop/spec/autocomplete/sqlSpecShow.js

@@ -828,6 +828,7 @@ define([
           beforeCursor: 'SHOW LOCKS tableName ',
           afterCursor: '',
           dialect: 'hive',
+          hasLocations: true,
           expectedResult: {
             lowerCase: false,
             suggestKeywords: ['EXTENDED', 'PARTITION']
@@ -840,6 +841,7 @@ define([
           beforeCursor: 'SHOW LOCKS tableName PARTITION (ds=\'2010-03-03\', hr=\'12\') ',
           afterCursor: '',
           dialect: 'hive',
+          hasLocations: true,
           expectedResult: {
             lowerCase: false,
             suggestKeywords: ['EXTENDED']
@@ -882,6 +884,7 @@ define([
           beforeCursor: 'SHOW PARTITIONS foo ',
           afterCursor: '',
           dialect: 'hive',
+          hasLocations: true,
           expectedResult: {
             lowerCase: false,
             suggestKeywords: ['PARTITION']

+ 52 - 16
desktop/core/src/desktop/static/desktop/spec/autocomplete/sqlSpecUpdate.js

@@ -47,7 +47,10 @@ define([
         afterCursor: '',
         expectedResult: {
           lowerCase: false,
-          suggestKeywords: ['SET']
+          suggestKeywords: ['SET'],
+          locations: [
+            {type: 'table', location: { first_line: 1, last_line: 1, first_column: 8, last_column: 11}, table: 'bar'}
+          ]
         }
       });
     });
@@ -58,7 +61,12 @@ define([
         afterCursor: '',
         expectedResult: {
           lowerCase: false,
-          suggestKeywords: ['WHERE']
+          suggestKeywords: ['WHERE'],
+          locations: [
+            {type: 'table', location: { first_line: 1, last_line: 1, first_column: 8, last_column: 11}, table: 'bar'},
+            {type: 'column', location: { first_line: 1, last_line: 1, first_column: 16, last_column: 18}, table: 'bar', identifierChain: [{ name: 'id' }]},
+            {type: 'column', location: { first_line: 1, last_line: 1, first_column: 22, last_column: 25}, table: 'bar', identifierChain: [{ name: 'foo' }]}
+          ]
         }
       });
     });
@@ -69,7 +77,11 @@ define([
         afterCursor: '',
         expectedResult: {
           lowerCase: false,
-          suggestKeywords: ['=']
+          suggestKeywords: ['='],
+          locations: [
+            {type: 'table', location: { first_line: 1, last_line: 1, first_column: 8, last_column: 11}, table: 'bar'},
+            {type: 'column', location: { first_line: 1, last_line: 1, first_column: 16, last_column: 18}, table: 'bar', identifierChain: [{ name: 'id' }]}
+          ]
         }
       });
     });
@@ -137,28 +149,36 @@ define([
           suggestColumns: {
             database: 'bar',
             table: 'foo'
-          }
+          },
+          locations: [
+            {type: 'table', location: { first_line: 1, last_line: 1, first_column: 12, last_column: 15}, database: 'bar', table: 'foo'}
+          ]
         }
       });
     });
 
-    it('should suggest columns for "UPDATE bar.foo SET id = 1, bar = \'foo\', |"', function() {
+    it('should suggest columns for "UPDATE bar.foo SET id = 1, bla = \'foo\', |"', function() {
       assertAutoComplete({
-        beforeCursor: 'UPDATE bar.foo SET id = 1, bar = \'foo\', ',
+        beforeCursor: 'UPDATE bar.foo SET id = 1, bla = \'foo\', ',
         afterCursor: '',
         expectedResult: {
           lowerCase: false,
           suggestColumns: {
             database: 'bar',
             table: 'foo'
-          }
+          },
+          locations: [
+            {type: 'table', location: { first_line: 1, last_line: 1, first_column: 12, last_column: 15}, database: 'bar', table: 'foo'},
+            {type: 'column', location: { first_line: 1, last_line: 1, first_column: 20, last_column: 22}, database: 'bar', table: 'foo', identifierChain: [{ name: 'id' }]},
+            {type: 'column', location: { first_line: 1, last_line: 1, first_column: 28, last_column: 31}, database: 'bar', table: 'foo', identifierChain: [{ name: 'bla' }]}
+          ]
         }
       });
     });
 
-    it('should suggest columns for "UPDATE bar.foo SET bar = \'foo\' WHERE |"', function() {
+    it('should suggest columns for "UPDATE bar.foo SET bla = \'foo\' WHERE |"', function() {
       assertAutoComplete({
-        beforeCursor: 'UPDATE bar.foo SET bar = \'foo\' WHERE ',
+        beforeCursor: 'UPDATE bar.foo SET bla = \'foo\' WHERE ',
         afterCursor: '',
         expectedResult: {
           lowerCase: false,
@@ -167,14 +187,18 @@ define([
             database: 'bar',
             table: 'foo'
           },
-          suggestKeywords: ['EXISTS', 'NOT EXISTS']
+          suggestKeywords: ['EXISTS', 'NOT EXISTS'],
+          locations: [
+            {type: 'table', location: { first_line: 1, last_line: 1, first_column: 12, last_column: 15}, database: 'bar', table: 'foo'},
+            {type: 'column', location: { first_line: 1, last_line: 1, first_column: 20, last_column: 23}, database: 'bar', table: 'foo', identifierChain: [{ name: 'bla' }]}
+          ]
         }
       });
     });
 
-    it('should suggest values for "UPDATE bar.foo SET bar = \'foo\' WHERE id = |"', function() {
+    it('should suggest values for "UPDATE bar.foo SET bla = \'foo\' WHERE id = |"', function() {
       assertAutoComplete({
-        beforeCursor: 'UPDATE bar.foo SET bar = \'foo\' WHERE id = ',
+        beforeCursor: 'UPDATE bar.foo SET bla = \'foo\' WHERE id = ',
         afterCursor: '',
         expectedResult: {
           lowerCase: false,
@@ -185,14 +209,21 @@ define([
             table: 'foo',
             identifierChain: [{ name: 'id' }]
           },
-          suggestColumns : { types: ['COLREF'] , database: 'bar', table: 'foo' }
+          suggestColumns : { types: ['COLREF'] , database: 'bar', table: 'foo' },
+          locations: [
+            {type: 'table', location: { first_line: 1, last_line: 1, first_column: 12, last_column: 15 }, database:'bar', table: 'foo'},
+            {type: 'column', location: { first_line: 1, last_line: 1, first_column: 20, last_column: 23 }, identifierChain: [{ name: 'bla'}], database: 'bar', table: 'foo'},
+            {type: 'column', location: { first_line: 1, last_line: 1, first_column: 38, last_column: 40 }, identifierChain: [{ name: 'id'}], database: 'bar', table: 'foo'}
+          ]
         }
       });
     });
 
-    it('should suggest columns for "UPDATE bar.foo SET bar = \'foo\' WHERE id = 1 AND |"', function() {
+
+
+    it('should suggest columns for "UPDATE bar.foo SET bla = \'foo\' WHERE id = 1 AND |"', function() {
       assertAutoComplete({
-        beforeCursor: 'UPDATE bar.foo SET bar = \'foo\' WHERE id = 1 AND ',
+        beforeCursor: 'UPDATE bar.foo SET bla = \'foo\' WHERE id = 1 AND ',
         afterCursor: '',
         expectedResult: {
           lowerCase: false,
@@ -200,7 +231,12 @@ define([
           suggestColumns: {
             database: 'bar',
             table: 'foo'
-          }
+          },
+          locations: [
+            {type: 'table', location: { first_line: 1, last_line: 1, first_column: 12, last_column: 15 }, database:'bar', table: 'foo'},
+            {type: 'column', location: { first_line: 1, last_line: 1, first_column: 20, last_column: 23 }, identifierChain: [{ name: 'bla'}], database: 'bar', table: 'foo'},
+            {type: 'column', location: { first_line: 1, last_line: 1, first_column: 38, last_column: 40 }, identifierChain: [{ name: 'id'}], database: 'bar', table: 'foo'}
+          ]
         }
       });
     });

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

@@ -44,6 +44,19 @@ define([
             if (testDefinition.ignoreErrors) {
               delete actualResponse.error;
             }
+            if (testDefinition.hasLocations) {
+              if (actualResponse.locations.length === 0) {
+                return {
+                  pass: false,
+                  message: '\nStatement: ' + testDefinition.beforeCursor + '|' + testDefinition.afterCursor + '\n' +
+                  '  Dialect: ' + testDefinition.dialect + '\n' +
+                  '           No locations found'
+                }
+              }
+            }
+            if (testDefinition.hasLocations || actualResponse.locations.length === 0) {
+              delete actualResponse.locations;
+            }
             var deleteKeywords = false;
             if (testDefinition.containsColRefKeywords) {
               if (typeof actualResponse.suggestColRefKeywords == 'undefined') {
@@ -117,8 +130,10 @@ define([
       var debug = false;
       if (typeof testDefinition.dialect === 'undefined') {
         expect(sql.parseSql(testDefinition.beforeCursor, testDefinition.afterCursor, testDefinition.dialect, sqlFunctions, debug)).toEqualDefinition(testDefinition);
-        expect(sql.parseSql(testDefinition.beforeCursor, testDefinition.afterCursor, 'hive', sqlFunctions, debug)).toEqualDefinition(testDefinition);
-        expect(sql.parseSql(testDefinition.beforeCursor, testDefinition.afterCursor, 'impala', sqlFunctions, debug)).toEqualDefinition(testDefinition);
+        testDefinition.dialect = 'hive';
+        expect(sql.parseSql(testDefinition.beforeCursor, testDefinition.afterCursor,  testDefinition.dialect, sqlFunctions, debug)).toEqualDefinition(testDefinition);
+        testDefinition.dialect = 'impala';
+        expect(sql.parseSql(testDefinition.beforeCursor, testDefinition.afterCursor,  testDefinition.dialect, sqlFunctions, debug)).toEqualDefinition(testDefinition);
       } else {
         expect(sql.parseSql(testDefinition.beforeCursor, testDefinition.afterCursor, testDefinition.dialect, sqlFunctions, debug)).toEqualDefinition(testDefinition);
       }

+ 0 - 2
desktop/core/src/desktop/static/desktop/spec/sqlFunctionsSpec.js

@@ -23,7 +23,6 @@ define([
 
       expect(completions.length).not.toEqual(0);
 
-      console.log(completions);
       var completionsWithCorrectType = completions.filter(function (completion) {
         return completion.meta === 'BOOLEAN' || completion.meta === 'T' || completion.meta === 'ARRAY' || completion.meta === 'MAP' || completion.meta === 'STRUCT';
       });
@@ -118,7 +117,6 @@ define([
     });
 
     it('should give the expected argument types at a specific position', function () {
-      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('impala', 'cos', 1)).toEqual(['DOUBLE']);

Some files were not shown because too many files changed in this diff