瀏覽代碼

HUE-5839 [autocomplete] Improve autocompletion around CTEs

Johan Ahlen 8 年之前
父節點
當前提交
b8037d8

+ 10 - 0
desktop/core/src/desktop/static/desktop/js/autocomplete/jison/sql_main.jison

@@ -1414,12 +1414,22 @@ WithQueries
 WithQueries_EDIT
  : WithQuery_EDIT
  | WithQueries ',' WithQuery_EDIT
+   {
+     addCommonTableExpressions($1);
+   }
  | WithQuery_EDIT ',' WithQueries
  | WithQueries ',' WithQuery_EDIT ',' WithQueries
+   {
+     addCommonTableExpressions($1);
+   }
  ;
 
 WithQuery
  : RegularOrBacktickedIdentifier AnyAs '(' TableSubQueryInner ')'
+   {
+     $4.alias = $1;
+     $$ = $4;
+   }
  ;
 
 WithQuery_EDIT

+ 44 - 6
desktop/core/src/desktop/static/desktop/js/autocomplete/sql.js

@@ -498,7 +498,7 @@ case 773:
      addCommonTableExpressions($$[$0-3]);
    
 break;
-case 774: case 775:
+case 774: case 775: case 815:
 
      addCommonTableExpressions($$[$0-2]);
    
@@ -636,6 +636,17 @@ this.$ = $$[$0];
 break;
 case 813:
 this.$ = $$[$0-2].concat([$$[$0]]);;
+break;
+case 817:
+
+     addCommonTableExpressions($$[$0-4]);
+   
+break;
+case 818:
+
+     $$[$0-1].alias = $$[$0-4];
+     this.$ = $$[$0-1];
+   
 break;
 case 819: case 1410: case 2104: case 2163: case 2242: case 2246: case 2311:
 
@@ -4719,6 +4730,7 @@ var prepareNewStatement = function () {
 };
 
 var addCommonTableExpressions = function (identifiers) {
+  parser.yy.result.commonTableExpressions = identifiers;
   parser.yy.latestCommonTableExpressions = identifiers;
 };
 
