Browse Source

[editor] Externalize the SqlAnalyzer from the AceEditor and AceAutocomplete components

This reduces the packaged size by dropping the direct dependendencies on all the parsers.
Johan Åhlén 4 năm trước cách đây
mục cha
commit
394145cda6
19 tập tin đã thay đổi với 279 bổ sung152 xóa
  1. 9 4
      desktop/core/src/desktop/js/apps/editor/components/aceEditor/AceEditor.vue
  2. 3 0
      desktop/core/src/desktop/js/apps/editor/components/aceEditor/AceEditorKoBridge.vue
  3. 11 5
      desktop/core/src/desktop/js/apps/editor/components/aceEditor/acePredict.ts
  4. 27 2
      desktop/core/src/desktop/js/apps/editor/components/aceEditor/autocomplete/AceAutocomplete.vue
  5. 33 13
      desktop/core/src/desktop/js/apps/editor/components/aceEditor/autocomplete/AutocompleteResults.ts
  6. 35 20
      desktop/core/src/desktop/js/apps/editor/components/aceEditor/autocomplete/SqlAutocompleter.ts
  7. 1 1
      desktop/core/src/desktop/js/apps/editor/snippet.js
  8. 5 1
      desktop/core/src/desktop/js/apps/tableBrowser/metastoreDatabase.js
  9. 8 2
      desktop/core/src/desktop/js/apps/tableBrowser/metastoreTable.js
  10. 24 14
      desktop/core/src/desktop/js/catalog/DataCatalogEntry.ts
  11. 17 14
      desktop/core/src/desktop/js/catalog/MultiTableEntry.ts
  12. 6 6
      desktop/core/src/desktop/js/catalog/analyzer/CombinedSqlAnalyser.test.ts
  13. 1 1
      desktop/core/src/desktop/js/catalog/analyzer/CombinedSqlAnalyser.ts
  14. 5 3
      desktop/core/src/desktop/js/catalog/analyzer/sqlAnalyzerRepository.ts
  15. 5 7
      desktop/core/src/desktop/js/catalog/dataCatalog.ts
  16. 21 17
      desktop/core/src/desktop/js/ko/components/assist/assistDbEntry.js
  17. 29 25
      desktop/core/src/desktop/js/ko/components/assist/assistDbNamespace.js
  18. 9 2
      desktop/core/src/desktop/js/ko/components/ko.catalogEntriesList.js
  19. 30 15
      desktop/core/src/desktop/js/sql/autocompleteResults.js

+ 9 - 4
desktop/core/src/desktop/js/apps/editor/components/aceEditor/AceEditor.vue

@@ -23,6 +23,7 @@
       v-if="editor && autocompleteParser"
       :autocomplete-parser="autocompleteParser"
       :sql-reference-provider="sqlReferenceProvider"
+      :sql-analyzer-provider="sqlAnalyzerProvider"
       :editor="editor"
       :editor-id="id"
       :executor="executor"
@@ -33,17 +34,17 @@
 <script lang="ts">
   import { ActiveStatementChangedEventDetails } from 'apps/editor/components/aceEditor/types';
   import { defineComponent, onMounted, PropType, ref, toRefs } from 'vue';
-
   import ace, { getAceMode } from 'ext/aceHelper';
   import { Ace } from 'ext/ace';
-  import { EXECUTE_ACTIVE_EXECUTABLE_TOPIC } from '../events';
 
   import { attachPredictTypeahead } from './acePredict';
   import AceAutocomplete from './autocomplete/AceAutocomplete.vue';
   import AceGutterHandler from './AceGutterHandler';
   import AceLocationHandler, { ACTIVE_STATEMENT_CHANGED_EVENT } from './AceLocationHandler';
+  import { EXECUTE_ACTIVE_EXECUTABLE_TOPIC } from '../events';
   import { formatSql } from 'apps/editor/api';
   import Executor from 'apps/editor/execution/executor';
+  import { SqlAnalyzerProvider } from 'catalog/analyzer/types';
   import SubscriptionTracker from 'components/utils/SubscriptionTracker';
   import {
     AutocompleteParser,
@@ -52,12 +53,12 @@
     SqlParserProvider
   } from 'parse/types';
   import { EditorInterpreter } from 'config/types';
+  import { SqlReferenceProvider } from 'sql/reference/types';
   import { hueWindow } from 'types/types';
   import huePubSub from 'utils/huePubSub';
   import { defer } from 'utils/hueUtils';
   import I18n from 'utils/i18n';
   import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
-  import { SqlReferenceProvider } from 'sql/reference/types';
 
   // Taken from https://www.cs.tut.fi/~jkorpela/chars/spaces.html
   const UNICODES_TO_REMOVE = /[\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u200B\u202F\u205F\u3000\uFEFF]/gi;
@@ -95,6 +96,10 @@
         required: false,
         default: () => ({})
       },
+      sqlAnalyzerProvider: {
+        type: Object as PropType<SqlAnalyzerProvider>,
+        default: undefined
+      },
       sqlParserProvider: {
         type: Object as PropType<SqlParserProvider>,
         default: undefined
@@ -509,7 +514,7 @@
         };
 
         if ((<hueWindow>window).ENABLE_PREDICT) {
-          attachPredictTypeahead(editor, executor.value.connector());
+          attachPredictTypeahead(editor, executor.value.connector(), this.sqlAnalyzerProvider);
         }
 
         let placeholderVisible = false;

+ 3 - 0
desktop/core/src/desktop/js/apps/editor/components/aceEditor/AceEditorKoBridge.vue

@@ -24,6 +24,7 @@
     :executor="executor"
     :initial-cursor-position="cursorPosition"
     :initial-value="value"
+    :sql-analyzer-provider="sqlAnalyzerProvider"
     :sql-parser-provider="sqlParserProvider"
     :sql-reference-provider="sqlReferenceProvider"
     @ace-created="aceCreated"
@@ -44,6 +45,7 @@
 
   import AceEditor from './AceEditor.vue';
   import Executor from 'apps/editor/execution/executor';
+  import sqlAnalyzerRepository from 'catalog/analyzer/sqlAnalyzerRepository';
   import SubscriptionTracker from 'components/utils/SubscriptionTracker';
   import sqlParserRepository from 'parse/sql/sqlParserRepository';
   import sqlReferenceRepository from 'sql/reference/sqlReferenceRepository';
