소스 검색

HUE-4437 [editor] The autocompleter should handle multiple db references in table primaries for Impala

Johan Ahlen 9 년 전
부모
커밋
1cef9b4c6c

+ 90 - 75
desktop/core/src/desktop/static/desktop/js/autocomplete/sql.jison

@@ -1462,9 +1462,6 @@ QuerySpecification_EDIT
    }
    }
  | 'SELECT' OptionalAllOrDistinct SelectList TableExpression_EDIT
  | 'SELECT' OptionalAllOrDistinct SelectList TableExpression_EDIT
  | 'SELECT' OptionalAllOrDistinct SelectList_EDIT error TableExpression
  | 'SELECT' OptionalAllOrDistinct SelectList_EDIT error TableExpression
-   {
-     console.log(1);
-   }
  | 'SELECT' OptionalAllOrDistinct SelectList_EDIT TableExpression
  | 'SELECT' OptionalAllOrDistinct SelectList_EDIT TableExpression
    {
    {
      if ($3.cursorAtStart) {
      if ($3.cursorAtStart) {
@@ -4223,7 +4220,7 @@ var prepareNewStatement = function () {
     parser.yy.errors.push(error);
     parser.yy.errors.push(error);
     return message;
     return message;
   };
   };
-}
+};
 
 
 var popQueryState = function (subQuery) {
 var popQueryState = function (subQuery) {
   linkTablePrimaries();
   linkTablePrimaries();
@@ -4245,15 +4242,15 @@ var popQueryState = function (subQuery) {
 
 
   parser.yy.latestTablePrimaries = parser.yy.primariesStack.pop();
   parser.yy.latestTablePrimaries = parser.yy.primariesStack.pop();
   parser.yy.locations = parser.yy.locationsStack.pop();
   parser.yy.locations = parser.yy.locationsStack.pop();
-}
+};
 
 
 var isHive = function () {
 var isHive = function () {
   return parser.yy.activeDialect === 'hive';
   return parser.yy.activeDialect === 'hive';
-}
+};
 
 
 var isImpala = function () {
 var isImpala = function () {
   return parser.yy.activeDialect === 'impala';
   return parser.yy.activeDialect === 'impala';
-}
+};
 
 
 var mergeSuggestKeywords = function() {
 var mergeSuggestKeywords = function() {
   var result = [];
   var result = [];
@@ -4266,7 +4263,7 @@ var mergeSuggestKeywords = function() {
     return { suggestKeywords: result };
     return { suggestKeywords: result };
   }
   }
   return {};
   return {};
-}
+};
 
 
 var suggestValueExpressionKeywords = function (valueExpression, extras) {
 var suggestValueExpressionKeywords = function (valueExpression, extras) {
   var expressionKeywords = getValueExpressionKeywords(valueExpression, extras)
   var expressionKeywords = getValueExpressionKeywords(valueExpression, extras)
@@ -4279,7 +4276,7 @@ var suggestValueExpressionKeywords = function (valueExpression, extras) {
   } else {
   } else {
     addColRefIfExists(valueExpression);
     addColRefIfExists(valueExpression);
   }
   }
-}
+};
 
 
 var getValueExpressionKeywords = function (valueExpression, extras) {
 var getValueExpressionKeywords = function (valueExpression, extras) {
   var types = valueExpression.lastType ? valueExpression.lastType.types : valueExpression.types;
   var types = valueExpression.lastType ? valueExpression.lastType.types : valueExpression.types;
@@ -4314,7 +4311,7 @@ var getValueExpressionKeywords = function (valueExpression, extras) {
     keywords = keywords.concat(['LIKE', 'NOT LIKE', 'REGEX', 'RLIKE']);
     keywords = keywords.concat(['LIKE', 'NOT LIKE', 'REGEX', 'RLIKE']);
   }
   }
   return { suggestKeywords: keywords };
   return { suggestKeywords: keywords };
-}
+};
 
 
 var suggestTypeKeywords = function () {
 var suggestTypeKeywords = function () {
   if (isHive()) {
   if (isHive()) {
@@ -4324,13 +4321,13 @@ var suggestTypeKeywords = function () {
   } else {
   } else {
     suggestKeywords(['BIGINT', 'BOOLEAN', 'CHAR', 'DECIMAL', 'DOUBLE', 'FLOAT', 'INT', 'SMALLINT', 'TIMESTAMP', 'STRING', 'TINYINT', 'VARCHAR']);
     suggestKeywords(['BIGINT', 'BOOLEAN', 'CHAR', 'DECIMAL', 'DOUBLE', 'FLOAT', 'INT', 'SMALLINT', 'TIMESTAMP', 'STRING', 'TINYINT', 'VARCHAR']);
   }
   }
-}
+};
 
 
 var addColRefIfExists = function (valueExpression) {
 var addColRefIfExists = function (valueExpression) {
   if (valueExpression.columnReference) {
   if (valueExpression.columnReference) {
     parser.yy.result.colRef = { identifierChain: valueExpression.columnReference };
     parser.yy.result.colRef = { identifierChain: valueExpression.columnReference };
   }
   }
-}
+};
 
 
 var valueExpressionSuggest = function (oppositeValueExpression) {
 var valueExpressionSuggest = function (oppositeValueExpression) {
   if (oppositeValueExpression && oppositeValueExpression.columnReference) {
   if (oppositeValueExpression && oppositeValueExpression.columnReference) {
@@ -4342,7 +4339,7 @@ var valueExpressionSuggest = function (oppositeValueExpression) {
   if (oppositeValueExpression && oppositeValueExpression.types[0] === 'NUMBER') {
   if (oppositeValueExpression && oppositeValueExpression.types[0] === 'NUMBER') {
     applyTypeToSuggestions(['NUMBER']);
     applyTypeToSuggestions(['NUMBER']);
   }
   }
-}
+};
 
 
 var applyTypeToSuggestions = function (types) {
 var applyTypeToSuggestions = function (types) {
   if (types[0] === 'BOOLEAN') {
   if (types[0] === 'BOOLEAN') {
@@ -4354,7 +4351,7 @@ var applyTypeToSuggestions = function (types) {
   if (parser.yy.result.suggestColumns) {
   if (parser.yy.result.suggestColumns) {
     parser.yy.result.suggestColumns.types = types;
     parser.yy.result.suggestColumns.types = types;
   }
   }
-}
+};
 
 
 var findCaseType = function (whenThenList) {
 var findCaseType = function (whenThenList) {
   var types = {};
   var types = {};
@@ -4367,12 +4364,12 @@ var findCaseType = function (whenThenList) {
     return { types: [Object.keys(types)[0]] };
     return { types: [Object.keys(types)[0]] };
   }
   }
   return { types: [ 'T' ] };
   return { types: [ 'T' ] };
-}
+};
 
 
 findReturnTypes = function (funcToken) {
 findReturnTypes = function (funcToken) {
   var funcName = funcToken.substring(0, funcToken.length - 1).toLowerCase();
   var funcName = funcToken.substring(0, funcToken.length - 1).toLowerCase();
   return parser.yy.sqlFunctions.getReturnTypes(parser.yy.activeDialect, funcName);
   return parser.yy.sqlFunctions.getReturnTypes(parser.yy.activeDialect, funcName);
-}
+};
 
 
 var applyArgumentTypesToSuggestions = function (funcToken, position) {
 var applyArgumentTypesToSuggestions = function (funcToken, position) {
   var funcName = funcToken.substring(0, funcToken.length - 1).toLowerCase();
   var funcName = funcToken.substring(0, funcToken.length - 1).toLowerCase();
@@ -4386,7 +4383,7 @@ var applyArgumentTypesToSuggestions = function (funcToken, position) {
   } else {
   } else {
     applyTypeToSuggestions(foundArguments);
     applyTypeToSuggestions(foundArguments);
   }
   }
-}
+};
 
 
 var commitLocations = function () {
 var commitLocations = function () {
   var i = parser.yy.locations.length;
   var i = parser.yy.locations.length;
@@ -4395,9 +4392,12 @@ var commitLocations = function () {
     expandIdentifierChain(location);
     expandIdentifierChain(location);
     // Impala can have references to previous tables after FROM, i.e. FROM testTable t, t.testArray
     // 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.
     // 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) {
+    if (location.type === 'table' && location.table && typeof location.identifierChain !== 'undefined' && location.identifierChain.length > 0) {
       location.type = 'column';
       location.type = 'column';
     }
     }
+    if (location.type === 'table' && typeof location.table === 'undefined') {
+      parser.yy.locations.splice(i, 1);
+    }
     if (location.type === 'column' && (typeof location.table === 'undefined' || typeof location.identifierChain === 'undefined')) {
     if (location.type === 'column' && (typeof location.table === 'undefined' || typeof location.identifierChain === 'undefined')) {
       parser.yy.locations.splice(i, 1);
       parser.yy.locations.splice(i, 1);
     }
     }
@@ -4455,7 +4455,7 @@ var prioritizeSuggestions = function () {
   } else {
   } else {
     delete parser.yy.result.subQueries;
     delete parser.yy.result.subQueries;
   }
   }
-}
+};
 
 
 /**
 /**
  * Impala supports referencing maps and arrays in the the table reference list i.e.
  * Impala supports referencing maps and arrays in the the table reference list i.e.
@@ -4475,35 +4475,44 @@ var prioritizeSuggestions = function () {
  *
  *
  * [ { name: 't' }, { name: 'someMap', keySet: true }, { name: 'bar' } ]
  * [ { name: 't' }, { name: 'someMap', keySet: true }, { name: 'bar' } ]
  */
  */
-parser.expandImpalaIdentifierChain = function (tablePrimaries, originalIdentifierChain) {
-  var identifierChain = originalIdentifierChain.concat(); // Clone in case it's called multiple times.
-  if (typeof identifierChain === 'undefined' || identifierChain.length === 0) {
+parser.expandImpalaIdentifierChain = function (tablePrimaries, identifierChain) {
+  var expandedChain = identifierChain.concat(); // Clone in case it's called multiple times.
+  if (typeof expandedChain === 'undefined' || expandedChain.length === 0) {
     return identifierChain;
     return identifierChain;
   }
   }
-  var firstIdentifier = identifierChain[0].name;
 
 
-  var foundPrimary = tablePrimaries.filter(function (tablePrimary) {
-    return tablePrimary.alias === firstIdentifier;
-  });
+  var expand = function (identifier, expandedChain) {
+    var foundPrimary = tablePrimaries.filter(function (tablePrimary) {
+      return tablePrimary.alias === identifier;
+    });
 
 
-  if (foundPrimary.length === 1 && foundPrimary[0].identifierChain) {
-    var firstPart = foundPrimary[0].identifierChain.concat();
-    var secondPart = identifierChain.slice(1);
-    var lastFromFirst = firstPart.pop();
-    if (typeof identifierChain[0].keySet !== 'undefined') {
-      firstPart.push({
-        name: lastFromFirst.name,
-        keySet: identifierChain[0].keySet
-      });
-    } else {
-      firstPart.push({
-        name: lastFromFirst.name
+    if (foundPrimary.length === 1 && foundPrimary[0].identifierChain) {
+      var parentPrimary = tablePrimaries.filter(function (tablePrimary) {
+        return tablePrimary.alias === foundPrimary[0].identifierChain[0].name;
       });
       });
-    }
-    return firstPart.concat(secondPart);
-  }
+      if (parentPrimary.length === 1) {
+        var keySet = expandedChain[0].keySet;
+        var secondPart = expandedChain.slice(1);
+        var firstPart = [];
+        // Clone to make sure we don't add keySet to the primaries
+        foundPrimary[0].identifierChain.forEach(function (identifier) {
+          firstPart.push({ name: identifier.name });
+        });
+        if (keySet && firstPart.length > 0) {
+          firstPart[firstPart.length - 1].keySet = true;
+        }
 
 
-  return identifierChain;
+        var result = firstPart.concat(secondPart);
+        if (result.length > 0) {
+          return expand(firstPart[0].name, result);
+        } else {
+          return result;
+        }
+      }
+    }
+    return expandedChain;
+  };
+  return expand(expandedChain[0].name, expandedChain);
 };
 };
 
 
 parser.identifyPartials = function (beforeCursor, afterCursor) {
 parser.identifyPartials = function (beforeCursor, afterCursor) {
@@ -4604,15 +4613,21 @@ var expandIdentifierChain = function (wrapper, isColumnSuggestion) {
 
 
     var dbAndTable = false;
     var dbAndTable = false;
     if (foundTable.length === 0) {
     if (foundTable.length === 0) {
-      foundTable = tablePrimaries.filter(function (tablePrimary) {
-        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;
-          }
-          return true;
-        }
-        return false;
-      });
+      // Give priority to the ones that match both DB and table
+      if (identifierChain.length > 1) {
+        foundTable = tablePrimaries.filter(function (tablePrimary) {
+          return tablePrimary.identifierChain && tablePrimary.identifierChain.length > 1 &&
+              tablePrimary.identifierChain[0].name === identifierChain[0].name &&
+              tablePrimary.identifierChain[1].name === identifierChain[1].name;
+        });
+        dbAndTable = foundTable.length > 0;
+      }
+      if (foundTable.length == 0) {
+        foundTable = tablePrimaries.filter(function (tablePrimary) {
+          return tablePrimary.identifierChain && tablePrimary.identifierChain.length > 0 &&
+              tablePrimary.identifierChain[0].name === identifierChain[0].name;
+        });
+      }
     }
     }
 
 
     if (foundTable.length === 1) {
     if (foundTable.length === 1) {
@@ -4642,7 +4657,7 @@ var expandIdentifierChain = function (wrapper, isColumnSuggestion) {
     }
     }
   }
   }
   wrapper.linked = true;
   wrapper.linked = true;
-}
+};
 
 
 var suggestTablePrimariesAsIdentifiers = function () {
 var suggestTablePrimariesAsIdentifiers = function () {
   if (typeof parser.yy.result.suggestIdentifiers === 'undefined') {
   if (typeof parser.yy.result.suggestIdentifiers === 'undefined') {
@@ -4662,7 +4677,7 @@ var suggestTablePrimariesAsIdentifiers = function () {
   if (parser.yy.result.suggestIdentifiers.length === 0) {
   if (parser.yy.result.suggestIdentifiers.length === 0) {
     delete parser.yy.result.suggestIdentifiers;
     delete parser.yy.result.suggestIdentifiers;
   }
   }
-}
+};
 
 
 var suggestLateralViewAliasesAsIdentifiers = function () {
 var suggestLateralViewAliasesAsIdentifiers = function () {
   if (typeof parser.yy.result.suggestIdentifiers === 'undefined') {
   if (typeof parser.yy.result.suggestIdentifiers === 'undefined') {
@@ -4714,7 +4729,7 @@ var linkTablePrimaries = function () {
   if (typeof parser.yy.result.suggestKeyValues !== 'undefined' && !parser.yy.result.suggestKeyValues.linked) {
   if (typeof parser.yy.result.suggestKeyValues !== 'undefined' && !parser.yy.result.suggestKeyValues.linked) {
     expandIdentifierChain(parser.yy.result.suggestKeyValues);
     expandIdentifierChain(parser.yy.result.suggestKeyValues);
   }
   }
-}
+};
 
 
 var getSubQuery = function (cols) {
 var getSubQuery = function (cols) {
   var columns = [];
   var columns = [];
@@ -4738,18 +4753,18 @@ var getSubQuery = function (cols) {
   return {
   return {
     columns: columns
     columns: columns
   };
   };
-}
+};
 
 
 var addTablePrimary = function (ref) {
 var addTablePrimary = function (ref) {
   if (typeof parser.yy.latestTablePrimaries === 'undefined') {
   if (typeof parser.yy.latestTablePrimaries === 'undefined') {
     parser.yy.latestTablePrimaries = [];
     parser.yy.latestTablePrimaries = [];
   }
   }
   parser.yy.latestTablePrimaries.push(ref);
   parser.yy.latestTablePrimaries.push(ref);
-}
+};
 
 
 var suggestNumbers = function (numbers) {
 var suggestNumbers = function (numbers) {
   parser.yy.result.suggestNumbers = numbers;
   parser.yy.result.suggestNumbers = numbers;
-}
+};
 
 
 var suggestDdlAndDmlKeywords = function () {
 var suggestDdlAndDmlKeywords = function () {
   var keywords = ['ALTER', 'CREATE', 'DELETE', 'DESCRIBE', 'DROP', 'EXPLAIN', 'INSERT', 'REVOKE', 'SELECT', 'SET', 'SHOW', 'TRUNCATE', 'UPDATE', 'USE'];
   var keywords = ['ALTER', 'CREATE', 'DELETE', 'DESCRIBE', 'DROP', 'EXPLAIN', 'INSERT', 'REVOKE', 'SELECT', 'SET', 'SHOW', 'TRUNCATE', 'UPDATE', 'USE'];
@@ -4763,7 +4778,7 @@ var suggestDdlAndDmlKeywords = function () {
   }
   }
 
 
   suggestKeywords(keywords);
   suggestKeywords(keywords);
-}
+};
 
 
 var checkForSelectListKeywords = function (selectList) {
 var checkForSelectListKeywords = function (selectList) {
   if (selectList.length === 0) {
   if (selectList.length === 0) {
@@ -4800,15 +4815,15 @@ var checkForKeywords = function (expression) {
       addColRefIfExists(expression);
       addColRefIfExists(expression);
     }
     }
   }
   }
-}
+};
 
 
 var suggestKeywords = function (keywords) {
 var suggestKeywords = function (keywords) {
   parser.yy.result.suggestKeywords = keywords.sort();
   parser.yy.result.suggestKeywords = keywords.sort();
-}
+};
 
 
 var suggestColRefKeywords = function (colRefKeywords) {
 var suggestColRefKeywords = function (colRefKeywords) {
   parser.yy.result.suggestColRefKeywords = colRefKeywords;
   parser.yy.result.suggestColRefKeywords = colRefKeywords;
-}
+};
 
 
 var suggestTablesOrColumns = function (identifier) {
 var suggestTablesOrColumns = function (identifier) {
   if (typeof parser.yy.latestTablePrimaries == 'undefined') {
   if (typeof parser.yy.latestTablePrimaries == 'undefined') {
@@ -4823,15 +4838,15 @@ var suggestTablesOrColumns = function (identifier) {
   } else {
   } else {
     suggestTables({ database: identifier });
     suggestTables({ database: identifier });
   }
   }
-}
+};
 
 
 var suggestFunctions = function (details) {
 var suggestFunctions = function (details) {
   parser.yy.result.suggestFunctions = details || {};
   parser.yy.result.suggestFunctions = details || {};
-}
+};
 
 
 var suggestAggregateFunctions = function () {
 var suggestAggregateFunctions = function () {
   parser.yy.result.suggestAggregateFunctions = true;
   parser.yy.result.suggestAggregateFunctions = true;
-}
+};
 
 
 var suggestColumns = function (details) {
 var suggestColumns = function (details) {
   if (typeof details === 'undefined') {
   if (typeof details === 'undefined') {
@@ -4840,15 +4855,15 @@ var suggestColumns = function (details) {
     details.identifierChain = [];
     details.identifierChain = [];
   }
   }
   parser.yy.result.suggestColumns = details;
   parser.yy.result.suggestColumns = details;
-}
+};
 
 
 var suggestKeyValues = function (details) {
 var suggestKeyValues = function (details) {
   parser.yy.result.suggestKeyValues = details || {};
   parser.yy.result.suggestKeyValues = details || {};
-}
+};
 
 
 var suggestTables = function (details) {
 var suggestTables = function (details) {
   parser.yy.result.suggestTables = details || {};
   parser.yy.result.suggestTables = details || {};
-}
+};
 
 
 var adjustLocationForCursor = function (location) {
 var adjustLocationForCursor = function (location) {
    // columns are 0-based and lines not, so add 1 to cols
    // columns are 0-based and lines not, so add 1 to cols
@@ -4878,31 +4893,31 @@ var addFunctionLocation = function (location, functionName) {
     last_column: location.last_column - 1
     last_column: location.last_column - 1
   }
   }
   parser.yy.locations.push({ type: 'function', location: adjustLocationForCursor(adjustedLocation), function: functionName.toLowerCase() });
   parser.yy.locations.push({ type: 'function', location: adjustLocationForCursor(adjustedLocation), function: functionName.toLowerCase() });
-}
+};
 
 
 var addDatabaseLocation = function (location, database) {
 var addDatabaseLocation = function (location, database) {
   parser.yy.locations.push({ type: 'database', location: adjustLocationForCursor(location), database: database });
   parser.yy.locations.push({ type: 'database', location: adjustLocationForCursor(location), database: database });
-}
+};
 
 
 var addTableLocation = function (location, identifierChain) {
 var addTableLocation = function (location, identifierChain) {
   parser.yy.locations.push({ type: 'table', location: adjustLocationForCursor(location), identifierChain: identifierChain });
   parser.yy.locations.push({ type: 'table', location: adjustLocationForCursor(location), identifierChain: identifierChain });
-}
+};
 
 
 var addColumnLocation = function (location, identifierChain) {
 var addColumnLocation = function (location, identifierChain) {
   parser.yy.locations.push({ type: 'column', location: adjustLocationForCursor(location), identifierChain: identifierChain });
   parser.yy.locations.push({ type: 'column', location: adjustLocationForCursor(location), identifierChain: identifierChain });
-}
+};
 
 
 var suggestDatabases = function (details) {
 var suggestDatabases = function (details) {
   parser.yy.result.suggestDatabases = details || {};
   parser.yy.result.suggestDatabases = details || {};
-}
+};
 
 
 var suggestHdfs = function (details) {
 var suggestHdfs = function (details) {
   parser.yy.result.suggestHdfs = details || {};
   parser.yy.result.suggestHdfs = details || {};
-}
+};
 
 
 var suggestValues = function (details) {
 var suggestValues = function (details) {
   parser.yy.result.suggestValues = true;
   parser.yy.result.suggestValues = true;
-}
+};
 
 
 var determineCase = function (text) {
 var determineCase = function (text) {
   parser.yy.lowerCase = text.toLowerCase() === text;
   parser.yy.lowerCase = text.toLowerCase() === text;
@@ -5022,4 +5037,4 @@ parser.parseSql = function(beforeCursor, afterCursor, dialect, sqlFunctions, deb
   }
   }
 
 
   return result;
   return result;
-}
+};

+ 90 - 77
desktop/core/src/desktop/static/desktop/js/autocomplete/sql.js

@@ -500,11 +500,6 @@ case 532:
      suggestTables({ prependQuestionMark: true, prependFrom: true });
      suggestTables({ prependQuestionMark: true, prependFrom: true });
      suggestDatabases({ prependQuestionMark: true, prependFrom: true, appendDot: true });
      suggestDatabases({ prependQuestionMark: true, prependFrom: true, appendDot: true });
    
    
-break;
-case 534:
-
-     console.log(1);
-   
 break;
 break;
 case 535:
 case 535:
 
 
@@ -2268,7 +2263,7 @@ var prepareNewStatement = function () {
     parser.yy.errors.push(error);
     parser.yy.errors.push(error);
     return message;
     return message;
   };
   };
-}
+};
 
 
 var popQueryState = function (subQuery) {
 var popQueryState = function (subQuery) {
   linkTablePrimaries();
   linkTablePrimaries();
@@ -2290,15 +2285,15 @@ var popQueryState = function (subQuery) {
 
 
   parser.yy.latestTablePrimaries = parser.yy.primariesStack.pop();
   parser.yy.latestTablePrimaries = parser.yy.primariesStack.pop();
   parser.yy.locations = parser.yy.locationsStack.pop();
   parser.yy.locations = parser.yy.locationsStack.pop();
-}
+};
 
 
 var isHive = function () {
 var isHive = function () {
   return parser.yy.activeDialect === 'hive';
   return parser.yy.activeDialect === 'hive';
-}
+};
 
 
 var isImpala = function () {
 var isImpala = function () {
   return parser.yy.activeDialect === 'impala';
   return parser.yy.activeDialect === 'impala';
-}
+};
 
 
 var mergeSuggestKeywords = function() {
 var mergeSuggestKeywords = function() {
   var result = [];
   var result = [];
@@ -2311,7 +2306,7 @@ var mergeSuggestKeywords = function() {
     return { suggestKeywords: result };
     return { suggestKeywords: result };
   }
   }
   return {};
   return {};
-}
+};
 
 
 var suggestValueExpressionKeywords = function (valueExpression, extras) {
 var suggestValueExpressionKeywords = function (valueExpression, extras) {
   var expressionKeywords = getValueExpressionKeywords(valueExpression, extras)
   var expressionKeywords = getValueExpressionKeywords(valueExpression, extras)
@@ -2324,7 +2319,7 @@ var suggestValueExpressionKeywords = function (valueExpression, extras) {
   } else {
   } else {
     addColRefIfExists(valueExpression);
     addColRefIfExists(valueExpression);
   }
   }
-}
+};
 
 
 var getValueExpressionKeywords = function (valueExpression, extras) {
 var getValueExpressionKeywords = function (valueExpression, extras) {
   var types = valueExpression.lastType ? valueExpression.lastType.types : valueExpression.types;
   var types = valueExpression.lastType ? valueExpression.lastType.types : valueExpression.types;
@@ -2359,7 +2354,7 @@ var getValueExpressionKeywords = function (valueExpression, extras) {
     keywords = keywords.concat(['LIKE', 'NOT LIKE', 'REGEX', 'RLIKE']);
     keywords = keywords.concat(['LIKE', 'NOT LIKE', 'REGEX', 'RLIKE']);
   }
   }
   return { suggestKeywords: keywords };
   return { suggestKeywords: keywords };
-}
+};
 
 
 var suggestTypeKeywords = function () {
 var suggestTypeKeywords = function () {
   if (isHive()) {
   if (isHive()) {
@@ -2369,13 +2364,13 @@ var suggestTypeKeywords = function () {
   } else {
   } else {
     suggestKeywords(['BIGINT', 'BOOLEAN', 'CHAR', 'DECIMAL', 'DOUBLE', 'FLOAT', 'INT', 'SMALLINT', 'TIMESTAMP', 'STRING', 'TINYINT', 'VARCHAR']);
     suggestKeywords(['BIGINT', 'BOOLEAN', 'CHAR', 'DECIMAL', 'DOUBLE', 'FLOAT', 'INT', 'SMALLINT', 'TIMESTAMP', 'STRING', 'TINYINT', 'VARCHAR']);
   }
   }
-}
+};
 
 
 var addColRefIfExists = function (valueExpression) {
 var addColRefIfExists = function (valueExpression) {
   if (valueExpression.columnReference) {
   if (valueExpression.columnReference) {
     parser.yy.result.colRef = { identifierChain: valueExpression.columnReference };
     parser.yy.result.colRef = { identifierChain: valueExpression.columnReference };
   }
   }
-}
+};
 
 
 var valueExpressionSuggest = function (oppositeValueExpression) {
 var valueExpressionSuggest = function (oppositeValueExpression) {
   if (oppositeValueExpression && oppositeValueExpression.columnReference) {
   if (oppositeValueExpression && oppositeValueExpression.columnReference) {
@@ -2387,7 +2382,7 @@ var valueExpressionSuggest = function (oppositeValueExpression) {
   if (oppositeValueExpression && oppositeValueExpression.types[0] === 'NUMBER') {
   if (oppositeValueExpression && oppositeValueExpression.types[0] === 'NUMBER') {
     applyTypeToSuggestions(['NUMBER']);
     applyTypeToSuggestions(['NUMBER']);
   }
   }
-}
+};
 
 
 var applyTypeToSuggestions = function (types) {
 var applyTypeToSuggestions = function (types) {
   if (types[0] === 'BOOLEAN') {
   if (types[0] === 'BOOLEAN') {
@@ -2399,7 +2394,7 @@ var applyTypeToSuggestions = function (types) {
   if (parser.yy.result.suggestColumns) {
   if (parser.yy.result.suggestColumns) {
     parser.yy.result.suggestColumns.types = types;
     parser.yy.result.suggestColumns.types = types;
   }
   }
-}
+};
 
 
 var findCaseType = function (whenThenList) {
 var findCaseType = function (whenThenList) {
   var types = {};
   var types = {};
@@ -2412,12 +2407,12 @@ var findCaseType = function (whenThenList) {
     return { types: [Object.keys(types)[0]] };
     return { types: [Object.keys(types)[0]] };
   }
   }
   return { types: [ 'T' ] };
   return { types: [ 'T' ] };
-}
+};
 
 
 findReturnTypes = function (funcToken) {
 findReturnTypes = function (funcToken) {
   var funcName = funcToken.substring(0, funcToken.length - 1).toLowerCase();
   var funcName = funcToken.substring(0, funcToken.length - 1).toLowerCase();
   return parser.yy.sqlFunctions.getReturnTypes(parser.yy.activeDialect, funcName);
   return parser.yy.sqlFunctions.getReturnTypes(parser.yy.activeDialect, funcName);
-}
+};
 
 
 var applyArgumentTypesToSuggestions = function (funcToken, position) {
 var applyArgumentTypesToSuggestions = function (funcToken, position) {
   var funcName = funcToken.substring(0, funcToken.length - 1).toLowerCase();
   var funcName = funcToken.substring(0, funcToken.length - 1).toLowerCase();
@@ -2431,7 +2426,7 @@ var applyArgumentTypesToSuggestions = function (funcToken, position) {
   } else {
   } else {
     applyTypeToSuggestions(foundArguments);
     applyTypeToSuggestions(foundArguments);
   }
   }
-}
+};
 
 
 var commitLocations = function () {
 var commitLocations = function () {
   var i = parser.yy.locations.length;
   var i = parser.yy.locations.length;
@@ -2440,9 +2435,12 @@ var commitLocations = function () {
     expandIdentifierChain(location);
     expandIdentifierChain(location);
     // Impala can have references to previous tables after FROM, i.e. FROM testTable t, t.testArray
     // 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.
     // 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) {
+    if (location.type === 'table' && location.table && typeof location.identifierChain !== 'undefined' && location.identifierChain.length > 0) {
       location.type = 'column';
       location.type = 'column';
     }
     }
+    if (location.type === 'table' && typeof location.table === 'undefined') {
+      parser.yy.locations.splice(i, 1);
+    }
     if (location.type === 'column' && (typeof location.table === 'undefined' || typeof location.identifierChain === 'undefined')) {
     if (location.type === 'column' && (typeof location.table === 'undefined' || typeof location.identifierChain === 'undefined')) {
       parser.yy.locations.splice(i, 1);
       parser.yy.locations.splice(i, 1);
     }
     }
@@ -2500,7 +2498,7 @@ var prioritizeSuggestions = function () {
   } else {
   } else {
     delete parser.yy.result.subQueries;
     delete parser.yy.result.subQueries;
   }
   }
-}
+};
 
 
 /**
 /**
  * Impala supports referencing maps and arrays in the the table reference list i.e.
  * Impala supports referencing maps and arrays in the the table reference list i.e.
@@ -2520,35 +2518,44 @@ var prioritizeSuggestions = function () {
  *
  *
  * [ { name: 't' }, { name: 'someMap', keySet: true }, { name: 'bar' } ]
  * [ { name: 't' }, { name: 'someMap', keySet: true }, { name: 'bar' } ]
  */
  */
-parser.expandImpalaIdentifierChain = function (tablePrimaries, originalIdentifierChain) {
-  var identifierChain = originalIdentifierChain.concat(); // Clone in case it's called multiple times.
-  if (typeof identifierChain === 'undefined' || identifierChain.length === 0) {
+parser.expandImpalaIdentifierChain = function (tablePrimaries, identifierChain) {
+  var expandedChain = identifierChain.concat(); // Clone in case it's called multiple times.
+  if (typeof expandedChain === 'undefined' || expandedChain.length === 0) {
     return identifierChain;
     return identifierChain;
   }
   }
-  var firstIdentifier = identifierChain[0].name;
 
 
-  var foundPrimary = tablePrimaries.filter(function (tablePrimary) {
-    return tablePrimary.alias === firstIdentifier;
-  });
+  var expand = function (identifier, expandedChain) {
+    var foundPrimary = tablePrimaries.filter(function (tablePrimary) {
+      return tablePrimary.alias === identifier;
+    });
 
 
-  if (foundPrimary.length === 1 && foundPrimary[0].identifierChain) {
-    var firstPart = foundPrimary[0].identifierChain.concat();
-    var secondPart = identifierChain.slice(1);
-    var lastFromFirst = firstPart.pop();
-    if (typeof identifierChain[0].keySet !== 'undefined') {
-      firstPart.push({
-        name: lastFromFirst.name,
-        keySet: identifierChain[0].keySet
+    if (foundPrimary.length === 1 && foundPrimary[0].identifierChain) {
+      var parentPrimary = tablePrimaries.filter(function (tablePrimary) {
+        return tablePrimary.alias === foundPrimary[0].identifierChain[0].name;
       });
       });
-    } else {
-      firstPart.push({
-        name: lastFromFirst.name
-      });
-    }
-    return firstPart.concat(secondPart);
-  }
+      if (parentPrimary.length === 1) {
+        var keySet = expandedChain[0].keySet;
+        var secondPart = expandedChain.slice(1);
+        var firstPart = [];
+        // Clone to make sure we don't add keySet to the primaries
+        foundPrimary[0].identifierChain.forEach(function (identifier) {
+          firstPart.push({ name: identifier.name });
+        });
+        if (keySet && firstPart.length > 0) {
+          firstPart[firstPart.length - 1].keySet = true;
+        }
 
 
-  return identifierChain;
+        var result = firstPart.concat(secondPart);
+        if (result.length > 0) {
+          return expand(firstPart[0].name, result);
+        } else {
+          return result;
+        }
+      }
+    }
+    return expandedChain;
+  };
+  return expand(expandedChain[0].name, expandedChain);
 };
 };
 
 
 parser.identifyPartials = function (beforeCursor, afterCursor) {
 parser.identifyPartials = function (beforeCursor, afterCursor) {
@@ -2649,15 +2656,21 @@ var expandIdentifierChain = function (wrapper, isColumnSuggestion) {
 
 
     var dbAndTable = false;
     var dbAndTable = false;
     if (foundTable.length === 0) {
     if (foundTable.length === 0) {
-      foundTable = tablePrimaries.filter(function (tablePrimary) {
-        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;
-          }
-          return true;
-        }
-        return false;
-      });
+      // Give priority to the ones that match both DB and table
+      if (identifierChain.length > 1) {
+        foundTable = tablePrimaries.filter(function (tablePrimary) {
+          return tablePrimary.identifierChain && tablePrimary.identifierChain.length > 1 &&
+              tablePrimary.identifierChain[0].name === identifierChain[0].name &&
+              tablePrimary.identifierChain[1].name === identifierChain[1].name;
+        });
+        dbAndTable = foundTable.length > 0;
+      }
+      if (foundTable.length == 0) {
+        foundTable = tablePrimaries.filter(function (tablePrimary) {
+          return tablePrimary.identifierChain && tablePrimary.identifierChain.length > 0 &&
+              tablePrimary.identifierChain[0].name === identifierChain[0].name;
+        });
+      }
     }
     }
 
 
     if (foundTable.length === 1) {
     if (foundTable.length === 1) {
@@ -2687,7 +2700,7 @@ var expandIdentifierChain = function (wrapper, isColumnSuggestion) {
     }
     }
   }
   }
   wrapper.linked = true;
   wrapper.linked = true;
-}
+};
 
 
 var suggestTablePrimariesAsIdentifiers = function () {
 var suggestTablePrimariesAsIdentifiers = function () {
   if (typeof parser.yy.result.suggestIdentifiers === 'undefined') {
   if (typeof parser.yy.result.suggestIdentifiers === 'undefined') {
@@ -2707,7 +2720,7 @@ var suggestTablePrimariesAsIdentifiers = function () {
   if (parser.yy.result.suggestIdentifiers.length === 0) {
   if (parser.yy.result.suggestIdentifiers.length === 0) {
     delete parser.yy.result.suggestIdentifiers;
     delete parser.yy.result.suggestIdentifiers;
   }
   }
-}
+};
 
 
 var suggestLateralViewAliasesAsIdentifiers = function () {
 var suggestLateralViewAliasesAsIdentifiers = function () {
   if (typeof parser.yy.result.suggestIdentifiers === 'undefined') {
   if (typeof parser.yy.result.suggestIdentifiers === 'undefined') {
@@ -2759,7 +2772,7 @@ var linkTablePrimaries = function () {
   if (typeof parser.yy.result.suggestKeyValues !== 'undefined' && !parser.yy.result.suggestKeyValues.linked) {
   if (typeof parser.yy.result.suggestKeyValues !== 'undefined' && !parser.yy.result.suggestKeyValues.linked) {
     expandIdentifierChain(parser.yy.result.suggestKeyValues);
     expandIdentifierChain(parser.yy.result.suggestKeyValues);
   }
   }
-}
+};
 
 
 var getSubQuery = function (cols) {
 var getSubQuery = function (cols) {
   var columns = [];
   var columns = [];
@@ -2783,18 +2796,18 @@ var getSubQuery = function (cols) {
   return {
   return {
     columns: columns
     columns: columns
   };
   };
-}
+};
 
 
 var addTablePrimary = function (ref) {
 var addTablePrimary = function (ref) {
   if (typeof parser.yy.latestTablePrimaries === 'undefined') {
   if (typeof parser.yy.latestTablePrimaries === 'undefined') {
     parser.yy.latestTablePrimaries = [];
     parser.yy.latestTablePrimaries = [];
   }
   }
   parser.yy.latestTablePrimaries.push(ref);
   parser.yy.latestTablePrimaries.push(ref);
-}
+};
 
 
 var suggestNumbers = function (numbers) {
 var suggestNumbers = function (numbers) {
   parser.yy.result.suggestNumbers = numbers;
   parser.yy.result.suggestNumbers = numbers;
-}
+};
 
 
 var suggestDdlAndDmlKeywords = function () {
 var suggestDdlAndDmlKeywords = function () {
   var keywords = ['ALTER', 'CREATE', 'DELETE', 'DESCRIBE', 'DROP', 'EXPLAIN', 'INSERT', 'REVOKE', 'SELECT', 'SET', 'SHOW', 'TRUNCATE', 'UPDATE', 'USE'];
   var keywords = ['ALTER', 'CREATE', 'DELETE', 'DESCRIBE', 'DROP', 'EXPLAIN', 'INSERT', 'REVOKE', 'SELECT', 'SET', 'SHOW', 'TRUNCATE', 'UPDATE', 'USE'];
@@ -2808,7 +2821,7 @@ var suggestDdlAndDmlKeywords = function () {
   }
   }
 
 
   suggestKeywords(keywords);
   suggestKeywords(keywords);
-}
+};
 
 
 var checkForSelectListKeywords = function (selectList) {
 var checkForSelectListKeywords = function (selectList) {
   if (selectList.length === 0) {
   if (selectList.length === 0) {
@@ -2845,15 +2858,15 @@ var checkForKeywords = function (expression) {
       addColRefIfExists(expression);
       addColRefIfExists(expression);
     }
     }
   }
   }
-}
+};
 
 
 var suggestKeywords = function (keywords) {
 var suggestKeywords = function (keywords) {
   parser.yy.result.suggestKeywords = keywords.sort();
   parser.yy.result.suggestKeywords = keywords.sort();
-}
+};
 
 
 var suggestColRefKeywords = function (colRefKeywords) {
 var suggestColRefKeywords = function (colRefKeywords) {
   parser.yy.result.suggestColRefKeywords = colRefKeywords;
   parser.yy.result.suggestColRefKeywords = colRefKeywords;
-}
+};
 
 
 var suggestTablesOrColumns = function (identifier) {
 var suggestTablesOrColumns = function (identifier) {
   if (typeof parser.yy.latestTablePrimaries == 'undefined') {
   if (typeof parser.yy.latestTablePrimaries == 'undefined') {
@@ -2868,15 +2881,15 @@ var suggestTablesOrColumns = function (identifier) {
   } else {
   } else {
     suggestTables({ database: identifier });
     suggestTables({ database: identifier });
   }
   }
-}
+};
 
 
 var suggestFunctions = function (details) {
 var suggestFunctions = function (details) {
   parser.yy.result.suggestFunctions = details || {};
   parser.yy.result.suggestFunctions = details || {};
-}
+};
 
 
 var suggestAggregateFunctions = function () {
 var suggestAggregateFunctions = function () {
   parser.yy.result.suggestAggregateFunctions = true;
   parser.yy.result.suggestAggregateFunctions = true;
-}
+};
 
 
 var suggestColumns = function (details) {
 var suggestColumns = function (details) {
   if (typeof details === 'undefined') {
   if (typeof details === 'undefined') {
@@ -2885,15 +2898,15 @@ var suggestColumns = function (details) {
     details.identifierChain = [];
     details.identifierChain = [];
   }
   }
   parser.yy.result.suggestColumns = details;
   parser.yy.result.suggestColumns = details;
-}
+};
 
 
 var suggestKeyValues = function (details) {
 var suggestKeyValues = function (details) {
   parser.yy.result.suggestKeyValues = details || {};
   parser.yy.result.suggestKeyValues = details || {};
-}
+};
 
 
 var suggestTables = function (details) {
 var suggestTables = function (details) {
   parser.yy.result.suggestTables = details || {};
   parser.yy.result.suggestTables = details || {};
-}
+};
 
 
 var adjustLocationForCursor = function (location) {
 var adjustLocationForCursor = function (location) {
    // columns are 0-based and lines not, so add 1 to cols
    // columns are 0-based and lines not, so add 1 to cols
@@ -2923,31 +2936,31 @@ var addFunctionLocation = function (location, functionName) {
     last_column: location.last_column - 1
     last_column: location.last_column - 1
   }
   }
   parser.yy.locations.push({ type: 'function', location: adjustLocationForCursor(adjustedLocation), function: functionName.toLowerCase() });
   parser.yy.locations.push({ type: 'function', location: adjustLocationForCursor(adjustedLocation), function: functionName.toLowerCase() });
-}
+};
 
 
 var addDatabaseLocation = function (location, database) {
 var addDatabaseLocation = function (location, database) {
   parser.yy.locations.push({ type: 'database', location: adjustLocationForCursor(location), database: database });
   parser.yy.locations.push({ type: 'database', location: adjustLocationForCursor(location), database: database });
-}
+};
 
 
 var addTableLocation = function (location, identifierChain) {
 var addTableLocation = function (location, identifierChain) {
   parser.yy.locations.push({ type: 'table', location: adjustLocationForCursor(location), identifierChain: identifierChain });
   parser.yy.locations.push({ type: 'table', location: adjustLocationForCursor(location), identifierChain: identifierChain });
-}
+};
 
 
 var addColumnLocation = function (location, identifierChain) {
 var addColumnLocation = function (location, identifierChain) {
   parser.yy.locations.push({ type: 'column', location: adjustLocationForCursor(location), identifierChain: identifierChain });
   parser.yy.locations.push({ type: 'column', location: adjustLocationForCursor(location), identifierChain: identifierChain });
-}
+};
 
 
 var suggestDatabases = function (details) {
 var suggestDatabases = function (details) {
   parser.yy.result.suggestDatabases = details || {};
   parser.yy.result.suggestDatabases = details || {};
-}
+};
 
 
 var suggestHdfs = function (details) {
 var suggestHdfs = function (details) {
   parser.yy.result.suggestHdfs = details || {};
   parser.yy.result.suggestHdfs = details || {};
-}
+};
 
 
 var suggestValues = function (details) {
 var suggestValues = function (details) {
   parser.yy.result.suggestValues = true;
   parser.yy.result.suggestValues = true;
-}
+};
 
 
 var determineCase = function (text) {
 var determineCase = function (text) {
   parser.yy.lowerCase = text.toLowerCase() === text;
   parser.yy.lowerCase = text.toLowerCase() === text;
@@ -3067,7 +3080,7 @@ parser.parseSql = function(beforeCursor, afterCursor, dialect, sqlFunctions, deb
   }
   }
 
 
   return result;
   return result;
-}
+};
 /* generated by jison-lex 0.3.4 */
 /* generated by jison-lex 0.3.4 */
 var lexer = (function(){
 var lexer = (function(){
 var lexer = ({
 var lexer = ({

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

@@ -515,7 +515,7 @@ define([
         expect(actual).toEqual([{ name: 't' }, { name: 'testMap' }]);
         expect(actual).toEqual([{ name: 't' }, { name: 'testMap' }]);
       });
       });
 
 
-      it('should expand without map reference', function () {
+      it('should not expand without map reference', function () {
         var tablePrimaries = [
         var tablePrimaries = [
           { alias: 't1', identifierChain: [{ name: 'databaseTwo' }, { name: 'testTable1' }] },
           { alias: 't1', identifierChain: [{ name: 'databaseTwo' }, { name: 'testTable1' }] },
           { alias: 't2', identifierChain: [{ name: 'testTable2' }] }
           { alias: 't2', identifierChain: [{ name: 'testTable2' }] }
@@ -525,7 +525,7 @@ define([
 
 
         var actual = sql.expandImpalaIdentifierChain(tablePrimaries, identifierChain);
         var actual = sql.expandImpalaIdentifierChain(tablePrimaries, identifierChain);
 
 
-        expect(actual).toEqual([{ name: 'databaseTwo' }, { name: 'testTable1' }]);
+        expect(actual).toEqual([{ name: 't1' }]);
       });
       });
     });
     });
   });
   });

+ 14 - 0
desktop/core/src/desktop/static/desktop/spec/autocomplete/sqlSpecSelect.js

@@ -4470,6 +4470,20 @@ define([
         });
         });
       });
       });
 
 
+      it('should suggest columns for "SELECT b.| FROM dbOne.foo f JOIN dbOne.bar b"', function() {
+        assertAutoComplete({
+          beforeCursor: 'SELECT b.',
+          afterCursor: ' FROM dbOne.foo f JOIN dbOne.bar b',
+          hasLocations: true,
+          dialect: 'impala',
+          expectedResult: {
+            lowerCase: false,
+            suggestKeywords: ['*'],
+            suggestColumns: { table: 'bar', database: 'dbOne' }
+          }
+        });
+      });
+
       it('should suggest columns for "SELECT t1.testTableColumn1, t2.testTableColumn3 FROM database_two.testTable1 t1 JOIN testTable2 t2 ON t1.|"', function() {
       it('should suggest columns for "SELECT t1.testTableColumn1, t2.testTableColumn3 FROM database_two.testTable1 t1 JOIN testTable2 t2 ON t1.|"', function() {
         assertAutoComplete({
         assertAutoComplete({
           beforeCursor: 'SELECT t1.testTableColumn1, t2.testTableColumn3 FROM database_two.testTable1 t1 JOIN testTable2 t2 ON t1.',
           beforeCursor: 'SELECT t1.testTableColumn1, t2.testTableColumn3 FROM database_two.testTable1 t1 JOIN testTable2 t2 ON t1.',