소스 검색

HUE-7738 [editor] Use async UDF return type resolution for the autocompleter results

Johan Ahlen 5 년 전
부모
커밋
85f0f7d0bc

+ 2 - 2
desktop/core/src/desktop/js/ko/components/contextPopover/functionContext.js

@@ -17,7 +17,7 @@
 import * as ko from 'knockout';
 
 import I18n from 'utils/i18n';
-import { findFunction } from 'sql/reference/sqlReferenceRepository';
+import { findUdf } from 'sql/reference/sqlReferenceRepository';
 
 const TEMPLATE_NAME = 'context-popover-function-details';
 
@@ -44,7 +44,7 @@ class FunctionContextTabs {
     this.loading = ko.observable(true);
     this.hasErrors = ko.observable(false);
 
-    findFunction(connector, data.function)
+    findUdf(connector, data.function)
       .then(this.details)
       .catch(() => {
         this.hasErrors(true);

+ 89 - 49
desktop/core/src/desktop/js/sql/autocompleteResults.js

@@ -29,9 +29,10 @@ import { DIALECT } from 'apps/notebook2/snippet';
 import { cancelActiveRequest } from 'api/apiUtils';
 import { findBrowserConnector, getRootFilePath } from 'utils/hueConfig';
 import {
-  findFunction,
-  getArgumentTypes,
-  getFunctionsWithReturnTypes,
+  findUdf,
+  getArgumentTypesForUdf,
+  getUdfsWithReturnTypes,
+  getReturnTypesForUdf,
   getSetOptions
 } from './reference/sqlReferenceRepository';
 
@@ -477,7 +478,7 @@ class AutocompleteResults {
 
     self.handleKeywords(colRefDeferred);
     self.handleIdentifiers();
-    self.handleColumnAliases();
+    self.activeDeferrals.push(self.handleColumnAliases());
     self.handleCommonTableExpressions();
     self.activeDeferrals.push(self.handleOptions());
     self.activeDeferrals.push(self.handleFunctions(colRefDeferred));
@@ -507,7 +508,7 @@ class AutocompleteResults {
 
   async adjustForUdfArgument() {
     return new Promise(async resolve => {
-      const foundArgumentTypes = (await getArgumentTypes(
+      const foundArgumentTypes = (await getArgumentTypesForUdf(
         this.snippet.connector(),
         this.parseResult.udfArgument.name,
         this.parseResult.udfArgument.position
@@ -665,8 +666,10 @@ class AutocompleteResults {
 
   handleColumnAliases() {
     const self = this;
+    const deferrals = [];
+    const columnAliasesDeferred = $.Deferred();
+    const columnAliasSuggestions = [];
     if (self.parseResult.suggestColumnAliases) {
-      const columnAliasSuggestions = [];
       self.parseResult.suggestColumnAliases.forEach(columnAlias => {
         const type =
           columnAlias.types && columnAlias.types.length === 1 ? columnAlias.types[0] : 'T';
@@ -678,6 +681,21 @@ class AutocompleteResults {
             popular: ko.observable(false),
             details: columnAlias
           });
+        } else if (type === 'UDFREF') {
+          const udfDeferred = $.Deferred();
+          deferrals.push(udfDeferred);
+          getReturnTypesForUdf(self.snippet.connector(), columnAlias.udfRef)
+            .then(types => {
+              const resolvedType = types.length === 1 ? types[0] : 'T';
+              columnAliasSuggestions.push({
+                value: columnAlias.name,
+                meta: resolvedType,
+                category: CATEGORIES.COLUMN,
+                popular: ko.observable(false),
+                details: columnAlias
+              });
+            })
+            .finally(udfDeferred.resolve);
         } else {
           columnAliasSuggestions.push({
             value: columnAlias.name,
@@ -688,8 +706,14 @@ class AutocompleteResults {
           });
         }
       });
-      self.appendEntries(columnAliasSuggestions);
     }
+    $.when.apply($, deferrals).always(() => {
+      if (columnAliasSuggestions.length) {
+        self.appendEntries(columnAliasSuggestions);
+      }
+      columnAliasesDeferred.resolve();
+    });
+    return columnAliasesDeferred;
   }
 
   handleCommonTableExpressions() {
@@ -748,44 +772,57 @@ class AutocompleteResults {
       const functionSuggestions = [];
       if (
         self.parseResult.suggestFunctions.types &&
-        self.parseResult.suggestFunctions.types[0] === 'COLREF'
+        (self.parseResult.suggestFunctions.types[0] === 'COLREF' ||
+          self.parseResult.suggestFunctions.types[0] === 'UDFREF')
       ) {
         initLoading(self.loadingFunctions, colRefDeferred);
 
-        colRefDeferred
-          .done(async colRef => {
-            const functionsToSuggest = await getFunctionsWithReturnTypes(
-              self.snippet.connector(),
-              [colRef.type.toUpperCase()],
-              self.parseResult.suggestAggregateFunctions || false,
-              self.parseResult.suggestAnalyticFunctions || false
-            );
+        const addUdfsOfTypes = async types => {
+          const functionsToSuggest = await getUdfsWithReturnTypes(
+            self.snippet.connector(),
+            types,
+            self.parseResult.suggestAggregateFunctions || false,
+            self.parseResult.suggestAnalyticFunctions || false
+          );
 
-            Object.keys(functionsToSuggest).forEach(name => {
-              functionSuggestions.push({
-                category: CATEGORIES.UDF,
-                value: name + '()',
-                meta: functionsToSuggest[name].returnTypes.join('|'),
-                weightAdjust:
-                  colRef.type.toUpperCase() !== 'T' &&
-                  functionsToSuggest[name].returnTypes.some(otherType => {
-                    return otherType === colRef.type.toUpperCase();
-                  })
-                    ? 1
-                    : 0,
-                popular: ko.observable(false),
-                details: functionsToSuggest[name]
-              });
+          const firstType = types[0].toUpperCase();
+
+          Object.keys(functionsToSuggest).forEach(name => {
+            functionSuggestions.push({
+              category: CATEGORIES.UDF,
+              value: name + '()',
+              meta: functionsToSuggest[name].returnTypes.join('|'),
+              weightAdjust:
+                firstType !== 'T' &&
+                functionsToSuggest[name].returnTypes.some(otherType => otherType === firstType)
+                  ? 1
+                  : 0,
+              popular: ko.observable(false),
+              details: functionsToSuggest[name]
             });
+          });
 
-            self.appendEntries(functionSuggestions);
-            functionsDeferred.resolve();
-          })
-          .fail(functionsDeferred.reject);
+          self.appendEntries(functionSuggestions);
+          functionsDeferred.resolve();
+        };
+
+        if (self.parseResult.suggestFunctions.types[0] === 'COLREF') {
+          colRefDeferred
+            .done(async colRef => {
+              await addUdfsOfTypes([colRef.type.toUpperCase()]);
+            })
+            .fail(functionsDeferred.reject);
+        } else {
+          getReturnTypesForUdf(self.snippet.connector(), self.parseResult.suggestFunctions.udfRef)
+            .then(addUdfsOfTypes)
+            .catch(async () => {
+              await addUdfsOfTypes(['T']);
+            });
+        }
       } else {
         const types = self.parseResult.suggestFunctions.types || ['T'];
 
-        getFunctionsWithReturnTypes(
+        getUdfsWithReturnTypes(
           self.snippet.connector(),
           types,
           self.parseResult.suggestAggregateFunctions || false,
@@ -992,22 +1029,25 @@ class AutocompleteResults {
           });
         };
 
+        const applyTypes = types => {
+          suggestColumns.tables.forEach(table => {
+            columnDeferrals.push(self.addColumns(table, types, columnSuggestions));
+          });
+          waitForCols();
+        };
+
         if (suggestColumns.types && suggestColumns.types[0] === 'COLREF') {
           colRefDeferred.done(colRef => {
-            suggestColumns.tables.forEach(table => {
-              columnDeferrals.push(
-                self.addColumns(table, [colRef.type.toUpperCase()], columnSuggestions)
-              );
-            });
-            waitForCols();
+            applyTypes([colRef.type.toUpperCase()]);
           });
+        } else if (suggestColumns.types && suggestColumns.types[0] === 'UDFREF') {
+          getReturnTypesForUdf(self.snippet.connector(), suggestColumns.udfRef)
+            .then(applyTypes)
+            .catch(() => {
+              applyTypes(['T']);
+            });
         } else {
-          suggestColumns.tables.forEach(table => {
-            columnDeferrals.push(
-              self.addColumns(table, suggestColumns.types || ['T'], columnSuggestions)
-            );
-          });
-          waitForCols();
+          applyTypes(suggestColumns.types || ['T']);
         }
       } else {
         columnsDeferred.reject();
@@ -1764,7 +1804,7 @@ class AutocompleteResults {
                         clean = clean.replace(substitution.replace, substitution.with);
                       });
 
-                      value.function = await findFunction(
+                      value.function = await findUdf(
                         self.snippet.connector(),
                         value.aggregateFunction
                       );

+ 10 - 11
desktop/core/src/desktop/js/sql/autocompleteResults.test.js

@@ -344,17 +344,16 @@ describe('AutocompleteResults.js', () => {
   it('should handle parse results with functions', async () => {
     subject.entries([]);
 
-    const spy = spyOn(sqlReferenceRepository, 'getFunctionsWithReturnTypes').and.callFake(
-      async () =>
-        Promise.resolve({
-          count: {
-            returnTypes: ['BIGINT'],
-            arguments: [[{ type: 'T' }]],
-            signature: 'count(col)',
-            draggable: 'count()',
-            description: 'some desc'
-          }
-        })
+    const spy = spyOn(sqlReferenceRepository, 'getUdfsWithReturnTypes').and.callFake(async () =>
+      Promise.resolve({
+        count: {
+          returnTypes: ['BIGINT'],
+          arguments: [[{ type: 'T' }]],
+          signature: 'count(col)',
+          draggable: 'count()',
+          description: 'some desc'
+        }
+      })
     );
 
     expect(subject.filtered().length).toBe(0);

+ 15 - 4
desktop/core/src/desktop/js/sql/reference/sqlReferenceRepository.js

@@ -50,7 +50,7 @@ export const getUdfCategories = async connector => {
   return [];
 };
 
-export const findFunction = async (connector, functionName) => {
+export const findUdf = async (connector, functionName) => {
   const categories = await getUdfCategories(connector);
   let found = undefined;
   categories.some(category => {
@@ -62,7 +62,18 @@ export const findFunction = async (connector, functionName) => {
   return found;
 };
 
-export const getFunctionsWithReturnTypes = async (
+export const getReturnTypesForUdf = async (connector, functionName) => {
+  if (!functionName) {
+    return ['T'];
+  }
+  const udf = await findUdf(connector, functionName);
+  if (!udf || !udf.returnTypes) {
+    return ['T'];
+  }
+  return udf.returnTypes;
+};
+
+export const getUdfsWithReturnTypes = async (
   connector,
   returnTypes,
   includeAggregate,
@@ -87,8 +98,8 @@ export const getFunctionsWithReturnTypes = async (
   return result;
 };
 
-export const getArgumentTypes = async (connector, functionName, argumentPosition) => {
-  const foundFunction = await findFunction(connector, functionName);
+export const getArgumentTypesForUdf = async (connector, functionName, argumentPosition) => {
+  const foundFunction = await findUdf(connector, functionName);
   if (!foundFunction) {
     return ['T'];
   }

+ 25 - 25
desktop/core/src/desktop/js/sql/reference/sqlReferenceRepository.test.js

@@ -14,7 +14,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import { getArgumentTypes } from './sqlReferenceRepository';
+import { getArgumentTypesForUdf } from './sqlReferenceRepository';
 
 describe('sqlReferenceRepository.js', () => {
   const hiveConn = { dialect: 'hive' };
@@ -85,43 +85,43 @@ describe('sqlReferenceRepository.js', () => {
   }));
 
   it('should give the expected argument types at a specific position', async () => {
-    expect(await getArgumentTypes(hiveConn, 'cos', 1)).toEqual(['DECIMAL', 'DOUBLE']);
-    expect(await getArgumentTypes(hiveConn, 'cos', 2)).toEqual([]);
-    expect(await getArgumentTypes(impalaConn, 'cos', 1)).toEqual(['DOUBLE']);
-    expect(await getArgumentTypes(impalaConn, 'cos', 2)).toEqual([]);
+    expect(await getArgumentTypesForUdf(hiveConn, 'cos', 1)).toEqual(['DECIMAL', 'DOUBLE']);
+    expect(await getArgumentTypesForUdf(hiveConn, 'cos', 2)).toEqual([]);
+    expect(await getArgumentTypesForUdf(impalaConn, 'cos', 1)).toEqual(['DOUBLE']);
+    expect(await getArgumentTypesForUdf(impalaConn, 'cos', 2)).toEqual([]);
 
-    expect(await getArgumentTypes(hiveConn, 'concat', 10)).toEqual(['BINARY', 'STRING']);
-    expect(await getArgumentTypes(impalaConn, 'concat', 10)).toEqual(['STRING']);
+    expect(await getArgumentTypesForUdf(hiveConn, 'concat', 10)).toEqual(['BINARY', 'STRING']);
+    expect(await getArgumentTypesForUdf(impalaConn, 'concat', 10)).toEqual(['STRING']);
   });
 
   it('should handle functions with different type of arguments', async () => {
-    expect(await getArgumentTypes(hiveConn, 'reflect', 1)).toEqual(['STRING']);
-    expect(await getArgumentTypes(hiveConn, 'reflect', 2)).toEqual(['STRING']);
-    expect(await getArgumentTypes(hiveConn, 'reflect', 3)).toEqual(['T']);
-    expect(await getArgumentTypes(hiveConn, 'reflect', 200)).toEqual(['T']);
+    expect(await getArgumentTypesForUdf(hiveConn, 'reflect', 1)).toEqual(['STRING']);
+    expect(await getArgumentTypesForUdf(hiveConn, 'reflect', 2)).toEqual(['STRING']);
+    expect(await getArgumentTypesForUdf(hiveConn, 'reflect', 3)).toEqual(['T']);
+    expect(await getArgumentTypesForUdf(hiveConn, 'reflect', 200)).toEqual(['T']);
   });
 
   it('should handle functions with an infinite amount of arguments', async () => {
-    expect(await getArgumentTypes(impalaConn, 'greatest', 1)).toEqual(['T']);
-    expect(await getArgumentTypes(impalaConn, 'greatest', 200)).toEqual(['T']);
+    expect(await getArgumentTypesForUdf(impalaConn, 'greatest', 1)).toEqual(['T']);
+    expect(await getArgumentTypesForUdf(impalaConn, 'greatest', 200)).toEqual(['T']);
 
-    expect(await getArgumentTypes(hiveConn, 'strleft', 1)).toEqual(['T']);
-    expect(await getArgumentTypes(hiveConn, 'strleft', 2)).toEqual(['T']);
-    expect(await getArgumentTypes(hiveConn, 'strleft', 3)).toEqual(['T']);
-    expect(await getArgumentTypes(hiveConn, 'strleft', 200)).toEqual(['T']);
+    expect(await getArgumentTypesForUdf(hiveConn, 'strleft', 1)).toEqual(['T']);
+    expect(await getArgumentTypesForUdf(hiveConn, 'strleft', 2)).toEqual(['T']);
+    expect(await getArgumentTypesForUdf(hiveConn, 'strleft', 3)).toEqual(['T']);
+    expect(await getArgumentTypesForUdf(hiveConn, 'strleft', 200)).toEqual(['T']);
   });
 
   it('should not return types for arguments out of bounds', async () => {
-    expect(await getArgumentTypes(impalaConn, 'strleft', 1)).toEqual(['STRING']);
-    expect(await getArgumentTypes(impalaConn, 'strleft', 2)).toEqual(['INT']);
-    expect(await getArgumentTypes(impalaConn, 'strleft', 3)).toEqual([]);
-    expect(await getArgumentTypes(impalaConn, 'strleft', 200)).toEqual([]);
+    expect(await getArgumentTypesForUdf(impalaConn, 'strleft', 1)).toEqual(['STRING']);
+    expect(await getArgumentTypesForUdf(impalaConn, 'strleft', 2)).toEqual(['INT']);
+    expect(await getArgumentTypesForUdf(impalaConn, 'strleft', 3)).toEqual([]);
+    expect(await getArgumentTypesForUdf(impalaConn, 'strleft', 200)).toEqual([]);
   });
 
   it("should return T for any argument if the udf isn't found", async () => {
-    expect(await getArgumentTypes(hiveConn, 'blabla', 2)).toEqual(['T']);
-    expect(await getArgumentTypes(hiveConn, 'blabla', 200)).toEqual(['T']);
-    expect(await getArgumentTypes(impalaConn, 'blabla', 2)).toEqual(['T']);
-    expect(await getArgumentTypes(impalaConn, 'blabla', 200)).toEqual(['T']);
+    expect(await getArgumentTypesForUdf(hiveConn, 'blabla', 2)).toEqual(['T']);
+    expect(await getArgumentTypesForUdf(hiveConn, 'blabla', 200)).toEqual(['T']);
+    expect(await getArgumentTypesForUdf(impalaConn, 'blabla', 2)).toEqual(['T']);
+    expect(await getArgumentTypesForUdf(impalaConn, 'blabla', 200)).toEqual(['T']);
   });
 });