@@ -90,6 +92,7 @@
       return {
         cursorPosition,
         editorId,
+        sqlAnalyzerProvider: sqlAnalyzerRepository,
         sqlParserProvider: sqlParserRepository,
         sqlReferenceProvider: sqlReferenceRepository,
         value

+ 11 - 5
desktop/core/src/desktop/js/apps/editor/components/aceEditor/acePredict.ts

@@ -17,18 +17,24 @@
 import { Ace } from 'ext/ace';
 
 import { CancellablePromise } from 'api/cancellablePromise';
-import { sqlAnalyzerRepository } from 'catalog/analyzer/sqlAnalyzerRepository';
-import { PredictResponse } from 'catalog/analyzer/types';
+import { PredictResponse, SqlAnalyzerProvider } from 'catalog/analyzer/types';
 import { Disposable } from 'components/utils/SubscriptionTracker';
 import { Connector } from 'config/types';
-import { defer } from 'utils/hueUtils';
+import { defer, noop } from 'utils/hueUtils';
 
 type ActivePredict = { text: string; element: HTMLElement };
 
-export const attachPredictTypeahead = (editor: Ace.Editor, connector: Connector): Disposable => {
+export const attachPredictTypeahead = (
+  editor: Ace.Editor,
+  connector: Connector,
+  sqlAnalyzerProvider: SqlAnalyzerProvider
+): Disposable => {
+  if (!sqlAnalyzerProvider) {
+    return { dispose: noop };
+  }
   let activePredict: { text: string; element: HTMLElement } | undefined;
 
-  const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(connector);
+  const sqlAnalyzer = sqlAnalyzerProvider.getSqlAnalyzer(connector);
 
   const addPredictElement = (text: string): ActivePredict => {
     const element = document.createElement('div');

+ 27 - 2
desktop/core/src/desktop/js/apps/editor/components/aceEditor/autocomplete/AceAutocomplete.vue

@@ -83,12 +83,14 @@
 </template>
 
 <script lang="ts">
-  import { defineComponent, PropType } from 'vue';
+  import { defineComponent, PropType, toRefs } from 'vue';
 
   import { Ace } from 'ext/ace';
   import ace from 'ext/aceHelper';
   import { AutocompleteParser } from 'parse/types';
   import { Connector } from 'config/types';
+  import NoopSqlAnalyzer from 'catalog/analyzer/NoopSqlAnalyzer';
+  import { SqlAnalyzerProvider } from 'catalog/analyzer/types';
 
   import { Category, CategoryId, CategoryInfo, extractCategories } from './Category';
   import Executor from 'apps/editor/execution/executor';
@@ -137,6 +139,10 @@
         type: Object as PropType<SqlReferenceProvider>,
         required: true
       },
+      sqlAnalyzerProvider: {
+        type: Object as PropType<SqlAnalyzerProvider | undefined>,
+        default: undefined
+      },
       editor: {
         type: Object as PropType<Ace.Editor>,
         required: true
@@ -156,9 +162,28 @@
       }
     },
     setup(props) {
+      const {
+        autocompleteParser,
+        editor,
+        editorId,
+        executor,
+        sqlAnalyzerProvider,
+        sqlReferenceProvider,
+        temporaryOnly
+      } = toRefs(props);
       const subTracker = new SubscriptionTracker();
 
-      const autocompleter = new SqlAutocompleter(props);
+      const autocompleter = new SqlAutocompleter({
+        autocompleteParser: autocompleteParser.value,
+        editor: editor.value,
+        editorId: editorId.value,
+        executor: executor.value,
+        sqlAnalyzerProvider: sqlAnalyzerProvider.value || {
+          getSqlAnalyzer: () => new NoopSqlAnalyzer()
+        },
+        sqlReferenceProvider: sqlReferenceProvider.value,
+        temporaryOnly: temporaryOnly.value
+      });
 
       const autocompleteResults = autocompleter.autocompleteResults;
 

+ 33 - 13
desktop/core/src/desktop/js/apps/editor/components/aceEditor/autocomplete/AutocompleteResults.ts

@@ -17,6 +17,7 @@
 import { Category, CategoryInfo } from './Category';
 import { CancellablePromise } from 'api/cancellablePromise';
 import Executor from 'apps/editor/execution/executor';
+import { SqlAnalyzer, SqlAnalyzerProvider } from 'catalog/analyzer/types';
 import DataCatalogEntry, {
   FieldSample,
   FieldSourceMeta,
@@ -148,6 +149,7 @@ class AutocompleteResults {
   temporaryOnly: boolean;
   activeDatabase: string;
   sqlReferenceProvider: SqlReferenceProvider;
+  sqlAnalyzer?: SqlAnalyzer;
 
   parseResult!: AutocompleteParseResult;
   subTracker = new SubscriptionTracker();
@@ -157,11 +159,13 @@ class AutocompleteResults {
 
   constructor(options: {
     sqlReferenceProvider: SqlReferenceProvider;
+    sqlAnalyzerProvider?: SqlAnalyzerProvider;
     executor: Executor;
     editor: Ace.Editor;
     temporaryOnly: boolean;
   }) {
     this.sqlReferenceProvider = options.sqlReferenceProvider;
+    this.sqlAnalyzer = options.sqlAnalyzerProvider?.getSqlAnalyzer(options.executor.connector());
     this.executor = options.executor;
     this.editor = options.editor;
     this.temporaryOnly = options.temporaryOnly;
@@ -1251,7 +1255,7 @@ class AutocompleteResults {
 
   async handleJoins(): Promise<Suggestion[]> {
     const suggestJoins = this.parseResult.suggestJoins;
-    if (!(<hueWindow>window).HAS_SQL_ANALYZER || !suggestJoins) {
+    if (!(<hueWindow>window).HAS_SQL_ANALYZER || !suggestJoins || !this.sqlAnalyzer) {
       return [];
     }
 
@@ -1278,8 +1282,9 @@ class AutocompleteResults {
       const topJoins = await new Promise<TopJoins>((resolve, reject) => {
         this.onCancelFunctions.push(reject);
         const topJoinsPromise = multiTableEntry.getTopJoins({
+          cancellable: true,
           silenceErrors: true,
-          cancellable: true
+          sqlAnalyzer: this.sqlAnalyzer!
         });
         this.cancellablePromises.push(topJoinsPromise);
         topJoinsPromise.then(resolve).catch(reject);
@@ -1371,7 +1376,7 @@ class AutocompleteResults {
 
   async handleJoinConditions(): Promise<Suggestion[]> {
     const suggestJoinConditions = this.parseResult.suggestJoinConditions;
-    if (!(<hueWindow>window).HAS_SQL_ANALYZER || !suggestJoinConditions) {
+    if (!(<hueWindow>window).HAS_SQL_ANALYZER || !suggestJoinConditions || !this.sqlAnalyzer) {
       return [];
     }
 
@@ -1399,8 +1404,9 @@ class AutocompleteResults {
       const topJoins = await new Promise<TopJoins>((resolve, reject) => {
         this.onCancelFunctions.push(reject);
         const topJoinsPromise = multiTableEntry.getTopJoins({
+          cancellable: true,
           silenceErrors: true,
-          cancellable: true
+          sqlAnalyzer: this.sqlAnalyzer!
         });
         this.cancellablePromises.push(topJoinsPromise);
         topJoinsPromise.then(resolve).catch(reject);
@@ -1461,7 +1467,8 @@ class AutocompleteResults {
     if (
       !(<hueWindow>window).HAS_SQL_ANALYZER ||
       !suggestAggregateFunctions ||
-      !suggestAggregateFunctions.tables.length
+      !suggestAggregateFunctions.tables.length ||
+      !this.sqlAnalyzer
     ) {
       return [];
     }
@@ -1490,8 +1497,9 @@ class AutocompleteResults {
       const topAggs = await new Promise<TopAggs>((resolve, reject) => {
         this.onCancelFunctions.push(reject);
         const topAggsDeferred = multiTableEntry.getTopAggs({
+          cancellable: true,
           silenceErrors: true,
-          cancellable: true
+          sqlAnalyzer: this.sqlAnalyzer!
         });
         this.cancellablePromises.push(topAggsDeferred);
         topAggsDeferred.then(resolve).catch(reject);
@@ -1590,6 +1598,9 @@ class AutocompleteResults {
     suggestSpec: CommonPopularSuggestion,
     columnsPromise: Promise<Suggestion[]>
   ): Promise<Suggestion[]> {
+    if (!this.sqlAnalyzer) {
+      return [];
+    }
     const paths: string[][] = [];
     suggestSpec.tables.forEach(table => {
       if (table.identifierChain) {
@@ -1613,7 +1624,8 @@ class AutocompleteResults {
           .loadSqlAnalyzerPopularityForTables({
             namespace: this.executor.namespace(),
             compute: this.executor.compute(),
-            paths: paths,
+            paths,
+            sqlAnalyzer: this.sqlAnalyzer!,
             silenceErrors: true,
             cancellable: true
           });
@@ -1713,7 +1725,7 @@ class AutocompleteResults {
 
   async handleFilters(): Promise<Suggestion[]> {
     const suggestFilters = this.parseResult.suggestFilters;
-    if (!(<hueWindow>window).HAS_SQL_ANALYZER || !suggestFilters) {
+    if (!(<hueWindow>window).HAS_SQL_ANALYZER || !suggestFilters || !this.sqlAnalyzer) {
       return [];
     }
 
@@ -1741,8 +1753,9 @@ class AutocompleteResults {
       const topFilters = await new Promise<TopFilters>((resolve, reject) => {
         this.onCancelFunctions.push(reject);
         const topFiltersPromise = multiTableEntry.getTopFilters({
+          cancellable: true,
           silenceErrors: true,
-          cancellable: true
+          sqlAnalyzer: this.sqlAnalyzer!
         });
         this.cancellablePromises.push(topFiltersPromise);
         topFiltersPromise.then(resolve).catch(reject);
@@ -1800,7 +1813,7 @@ class AutocompleteResults {
 
   async handlePopularTables(tablesPromise: Promise<Suggestion[]>): Promise<Suggestion[]> {
     const suggestTables = this.parseResult.suggestTables;
-    if (!(<hueWindow>window).HAS_SQL_ANALYZER || !suggestTables) {
+    if (!(<hueWindow>window).HAS_SQL_ANALYZER || !suggestTables || !this.sqlAnalyzer) {
       return [];
     }
 
@@ -1829,8 +1842,9 @@ class AutocompleteResults {
       const childEntries = await new Promise<DataCatalogEntry[]>((resolve, reject) => {
         this.onCancelFunctions.push(reject);
         const popularityPromise = entry.loadSqlAnalyzerPopularityForChildren({
+          cancellable: true,
           silenceErrors: true,
-          cancellable: true
+          sqlAnalyzer: this.sqlAnalyzer!
         });
         this.cancellablePromises.push(popularityPromise);
         popularityPromise.then(resolve).catch(reject);
@@ -1869,7 +1883,12 @@ class AutocompleteResults {
   async handlePopularColumns(columnsPromise: Promise<Suggestion[]>): Promise<Suggestion[]> {
     const suggestColumns = this.parseResult.suggestColumns;
 
-    if (!(<hueWindow>window).HAS_SQL_ANALYZER || !suggestColumns || !suggestColumns.source) {
+    if (
+      !(<hueWindow>window).HAS_SQL_ANALYZER ||
+      !suggestColumns ||
+      !suggestColumns.source ||
+      !this.sqlAnalyzer
+    ) {
       return [];
     }
 
@@ -1905,7 +1924,8 @@ class AutocompleteResults {
           .loadSqlAnalyzerPopularityForTables({
             namespace: this.executor.namespace(),
             compute: this.executor.compute(),
-            paths: paths,
+            paths,
+            sqlAnalyzer: this.sqlAnalyzer!,
             silenceErrors: true,
             cancellable: true
           });

+ 35 - 20
desktop/core/src/desktop/js/apps/editor/components/aceEditor/autocomplete/SqlAutocompleter.ts

@@ -14,6 +14,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import { SqlAnalyzerProvider } from '../../../../../catalog/analyzer/types';
 import {
   ACTIVE_STATEMENT_CHANGED_EVENT,
   GET_ACTIVE_LOCATIONS_EVENT,
@@ -30,6 +31,18 @@ import AutocompleteResults from './AutocompleteResults';
 import { SqlReferenceProvider } from 'sql/reference/types';
 import huePubSub from 'utils/huePubSub';
 
+interface SqlAutocompleterOptions {
+  editorId: string;
+  executor: Executor;
+  temporaryOnly?: boolean;
+  editor: Ace.Editor;
+  fixedPrefix?: () => string;
+  fixedPostfix?: () => string;
+  autocompleteParser: AutocompleteParser;
+  sqlReferenceProvider: SqlReferenceProvider;
+  sqlAnalyzerProvider: SqlAnalyzerProvider;
+}
+
 export default class SqlAutocompleter implements Disposable {
   editor: Ace.Editor;
   executor: Executor;
@@ -44,28 +57,30 @@ export default class SqlAutocompleter implements Disposable {
 
   onPartial?: (partial: string) => void;
 
-  constructor(options: {
-    editorId: string;
-    executor: Executor;
-    temporaryOnly?: boolean;
-    editor: Ace.Editor;
-    fixedPrefix?: () => string;
-    fixedPostfix?: () => string;
-    autocompleteParser: AutocompleteParser;
-    sqlReferenceProvider: SqlReferenceProvider;
-  }) {
-    this.editorId = options.editorId;
-    this.editor = options.editor;
-    this.executor = options.executor;
-    this.fixedPrefix = options.fixedPrefix || (() => '');
-    this.fixedPostfix = options.fixedPrefix || (() => '');
-    this.autocompleteParser = options.autocompleteParser;
+  constructor({
+    editorId,
+    executor,
+    temporaryOnly = false,
+    editor,
+    fixedPrefix,
+    fixedPostfix,
+    autocompleteParser,
+    sqlReferenceProvider,
+    sqlAnalyzerProvider
+  }: SqlAutocompleterOptions) {
+    this.editorId = editorId;
+    this.editor = editor;
+    this.executor = executor;
+    this.fixedPrefix = fixedPrefix || (() => '');
+    this.fixedPostfix = fixedPostfix || (() => '');
+    this.autocompleteParser = autocompleteParser;
 
     this.autocompleteResults = new AutocompleteResults({
-      sqlReferenceProvider: options.sqlReferenceProvider,
-      executor: options.executor,
-      editor: this.editor,
-      temporaryOnly: !!options.temporaryOnly
+      sqlReferenceProvider,
+      sqlAnalyzerProvider,
+      executor,
+      editor,
+      temporaryOnly
     });
 
     this.subTracker.subscribe(

+ 1 - 1
desktop/core/src/desktop/js/apps/editor/snippet.js

@@ -51,7 +51,7 @@ import {
 } from 'ko/bindings/ace/aceLocationHandler';
 import { findEditorConnector, getLastKnownConfig } from 'config/hueConfig';
 import { cancelActiveRequest } from 'api/apiUtils';
-import { sqlAnalyzerRepository } from 'catalog/analyzer/sqlAnalyzerRepository';
+import sqlAnalyzerRepository from 'catalog/analyzer/sqlAnalyzerRepository';
 import {
   ASSIST_GET_DATABASE_EVENT,
   ASSIST_GET_SOURCE_EVENT,

+ 5 - 1
desktop/core/src/desktop/js/apps/tableBrowser/metastoreDatabase.js

@@ -14,6 +14,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import sqlAnalyzerRepository from 'catalog/analyzer/sqlAnalyzerRepository';
 import $ from 'jquery';
 import * as ko from 'knockout';
 
@@ -148,8 +149,11 @@ class MetastoreDatabase {
         }
         if (sqlAnalyzerEnabled) {
           this.loadingTablePopularity(true);
+          const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(
+            this.catalogEntry.getConnector()
+          );
           this.catalogEntry
-            .loadSqlAnalyzerPopularityForChildren()
+            .loadSqlAnalyzerPopularityForChildren({ sqlAnalyzer })
             .then(() => {
               this.tables().forEach(table => {
                 table.sqlAnalyzerStats(table.catalogEntry.sqlAnalyzerPopularity);

+ 8 - 2
desktop/core/src/desktop/js/apps/tableBrowser/metastoreTable.js

@@ -14,6 +14,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import sqlAnalyzerRepository from 'catalog/analyzer/sqlAnalyzerRepository';
 import $ from 'jquery';
 import * as ko from 'knockout';
 
@@ -157,8 +158,12 @@ class MetastoreTable {
             )
           );
 
+          const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(
+            this.catalogEntry.getConnector()
+          );
+
           this.catalogEntry
-            .getSqlAnalyzerMeta()
+            .getSqlAnalyzerMeta({ sqlAnalyzer })
             .then(sqlAnalyzerMeta => {
               this.sqlAnalyzerDetails(sqlAnalyzerMeta);
 
@@ -201,8 +206,9 @@ class MetastoreTable {
         this.loadingViewSql(true);
       }
 
+      const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(this.catalogEntry.getConnector());
       this.catalogEntry
-        .getTopJoins({ silenceErrors: true })
+        .getTopJoins({ silenceErrors: true, sqlAnalyzer })
         .then(topJoins => {
           if (topJoins && topJoins.values) {
             const joins = [];

+ 24 - 14
desktop/core/src/desktop/js/catalog/DataCatalogEntry.ts

@@ -23,7 +23,6 @@ import {
   fetchSourceMetadata
 } from 'catalog/api';
 import MultiTableEntry, { TopAggs, TopFilters, TopJoins } from 'catalog/MultiTableEntry';
-import { sqlAnalyzerRepository } from './analyzer/sqlAnalyzerRepository';
 import * as ko from 'knockout';
 
 import apiHelper from 'api/apiHelper';
@@ -32,6 +31,7 @@ import { Compute, Connector, Namespace } from 'config/types';
 import { hueWindow } from 'types/types';
 import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';
+import { SqlAnalyzer } from './analyzer/types';
 import {
   CatalogGetOptions,
   DataCatalog,
@@ -424,14 +424,17 @@ export default class DataCatalogEntry {
   /**
    * Helper function to reload the nav opt metadata for the given entry
    */
-  private reloadSqlAnalyzerMeta(options?: ReloadOptions): CancellablePromise<SqlAnalyzerMeta> {
-    const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(this.getConnector());
+  private reloadSqlAnalyzerMeta({
+    cancellable,
+    silenceErrors,
+    sqlAnalyzer
+  }: ReloadOptions & { sqlAnalyzer: SqlAnalyzer }): CancellablePromise<SqlAnalyzerMeta> {
     if (this.dataCatalog.canHaveSqlAnalyzerMeta()) {
       this.sqlAnalyzerMetaPromise = new CancellablePromise<SqlAnalyzerMeta>(
         async (resolve, reject, onCancel) => {
           const fetchPromise = sqlAnalyzer.fetchSqlAnalyzerMeta({
             path: this.path,
-            silenceErrors: options && options.silenceErrors
+            silenceErrors
           });
           onCancel(() => {
             fetchPromise.cancel();
@@ -450,7 +453,7 @@ export default class DataCatalogEntry {
     } else {
       this.sqlAnalyzerMetaPromise = CancellablePromise.reject();
     }
-    return applyCancellable(this.sqlAnalyzerMetaPromise, options);
+    return applyCancellable(this.sqlAnalyzerMetaPromise, { cancellable });
   }
 
   private reloadPartitions(options?: ReloadOptions): CancellablePromise<Partitions> {
@@ -895,7 +898,7 @@ export default class DataCatalogEntry {
    * Loads SQL Analyzer popularity for the children of this entry.
    */
   loadSqlAnalyzerPopularityForChildren(
-    options?: CatalogGetOptions
+    options: CatalogGetOptions & { sqlAnalyzer: SqlAnalyzer }
   ): CancellablePromise<DataCatalogEntry[]> {
     if (
       this.sqlAnalyzerPopularityForChildrenPromise &&
@@ -903,7 +906,7 @@ export default class DataCatalogEntry {
     ) {
       this.sqlAnalyzerPopularityForChildrenPromise = undefined;
     }
-    options = forceSilencedErrors(options);
+    options.silenceErrors = true;
 
     if (!this.dataCatalog.canHaveSqlAnalyzerMeta()) {
       return CancellablePromise.reject();
@@ -936,8 +939,7 @@ export default class DataCatalogEntry {
             cancellablePromises.forEach(cancellable => cancellable.cancel());
           });
 
-          const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(this.dataCatalog.connector);
-          const popularityPromise = sqlAnalyzer.fetchPopularity({
+          const popularityPromise = options.sqlAnalyzer.fetchPopularity({
             ...options,
             paths: [this.path]
           });
@@ -1581,11 +1583,13 @@ export default class DataCatalogEntry {
   /**
    * Gets the SQL Analyzer metadata for the entry. It will fetch it if not cached or if the refresh option is set.
    */
-  getSqlAnalyzerMeta(options?: CatalogGetOptions): CancellablePromise<SqlAnalyzerMeta> {
+  getSqlAnalyzerMeta(
+    options: CatalogGetOptions & { sqlAnalyzer: SqlAnalyzer }
+  ): CancellablePromise<SqlAnalyzerMeta> {
     if (this.sqlAnalyzerMetaPromise && this.sqlAnalyzerMetaPromise.cancelled) {
       this.sqlAnalyzerMetaPromise = undefined;
     }
-    options = forceSilencedErrors(options);
+    options.silenceErrors = true;
 
     if (!this.dataCatalog.canHaveSqlAnalyzerMeta() || !this.isTableOrView()) {
       return CancellablePromise.reject();
@@ -1696,7 +1700,9 @@ export default class DataCatalogEntry {
   /**
    * Gets the top aggregate UDFs for the entry if it's a table or view. It will fetch it if not cached or if the refresh option is set.
    */
-  getTopAggs(options?: CatalogGetOptions): CancellablePromise<TopAggs> {
+  getTopAggs(
+    options: CatalogGetOptions & { sqlAnalyzer: SqlAnalyzer }
+  ): CancellablePromise<TopAggs> {
     const promise = new CancellablePromise<TopAggs>(async (resolve, reject, onCancel) => {
       const multiTableEntry = await getMultiTableEntry(this);
       const topAggsPromise = multiTableEntry.getTopAggs(options);
@@ -1713,7 +1719,9 @@ export default class DataCatalogEntry {
    *
    * @return {CancellableJqPromise}
    */
-  getTopFilters(options?: CatalogGetOptions): CancellablePromise<TopFilters> {
+  getTopFilters(
+    options: CatalogGetOptions & { sqlAnalyzer: SqlAnalyzer }
+  ): CancellablePromise<TopFilters> {
     const promise = new CancellablePromise<TopFilters>(async (resolve, reject, onCancel) => {
       const multiTableEntry = await getMultiTableEntry(this);
       const topFiltersPromise = multiTableEntry.getTopFilters(options);
@@ -1728,7 +1736,9 @@ export default class DataCatalogEntry {
   /**
    * Gets the top joins for the entry if it's a table or view. It will fetch it if not cached or if the refresh option is set.
    */
-  getTopJoins(options?: CatalogGetOptions): CancellablePromise<TopJoins> {
+  getTopJoins(
+    options: CatalogGetOptions & { sqlAnalyzer: SqlAnalyzer }
+  ): CancellablePromise<TopJoins> {
     const promise = new CancellablePromise<TopJoins>(async (resolve, reject, onCancel) => {
       const multiTableEntry = await getMultiTableEntry(this);
       const topJoinsPromise = multiTableEntry.getTopJoins(options);

+ 17 - 14
desktop/core/src/desktop/js/catalog/MultiTableEntry.ts

@@ -17,11 +17,10 @@
 import { noop } from 'lodash';
 
 import DataCatalogEntry from 'catalog/DataCatalogEntry';
-import { PopularityOptions } from './analyzer/types';
+import { PopularityOptions, SqlAnalyzer } from './analyzer/types';
 import { CatalogGetOptions, DataCatalog, TimestampedData } from './dataCatalog';
 import { CancellablePromise } from 'api/cancellablePromise';
 import { applyCancellable } from 'catalog/catalogUtils';
-import { sqlAnalyzerRepository } from 'catalog/analyzer/sqlAnalyzerRepository';
 import { UdfDetails } from 'sql/reference/types';
 import { Connector } from 'config/types';
 import { hueWindow } from 'types/types';
@@ -226,8 +225,9 @@ class MultiTableEntry {
   /**
    * Gets the top aggregate UDFs for the entry. It will fetch it if not cached or if the refresh option is set.
    */
-  getTopAggs(options?: CatalogGetOptions): CancellablePromise<TopAggs> {
-    const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(this.dataCatalog.connector);
+  getTopAggs(
+    options: CatalogGetOptions & { sqlAnalyzer: SqlAnalyzer }
+  ): CancellablePromise<TopAggs> {
     return genericSqlAnalyzerGet<TopAggs>(
       this,
       options,
@@ -238,15 +238,16 @@ class MultiTableEntry {
       val => {
         this.topAggs = val;
       },
-      sqlAnalyzer.fetchTopAggs.bind(sqlAnalyzer)
+      options.sqlAnalyzer.fetchTopAggs.bind(options.sqlAnalyzer)
     );
   }
 
   /**
    * Gets the top columns for the entry. It will fetch it if not cached or if the refresh option is set.
    */
-  getTopColumns(options?: CatalogGetOptions): CancellablePromise<TopColumns> {
-    const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(this.dataCatalog.connector);
+  getTopColumns(
+    options: CatalogGetOptions & { sqlAnalyzer: SqlAnalyzer }
+  ): CancellablePromise<TopColumns> {
     return genericSqlAnalyzerGet<TopColumns>(
       this,
       options,
@@ -257,15 +258,16 @@ class MultiTableEntry {
       val => {
         this.topColumns = val;
       },
-      sqlAnalyzer.fetchTopColumns.bind(sqlAnalyzer)
+      options.sqlAnalyzer.fetchTopColumns.bind(options.sqlAnalyzer)
     );
   }
 
   /**
    * Gets the top filters for the entry. It will fetch it if not cached or if the refresh option is set.
    */
-  getTopFilters(options?: CatalogGetOptions): CancellablePromise<TopFilters> {
-    const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(this.dataCatalog.connector);
+  getTopFilters(
+    options: CatalogGetOptions & { sqlAnalyzer: SqlAnalyzer }
+  ): CancellablePromise<TopFilters> {
     return genericSqlAnalyzerGet<TopFilters>(
       this,
       options,
@@ -276,15 +278,16 @@ class MultiTableEntry {
       val => {
         this.topFilters = val;
       },
-      sqlAnalyzer.fetchTopFilters.bind(sqlAnalyzer)
+      options.sqlAnalyzer.fetchTopFilters.bind(options.sqlAnalyzer)
     );
   }
 
   /**
    * Gets the top joins for the entry. It will fetch it if not cached or if the refresh option is set.
    */
-  getTopJoins(options?: CatalogGetOptions): CancellablePromise<TopJoins> {
-    const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(this.dataCatalog.connector);
+  getTopJoins(
+    options: CatalogGetOptions & { sqlAnalyzer: SqlAnalyzer }
+  ): CancellablePromise<TopJoins> {
     return genericSqlAnalyzerGet<TopJoins>(
       this,
       options,
@@ -295,7 +298,7 @@ class MultiTableEntry {
       val => {
         this.topJoins = val;
       },
-      sqlAnalyzer.fetchTopJoins.bind(sqlAnalyzer)
+      options.sqlAnalyzer.fetchTopJoins.bind(options.sqlAnalyzer)
     );
   }
 }

+ 6 - 6
desktop/core/src/desktop/js/catalog/analyzer/MixedSqlAnalyser.test.ts → desktop/core/src/desktop/js/catalog/analyzer/CombinedSqlAnalyser.test.ts

@@ -15,7 +15,7 @@
 // limitations under the License.
 
 import { Connector } from 'config/types';
-import MixedSqlAnalyzer from './MixedSqlAnalyzer';
+import CombinedSqlAnalyser from './CombinedSqlAnalyser';
 
 const connectorA: Connector = {
   buttonName: '',
@@ -29,7 +29,7 @@ const connectorA: Connector = {
 describe('SqlAnalyzer.ts', () => {
   describe('checkMissingLimit', () => {
     it('Should detect a missing LIMIT', async () => {
-      const isMissingLimit = await new MixedSqlAnalyzer(connectorA).checkMissingLimit(
+      const isMissingLimit = await new CombinedSqlAnalyser(connectorA).checkMissingLimit(
         'SELECT * FROM employee',
         'hive'
       );
@@ -38,7 +38,7 @@ describe('SqlAnalyzer.ts', () => {
     });
 
     it('Should avoid warning from a missing LIMIT in SELECT without a table', async () => {
-      const isMissingLimit = await new MixedSqlAnalyzer(connectorA).checkMissingLimit(
+      const isMissingLimit = await new CombinedSqlAnalyser(connectorA).checkMissingLimit(
         'SELECT 1',
         'hive'
       );
@@ -47,7 +47,7 @@ describe('SqlAnalyzer.ts', () => {
     });
 
     it('Should not warning from a missing LIMIT in CREATE', async () => {
-      const isMissingLimit = await new MixedSqlAnalyzer(connectorA).checkMissingLimit(
+      const isMissingLimit = await new CombinedSqlAnalyser(connectorA).checkMissingLimit(
         'CREATE TABLE a (a int)',
         'hive'
       );
@@ -58,7 +58,7 @@ describe('SqlAnalyzer.ts', () => {
 
   describe('checkSelectStar', () => {
     it('Should detect a SELECT *', async () => {
-      const isSelectStar = await new MixedSqlAnalyzer(connectorA).checkSelectStar(
+      const isSelectStar = await new CombinedSqlAnalyser(connectorA).checkSelectStar(
         'SELECT * FROM employee',
         'hive'
       );
@@ -66,7 +66,7 @@ describe('SqlAnalyzer.ts', () => {
       expect(isSelectStar).toBeTruthy();
     });
     it('Should not warning from a non SELECT *', async () => {
-      const isSelectStar = await new MixedSqlAnalyzer(connectorA).checkSelectStar(
+      const isSelectStar = await new CombinedSqlAnalyser(connectorA).checkSelectStar(
         'SELECT name FROM employee',
         'hive'
       );

+ 1 - 1
desktop/core/src/desktop/js/catalog/analyzer/MixedSqlAnalyzer.ts → desktop/core/src/desktop/js/catalog/analyzer/CombinedSqlAnalyser.ts

@@ -39,7 +39,7 @@ import { Connector, Namespace } from 'config/types';
 import { hueWindow } from 'types/types';
 import I18n from 'utils/i18n';
 
-export default class MixedSqlAnalyzer implements SqlAnalyzer {
+export default class CombinedSqlAnalyser implements SqlAnalyzer {
   apiAnalyzer: ApiSqlAnalyzer;
   connector: Connector;
 

+ 5 - 3
desktop/core/src/desktop/js/catalog/analyzer/sqlAnalyzerRepository.ts

@@ -15,7 +15,7 @@
 // limitations under the License.
 
 import NoopSqlAnalyzer from './NoopSqlAnalyzer';
-import MixedSqlAnalyzer from './MixedSqlAnalyzer';
+import CombinedSqlAnalyser from './CombinedSqlAnalyser';
 import { Connector } from 'config/types';
 import { hueWindow } from 'types/types';
 import { SqlAnalyzer, SqlAnalyzerProvider, SqlAnalyzerMode } from './types';
@@ -28,12 +28,12 @@ const createSqlAnalyzer = (connector: Connector): SqlAnalyzer => {
     (<hueWindow>window).SQL_ANALYZER_MODE === SqlAnalyzerMode.local ||
     (<hueWindow>window).SQL_ANALYZER_MODE === SqlAnalyzerMode.api
   ) {
-    return new MixedSqlAnalyzer(connector);
+    return new CombinedSqlAnalyser(connector);
   }
   return new NoopSqlAnalyzer();
 };
 
-export const sqlAnalyzerRepository: SqlAnalyzerProvider = {
+const sqlAnalyzerRepository: SqlAnalyzerProvider = {
   getSqlAnalyzer: (connector: Connector): SqlAnalyzer => {
     let sqlAnalyzer = sqlAnalyzerInstances[connector.id];
     if (!sqlAnalyzer) {
@@ -43,3 +43,5 @@ export const sqlAnalyzerRepository: SqlAnalyzerProvider = {
     return sqlAnalyzer;
   }
 };
+
+export default sqlAnalyzerRepository;

+ 5 - 7
desktop/core/src/desktop/js/catalog/dataCatalog.ts

@@ -37,10 +37,9 @@ import MultiTableEntry, {
   TopFilters,
   TopJoins
 } from 'catalog/MultiTableEntry';
-import { sqlAnalyzerRepository } from 'catalog/analyzer/sqlAnalyzerRepository';
 import { Compute, Connector, Namespace } from 'config/types';
 import { hueWindow } from 'types/types';
-import { SqlAnalyzerMode } from './analyzer/types';
+import { SqlAnalyzer, SqlAnalyzerMode } from './analyzer/types';
 
 export interface TimestampedData {
   hueTimestamp?: number;
@@ -380,11 +379,12 @@ export class DataCatalog {
    * Loads SQL Analyzer popularity for multiple tables in one go.
    */
   loadSqlAnalyzerPopularityForTables(options: {
-    namespace: Namespace;
+    cancellable?: boolean;
     compute: Compute;
+    namespace: Namespace;
     paths: string[][];
     silenceErrors?: boolean;
-    cancellable?: boolean;
+    sqlAnalyzer: SqlAnalyzer;
   }): CancellablePromise<DataCatalogEntry[]> {
     const cancellablePromises: Cancellable[] = [];
     const popularEntries: DataCatalogEntry[] = [];
@@ -438,9 +438,7 @@ export class DataCatalog {
           return;
         }
 
-        const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(this.connector);
-
-        const fetchPromise = sqlAnalyzer.fetchPopularity({
+        const fetchPromise = options.sqlAnalyzer.fetchPopularity({
           silenceErrors: true,
           paths: pathsToLoad
         });

+ 21 - 17
desktop/core/src/desktop/js/ko/components/assist/assistDbEntry.js

@@ -14,6 +14,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import sqlAnalyzerRepository from 'catalog/analyzer/sqlAnalyzerRepository';
 import $ from 'jquery';
 import * as ko from 'knockout';
 
@@ -403,25 +404,28 @@ class AssistDbEntry {
       (self.catalogEntry.isTable() || self.catalogEntry.isDatabase()) &&
       !self.assistDbNamespace.nonSqlType
     ) {
-      self.catalogEntry.loadSqlAnalyzerPopularityForChildren({ silenceErrors: true }).then(() => {
-        loadEntriesDeferred.done(() => {
-          if (!self.hasErrors()) {
-            self.entries().forEach(entry => {
-              if (entry.catalogEntry.sqlAnalyzerPopularity) {
-                if (entry.catalogEntry.sqlAnalyzerPopularity.popularity) {
-                  entry.popularity(entry.catalogEntry.sqlAnalyzerPopularity.popularity);
-                } else if (entry.catalogEntry.sqlAnalyzerPopularity.column_count) {
-                  entry.popularity(entry.catalogEntry.sqlAnalyzerPopularity.column_count);
-                } else if (entry.catalogEntry.sqlAnalyzerPopularity.selectColumn) {
-                  entry.popularity(
-                    entry.catalogEntry.sqlAnalyzerPopularity.selectColumn.columnCount
-                  );
+      const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(self.catalogEntry.getConnector());
+      self.catalogEntry
+        .loadSqlAnalyzerPopularityForChildren({ silenceErrors: true, sqlAnalyzer })
+        .then(() => {
+          loadEntriesDeferred.done(() => {
+            if (!self.hasErrors()) {
+              self.entries().forEach(entry => {
+                if (entry.catalogEntry.sqlAnalyzerPopularity) {
+                  if (entry.catalogEntry.sqlAnalyzerPopularity.popularity) {
+                    entry.popularity(entry.catalogEntry.sqlAnalyzerPopularity.popularity);
+                  } else if (entry.catalogEntry.sqlAnalyzerPopularity.column_count) {
+                    entry.popularity(entry.catalogEntry.sqlAnalyzerPopularity.column_count);
+                  } else if (entry.catalogEntry.sqlAnalyzerPopularity.selectColumn) {
+                    entry.popularity(
+                      entry.catalogEntry.sqlAnalyzerPopularity.selectColumn.columnCount
+                    );
+                  }
                 }
-              }
-            });
-          }
+              });
+            }
+          });
         });
-      });
     }
 
     self.catalogEntry

+ 29 - 25
desktop/core/src/desktop/js/ko/components/assist/assistDbNamespace.js

@@ -14,6 +14,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import sqlAnalyzerRepository from 'catalog/analyzer/sqlAnalyzerRepository';
 import $ from 'jquery';
 import * as ko from 'knockout';
 
@@ -118,34 +119,37 @@ class AssistDbNamespace {
       }
 
       if (window.HAS_SQL_ANALYZER && db && !db.popularityIndexSet && !self.nonSqlType) {
-        db.catalogEntry.loadSqlAnalyzerPopularityForChildren({ silenceErrors: true }).then(() => {
-          const applyPopularity = () => {
-            db.entries().forEach(entry => {
-              if (
-                entry.catalogEntry.sqlAnalyzerPopularity &&
-                entry.catalogEntry.sqlAnalyzerPopularity.popularity >= 5
-              ) {
-                entry.popularity(entry.catalogEntry.sqlAnalyzerPopularity.popularity);
-              }
-            });
-          };
+        const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(db.catalogEntry.getConnector());
+        db.catalogEntry
+          .loadSqlAnalyzerPopularityForChildren({ silenceErrors: true, sqlAnalyzer })
+          .then(() => {
+            const applyPopularity = () => {
+              db.entries().forEach(entry => {
+                if (
+                  entry.catalogEntry.sqlAnalyzerPopularity &&
+                  entry.catalogEntry.sqlAnalyzerPopularity.popularity >= 5
+                ) {
+                  entry.popularity(entry.catalogEntry.sqlAnalyzerPopularity.popularity);
+                }
+              });
+            };
 
-          if (db.loading()) {
-            const subscription = db.loading.subscribe(() => {
-              subscription.dispose();
-              applyPopularity();
-            });
-          } else if (db.entries().length === 0) {
-            const subscription = db.entries.subscribe(newEntries => {
-              if (newEntries.length > 0) {
+            if (db.loading()) {
+              const subscription = db.loading.subscribe(() => {
                 subscription.dispose();
                 applyPopularity();
-              }
-            });
-          } else {
-            applyPopularity();
-          }
-        });
+              });
+            } else if (db.entries().length === 0) {
+              const subscription = db.entries.subscribe(newEntries => {
+                if (newEntries.length > 0) {
+                  subscription.dispose();
+                  applyPopularity();
+                }
+              });
+            } else {
+              applyPopularity();
+            }
+          });
       }
     });
 

+ 9 - 2
desktop/core/src/desktop/js/ko/components/ko.catalogEntriesList.js

@@ -14,6 +14,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import sqlAnalyzerRepository from 'catalog/analyzer/sqlAnalyzerRepository';
 import $ from 'jquery';
 import * as ko from 'knockout';
 
@@ -461,10 +462,12 @@ class CatalogEntriesList {
           self.loading(false);
         });
 
+      const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(self.catalogEntry().getConnector());
+
       if (self.catalogEntry().isTableOrView()) {
         const joinsPromise = self
           .catalogEntry()
-          .getTopJoins({ silenceErrors: true, cancellable: true });
+          .getTopJoins({ cancellable: true, silenceErrors: true, sqlAnalyzer });
         joinsPromise
           .then(topJoins => {
             if (topJoins && topJoins.values && topJoins.values.length) {
@@ -518,7 +521,11 @@ class CatalogEntriesList {
       self.cancellablePromises.push(
         self
           .catalogEntry()
-          .loadSqlAnalyzerPopularityForChildren({ silenceErrors: true, cancellable: true })
+          .loadSqlAnalyzerPopularityForChildren({
+            cancellable: true,
+            silenceErrors: true,
+            sqlAnalyzer
+          })
           .then(popularEntries => {
             if (popularEntries.length) {
               childPromise

+ 30 - 15
desktop/core/src/desktop/js/sql/autocompleteResults.js

@@ -18,6 +18,7 @@ import $ from 'jquery';
 import * as ko from 'knockout';
 
 import apiHelper from 'api/apiHelper';
+import sqlAnalyzerRepository from 'catalog/analyzer/sqlAnalyzerRepository';
 import dataCatalog from 'catalog/dataCatalog';
 import HueColors from 'utils/hueColors';
 import hueUtils from 'utils/hueUtils';
@@ -1483,8 +1484,9 @@ class AutocompleteResults {
     }
     this.loadingJoins(true);
 
+    const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(this.snippet.connector());
     const paths = this.tableIdentifierChainsToPaths(suggestJoins.tables);
-    if (!paths.length) {
+    if (!sqlAnalyzer || !paths.length) {
       return [];
     }
 
@@ -1506,9 +1508,10 @@ class AutocompleteResults {
       const topJoins = await new Promise((resolve, reject) => {
         this.onCancelFunctions.push(reject);
         const topJoinsPromise = multiTableEntry.getTopJoins({
-          silenceErrors: true,
           cancellable: true,
-          connector: this.snippet.connector()
+          connector: this.snippet.connector(),
+          silenceErrors: true,
+          sqlAnalyzer
         });
         this.cancellablePromises.push(topJoinsPromise);
         topJoinsPromise.then(resolve).catch(reject);
@@ -1607,8 +1610,9 @@ class AutocompleteResults {
     }
     this.loadingJoinConditions(true);
 
+    const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(this.snippet.connector());
     const paths = this.tableIdentifierChainsToPaths(suggestJoinConditions.tables);
-    if (!paths.length) {
+    if (!sqlAnalyzer || !paths.length) {
       return [];
     }
 
@@ -1631,9 +1635,10 @@ class AutocompleteResults {
       const topJoins = await new Promise((resolve, reject) => {
         this.onCancelFunctions.push(reject);
         const topJoinsPromise = multiTableEntry.getTopJoins({
-          silenceErrors: true,
           cancellable: true,
-          connector: this.snippet.connector()
+          connector: this.snippet.connector(),
+          silenceErrors: true,
+          sqlAnalyzer
         });
         this.cancellablePromises.push(topJoinsPromise);
         topJoinsPromise.then(resolve).catch(reject);
@@ -1701,8 +1706,9 @@ class AutocompleteResults {
 
     this.loadingAggregateFunctions(true);
 
+    const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(this.snippet.connector());
     const paths = this.tableIdentifierChainsToPaths(suggestAggregateFunctions.tables);
-    if (!paths.length) {
+    if (!sqlAnalyzer || !paths.length) {
       return [];
     }
 
@@ -1725,9 +1731,10 @@ class AutocompleteResults {
       const topAggs = await new Promise((resolve, reject) => {
         this.onCancelFunctions.push(reject);
         const topAggsPromise = multiTableEntry.getTopAggs({
-          silenceErrors: true,
           cancellable: true,
-          connector: this.snippet.connector()
+          connector: this.snippet.connector(),
+          silenceErrors: true,
+          sqlAnalyzer
         });
         this.cancellablePromises.push(topAggsPromise);
         topAggsPromise.then(resolve).catch(reject);
@@ -1840,12 +1847,14 @@ class AutocompleteResults {
     try {
       const entries = await new Promise((resolve, reject) => {
         this.onCancelFunctions.push(reject);
+        const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(this.snippet.connector());
         const popularityPromise = dataCatalog
           .getCatalog(this.snippet.connector())
           .loadSqlAnalyzerPopularityForTables({
             namespace: this.snippet.namespace(),
             compute: this.snippet.compute(),
-            paths: paths,
+            paths,
+            sqlAnalyzer,
             silenceErrors: true,
             cancellable: true
           });
@@ -1947,8 +1956,9 @@ class AutocompleteResults {
     }
     this.loadingFilters(true);
 
+    const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(this.snippet.connector());
     const paths = this.tableIdentifierChainsToPaths(suggestFilters.tables);
-    if (!paths.length) {
+    if (!sqlAnalyzer || !paths.length) {
       return [];
     }
 
@@ -1971,9 +1981,10 @@ class AutocompleteResults {
       const topFilters = await new Promise((resolve, reject) => {
         this.onCancelFunctions.push(reject);
         const topFiltersPromise = multiTableEntry.getTopFilters({
-          silenceErrors: true,
           cancellable: true,
-          connector: this.snippet.connector()
+          connector: this.snippet.connector(),
+          silenceErrors: true,
+          sqlAnalyzer
         });
         this.cancellablePromises.push(topFiltersPromise);
         topFiltersPromise.then(resolve).catch(reject);
@@ -2063,9 +2074,11 @@ class AutocompleteResults {
 
       const childEntries = await new Promise((resolve, reject) => {
         this.onCancelFunctions.push(reject);
+        const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(entry.getConnector());
         const popularityPromise = entry.loadSqlAnalyzerPopularityForChildren({
+          cancellable: true,
           silenceErrors: true,
-          cancellable: true
+          sqlAnalyzer
         });
         this.cancellablePromises.push(popularityPromise);
         popularityPromise.then(resolve).catch(reject);
@@ -2138,12 +2151,14 @@ class AutocompleteResults {
 
       const popularEntries = await new Promise((resolve, reject) => {
         this.onCancelFunctions.push(reject);
+        const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(this.snippet.connector());
         const popularityPromise = dataCatalog
           .getCatalog(this.snippet.connector())
           .loadSqlAnalyzerPopularityForTables({
             namespace: this.snippet.namespace(),
             compute: this.snippet.compute(),
-            paths: paths,
+            paths,
+            sqlAnalyzer,
             silenceErrors: true,
             cancellable: true
           });