@@ -5068,9 +5080,24 @@ var commitLocations = function () {
 var prioritizeSuggestions = function () {
   parser.yy.result.lowerCase = parser.yy.lowerCase || false;
 
+  var cteIndex = {};
+
+  if (typeof parser.yy.latestCommonTableExpressions !== 'undefined') {
+    parser.yy.latestCommonTableExpressions.forEach(function (cte) {
+      cteIndex[cte.alias.toLowerCase()] = cte;
+    })
+  }
+
   SIMPLE_TABLE_REF_SUGGESTIONS.forEach(function (suggestionType) {
     if (suggestionType !== 'suggestAggregateFunctions' && typeof parser.yy.result[suggestionType] !== 'undefined' && parser.yy.result[suggestionType].tables.length === 0) {
       delete parser.yy.result[suggestionType];
+    } else if (typeof parser.yy.result[suggestionType] !== 'undefined' && typeof parser.yy.result[suggestionType].tables !== 'undefined') {
+      for (var i = parser.yy.result[suggestionType].tables.length - 1; i >= 0; i--) {
+        var table = parser.yy.result[suggestionType].tables[i];
+        if (table.identifierChain.length === 1 && typeof table.identifierChain[0].name !== 'undefined' && typeof cteIndex[table.identifierChain[0].name] !== 'undefined') {
+          parser.yy.result[suggestionType].tables.splice(i, 1);
+        }
+      }
     }
   });
 
@@ -5120,6 +5147,17 @@ var prioritizeSuggestions = function () {
     } else {
       delete parser.yy.result.suggestTables;
       delete parser.yy.result.suggestDatabases;
+
+      suggestColumns.tables.forEach(function (table) {
+        if (typeof table.identifierChain !== 'undefined' && table.identifierChain.length === 1 && typeof table.identifierChain[0].name !== 'undefined') {
+          var cte = cteIndex[table.identifierChain[0].name.toLowerCase()];
+          if (typeof cte !== 'undefined') {
+            delete table.identifierChain[0].name;
+            table.identifierChain[0].cte = cte.alias;
+          }
+        }
+      });
+
       if (typeof suggestColumns.identifierChain !== 'undefined') {
         delete suggestColumns.identifierChain;
       }
@@ -5136,15 +5174,15 @@ var prioritizeSuggestions = function () {
 
   if (typeof parser.yy.result.suggestTables !== 'undefined' && typeof parser.yy.latestCommonTableExpressions !== 'undefined') {
     var ctes = [];
-    parser.yy.latestCommonTableExpressions.forEach(function (identifier) {
-      var cte = { name: identifier };
+    parser.yy.latestCommonTableExpressions.forEach(function (cte) {
+      var suggestion = { name: cte.alias };
       if (parser.yy.result.suggestTables.prependFrom) {
-        cte.prependFrom = true
+        suggestion.prependFrom = true
       }
       if (parser.yy.result.suggestTables.prependQuestionMark) {
-        cte.prependQuestionMark = true;
+        suggestion.prependQuestionMark = true;
       }
-      ctes.push(cte);
+      ctes.push(suggestion);
     });
     if (ctes.length > 0) {
       parser.yy.result.suggestCommonTableExpressions = ctes;

+ 32 - 5
desktop/core/src/desktop/static/desktop/js/autocomplete/sql_support.js

@@ -34,6 +34,7 @@ var prepareNewStatement = function () {
 };
 
 var addCommonTableExpressions = function (identifiers) {
+  parser.yy.result.commonTableExpressions = identifiers;
   parser.yy.latestCommonTableExpressions = identifiers;
 };
 
@@ -383,9 +384,24 @@ var commitLocations = function () {
 var prioritizeSuggestions = function () {
   parser.yy.result.lowerCase = parser.yy.lowerCase || false;
 
+  var cteIndex = {};
+
+  if (typeof parser.yy.latestCommonTableExpressions !== 'undefined') {
+    parser.yy.latestCommonTableExpressions.forEach(function (cte) {
+      cteIndex[cte.alias.toLowerCase()] = cte;
+    })
+  }
+
   SIMPLE_TABLE_REF_SUGGESTIONS.forEach(function (suggestionType) {
     if (suggestionType !== 'suggestAggregateFunctions' && typeof parser.yy.result[suggestionType] !== 'undefined' && parser.yy.result[suggestionType].tables.length === 0) {
       delete parser.yy.result[suggestionType];
+    } else if (typeof parser.yy.result[suggestionType] !== 'undefined' && typeof parser.yy.result[suggestionType].tables !== 'undefined') {
+      for (var i = parser.yy.result[suggestionType].tables.length - 1; i >= 0; i--) {
+        var table = parser.yy.result[suggestionType].tables[i];
+        if (table.identifierChain.length === 1 && typeof table.identifierChain[0].name !== 'undefined' && typeof cteIndex[table.identifierChain[0].name] !== 'undefined') {
+          parser.yy.result[suggestionType].tables.splice(i, 1);
+        }
+      }
     }
   });
 
@@ -435,6 +451,17 @@ var prioritizeSuggestions = function () {
     } else {
       delete parser.yy.result.suggestTables;
       delete parser.yy.result.suggestDatabases;
+
+      suggestColumns.tables.forEach(function (table) {
+        if (typeof table.identifierChain !== 'undefined' && table.identifierChain.length === 1 && typeof table.identifierChain[0].name !== 'undefined') {
+          var cte = cteIndex[table.identifierChain[0].name.toLowerCase()];
+          if (typeof cte !== 'undefined') {
+            delete table.identifierChain[0].name;
+            table.identifierChain[0].cte = cte.alias;
+          }
+        }
+      });
+
       if (typeof suggestColumns.identifierChain !== 'undefined') {
         delete suggestColumns.identifierChain;
       }
@@ -451,15 +478,15 @@ var prioritizeSuggestions = function () {
 
   if (typeof parser.yy.result.suggestTables !== 'undefined' && typeof parser.yy.latestCommonTableExpressions !== 'undefined') {
     var ctes = [];
-    parser.yy.latestCommonTableExpressions.forEach(function (identifier) {
-      var cte = { name: identifier };
+    parser.yy.latestCommonTableExpressions.forEach(function (cte) {
+      var suggestion = { name: cte.alias };
       if (parser.yy.result.suggestTables.prependFrom) {
-        cte.prependFrom = true
+        suggestion.prependFrom = true
       }
       if (parser.yy.result.suggestTables.prependQuestionMark) {
-        cte.prependQuestionMark = true;
+        suggestion.prependQuestionMark = true;
       }
-      ctes.push(cte);
+      ctes.push(suggestion);
     });
     if (ctes.length > 0) {
       parser.yy.result.suggestCommonTableExpressions = ctes;

+ 19 - 1
desktop/core/src/desktop/static/desktop/js/sqlAutocompleter2.js

@@ -902,7 +902,25 @@ var SqlAutocompleter2 = (function () {
     var self = this;
     var addColumnsDeferred = $.Deferred();
 
-    if (typeof table.identifierChain !== 'undefined' && table.identifierChain.length === 1 && typeof table.identifierChain[0].subQuery !== 'undefined') {
+    if (typeof table.identifierChain !== 'undefined' && table.identifierChain.length === 1 && typeof table.identifierChain[0].cte !== 'undefined') {
+      if (typeof parseResult.commonTableExpressions !== 'undefined' && parseResult.commonTableExpressions.length > 0) {
+        parseResult.commonTableExpressions.every(function (cte) {
+          if (cte.alias === table.identifierChain[0].cte) {
+            cte.columns.forEach(function (column) {
+              var type = typeof column.type !== 'undefined' && column.type !== 'COLREF' ? column.type : 'T';
+              if (typeof column.alias !== 'undefined') {
+                columnSuggestions.push({value: self.backTickIfNeeded(column.alias), meta: type, weight: DEFAULT_WEIGHTS.COLUMN, table: table })
+              } else if (typeof column.identifierChain !== 'undefined' && column.identifierChain.length > 0 && typeof column.identifierChain[column.identifierChain.length - 1].name !== 'undefined') {
+                columnSuggestions.push({value: self.backTickIfNeeded(column.identifierChain[column.identifierChain.length - 1].name), meta: type, weight: DEFAULT_WEIGHTS.COLUMN, table: table })
+              }
+            });
+            return false;
+          }
+          return true;
+        })
+      }
+      addColumnsDeferred.resolve();
+    } else if (typeof table.identifierChain !== 'undefined' && table.identifierChain.length === 1 && typeof table.identifierChain[0].subQuery !== 'undefined') {
       var foundSubQuery = self.locateSubQuery(parseResult.subQueries, table.identifierChain[0].subQuery);
 
       var addSubQueryColumns = function (subQueryColumns) {

+ 35 - 1
desktop/core/src/desktop/static/desktop/js/sqlAutocompleter3.js

@@ -697,7 +697,41 @@ var SqlAutocompleter3 = (function () {
     var self = this;
     var addColumnsDeferred = $.Deferred();
 
-    if (typeof table.identifierChain !== 'undefined' && table.identifierChain.length === 1 && typeof table.identifierChain[0].subQuery !== 'undefined') {
+    if (typeof table.identifierChain !== 'undefined' && table.identifierChain.length === 1 && typeof table.identifierChain[0].cte !== 'undefined') {
+      if (typeof self.parseResult.commonTableExpressions !== 'undefined' && self.parseResult.commonTableExpressions.length > 0) {
+        self.parseResult.commonTableExpressions.every(function (cte) {
+          if (cte.alias === table.identifierChain[0].cte) {
+            cte.columns.forEach(function (column) {
+              var type = typeof column.type !== 'undefined' && column.type !== 'COLREF' ? column.type : 'T';
+              if (typeof column.alias !== 'undefined') {
+                columnSuggestions.push({
+                  value: self.backTickIfNeeded(column.alias),
+                  filterValue: column.alias,
+                  meta: type,
+                  category: CATEGORIES.COLUMN,
+                  table: table,
+                  popular: ko.observable(false),
+                  details: column
+                })
+              } else if (typeof column.identifierChain !== 'undefined' && column.identifierChain.length > 0 && typeof column.identifierChain[column.identifierChain.length - 1].name !== 'undefined') {
+                columnSuggestions.push({
+                  value: self.backTickIfNeeded(column.identifierChain[column.identifierChain.length - 1].name),
+                  filterValue: column.identifierChain[column.identifierChain.length - 1].name,
+                  meta: type,
+                  category: CATEGORIES.COLUMN,
+                  table: table,
+                  popular: ko.observable(false),
+                  details: column
+                })
+              }
+            });
+            return false;
+          }
+          return true;
+        })
+      }
+      addColumnsDeferred.resolve();
+    } else if (typeof table.identifierChain !== 'undefined' && table.identifierChain.length === 1 && typeof table.identifierChain[0].subQuery !== 'undefined') {
       var foundSubQuery = locateSubQuery(self.parseResult.subQueries, table.identifierChain[0].subQuery);
 
       var addSubQueryColumns = function (subQueryColumns) {

+ 8 - 5
desktop/core/src/desktop/static/desktop/spec/autocomplete/sqlSpecInsert.js

@@ -898,8 +898,9 @@
           expectedResult: {
             lowerCase: false,
             suggestTables: {},
-            suggestDatabases: { appendDot: true},
-            suggestCommonTableExpressions: [{ name: 't1' },{ name: 't2' }]
+            suggestDatabases: { appendDot: true },
+            suggestCommonTableExpressions: [{ name: 't1' }, { name: 't2' }],
+            commonTableExpressions: [{ alias: 't1', columns: [{ type: 'NUMBER' }] }, { alias: 't2', columns: [{ type: 'NUMBER' }] }],
           }
         });
       });
@@ -1071,7 +1072,8 @@
           noErrors: true,
           expectedResult: {
             lowerCase: false,
-            suggestKeywords: ['PARTITION', '[NOSHUFFLE]', '[SHUFFLE]', 'SELECT', 'VALUES']
+            suggestKeywords: ['PARTITION', '[NOSHUFFLE]', '[SHUFFLE]', 'SELECT', 'VALUES'],
+            commonTableExpressions: [{ alias: 't1', columns: [{ type: 'NUMBER' }] }, { alias: 't2', columns: [{ type: 'NUMBER' }] }]
           }
         });
       });
@@ -1123,8 +1125,9 @@
           expectedResult: {
             lowerCase: true,
             suggestTables: {},
-            suggestDatabases: { appendDot: true},
-            suggestCommonTableExpressions: [{ name: 't1' },{ name: 't2' }]
+            suggestDatabases: { appendDot: true },
+            suggestCommonTableExpressions: [{ name: 't1' },{ name: 't2' }],
+            commonTableExpressions: [{ alias: 't1', columns: [{ type: 'NUMBER' }] }, { alias: 't2', columns: [{ type: 'NUMBER' }] }]
           }
         });
       });

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

@@ -5580,7 +5580,44 @@
             suggestFunctions: {},
             suggestTables: { prependQuestionMark: true, prependFrom: true },
             suggestDatabases: { prependQuestionMark: true, prependFrom: true, appendDot:true },
-            suggestCommonTableExpressions: [{ name: 't1', prependFrom: true, prependQuestionMark:true }]
+            suggestCommonTableExpressions: [{ name: 't1', prependFrom: true, prependQuestionMark:true }],
+            commonTableExpressions: [{ alias: 't1', columns: [{ tables: [{ identifierChain: [{ name: 'FOO' }] }] }] }]
+          }
+        });
+      });
+
+      it('should suggest identifiers for "WITH t1 AS (SELECT * FROM FOO), t2 AS (SELECT |', function () {
+        assertAutoComplete({
+          beforeCursor: 'WITH t1 AS (SELECT * FROM FOO), t2 AS (SELECT ',
+          afterCursor: '',
+          containsKeywords: ['*', 'ALL', 'DISTINCT'],
+          expectedResult: {
+            suggestAggregateFunctions: { tables: [] },
+            suggestAnalyticFunctions: true,
+            suggestFunctions: {},
+            suggestTables: { prependQuestionMark: true, prependFrom: true },
+            suggestDatabases: { prependQuestionMark: true, prependFrom: true, appendDot: true },
+            lowerCase: false,
+            suggestCommonTableExpressions: [{ name: 't1', prependFrom: true, prependQuestionMark: true }],
+            commonTableExpressions: [{ alias: 't1', columns: [{ tables: [{ identifierChain: [{ name: 'FOO' }] }] }] }]
+          }
+        });
+      });
+
+      it('should suggest identifiers for "WITH t1 AS (SELECT id FROM foo), t2 AS (SELECT | FROM t1)', function () {
+        assertAutoComplete({
+          beforeCursor: 'WITH t1 AS (SELECT id FROM foo), t2 AS (SELECT ',
+          afterCursor: ' FROM t1)',
+          dialect: 'hive',
+          noErrors: true,
+          containsKeywords: ['*', 'ALL', 'DISTINCT'],
+          expectedResult: {
+            suggestAggregateFunctions: { tables: [] },
+            suggestAnalyticFunctions: true,
+            suggestFunctions: {},
+            suggestColumns: { source: 'select', tables: [{ identifierChain: [{ cte: 't1' }] }] },
+            commonTableExpressions: [{ alias: 't1', columns: [{ identifierChain: [{ name: 'foo' }, { name: 'id' }], type: 'COLREF' }] }],
+            lowerCase: false
           }
         });
       });
@@ -5594,7 +5631,8 @@
             lowerCase: false,
             suggestTables: { },
             suggestDatabases: { appendDot: true },
-            suggestCommonTableExpressions: [{ name: 't1' }]
+            suggestCommonTableExpressions: [{ name: 't1' }],
+            commonTableExpressions: [{  alias: 't1', columns: [{ tables: [{ identifierChain: [{ name: 'FOO' }] }] }] }]
           }
         });
       });

+ 2 - 0
desktop/libs/notebook/src/notebook/templates/hue_ace_autocompleter.mako

@@ -90,6 +90,7 @@ from desktop.views import _ko
   </script>
 
   <script type="text/html" id="autocomplete-details-column">
+    <!-- ko if: typeof details.name !== 'undefined' -->
     <div class="autocompleter-details">
       <div class="autocompleter-header"><i class="fa fa-fw fa-columns"></i> <span data-bind="text: details.name"></span></div>
       <div class="autocompleter-details-contents">
@@ -111,6 +112,7 @@ from desktop.views import _ko
         <!-- /ko -->
       </div>
     </div>
+    <!-- /ko -->
   </script>
 
   <script type="text/html" id="autocomplete-details-variable">