浏览代码

HUE-9207 [frontend] Introduce optimizer strategies

For this change I also had to introduce connectors throughout, for now it's only used to determine which optimizer strategy to use
Johan Ahlen 5 年之前
父节点
当前提交
9b275e914e
共有 31 个文件被更改,包括 491 次插入278 次删除
  1. 9 0
      desktop/core/src/desktop/js/apps/notebook/snippet.js
  2. 27 27
      desktop/core/src/desktop/js/apps/notebook2/snippet.js
  3. 6 0
      desktop/core/src/desktop/js/apps/table_browser/app.js
  4. 6 0
      desktop/core/src/desktop/js/apps/table_browser/metastoreNamespace.js
  5. 3 1
      desktop/core/src/desktop/js/catalog/catalogUtils.js
  6. 41 16
      desktop/core/src/desktop/js/catalog/dataCatalog.js
  7. 13 7
      desktop/core/src/desktop/js/catalog/dataCatalogEntry.js
  8. 29 14
      desktop/core/src/desktop/js/catalog/multiTableEntry.js
  9. 164 0
      desktop/core/src/desktop/js/catalog/optimizer/apiStrategy.js
  10. 102 0
      desktop/core/src/desktop/js/catalog/optimizer/baseStrategy.js
  11. 30 0
      desktop/core/src/desktop/js/catalog/optimizer/optimizer.js
  12. 0 205
      desktop/core/src/desktop/js/catalog/optimizer/optimizerApiHelper.js
  13. 2 0
      desktop/core/src/desktop/js/jquery/plugins/jquery.hiveautocomplete.js
  14. 1 0
      desktop/core/src/desktop/js/ko/bindings/ace/aceLocationHandler.js
  15. 8 0
      desktop/core/src/desktop/js/ko/bindings/ko.sqlContextPopover.js
  16. 3 0
      desktop/core/src/desktop/js/ko/components/assist/assistDbNamespace.js
  17. 4 0
      desktop/core/src/desktop/js/ko/components/assist/assistDbSource.js
  18. 2 0
      desktop/core/src/desktop/js/ko/components/assist/ko.assistDashboardPanel.js
  19. 3 1
      desktop/core/src/desktop/js/ko/components/assist/ko.assistDbPanel.js
  20. 2 0
      desktop/core/src/desktop/js/ko/components/assist/ko.assistEditorContextPanel.js
  21. 1 0
      desktop/core/src/desktop/js/ko/components/contextPopover/asteriskContextTabs.js
  22. 1 0
      desktop/core/src/desktop/js/ko/components/contextPopover/dataCatalogContext.js
  23. 6 1
      desktop/core/src/desktop/js/ko/components/contextPopover/ko.contextPopover.js
  24. 2 0
      desktop/core/src/desktop/js/ko/components/ko.contextSelector.js
  25. 1 0
      desktop/core/src/desktop/js/ko/components/ko.historyPanel.js
  26. 1 0
      desktop/core/src/desktop/js/ko/components/ko.pollingCatalogEntriesList.js
  27. 11 2
      desktop/core/src/desktop/js/sql/autocompleteResults.js
  28. 1 0
      desktop/core/src/desktop/js/sql/autocompleteResults.test.js
  29. 2 0
      desktop/core/src/desktop/models.py
  30. 5 2
      desktop/core/src/desktop/static/desktop/js/jquery.hiveautocomplete.js
  31. 5 2
      desktop/libs/indexer/src/indexer/templates/importer.mako

+ 9 - 0
desktop/core/src/desktop/js/apps/notebook/snippet.js

@@ -179,6 +179,14 @@ class Snippet {
       self.status('ready');
     });
 
+    self.connector = ko.pureComputed(() => {
+      // To support optimizer changes in editor v2
+      if (self.type() === 'hive' || self.type() === 'impala') {
+        return { optimizer: 'api' };
+      }
+      return {};
+    });
+
     self.isBatchable = ko.computed(() => {
       return (
         self.type() == 'hive' ||
@@ -1052,6 +1060,7 @@ class Snippet {
                 sourceType: self.type(),
                 namespace: self.namespace(),
                 compute: self.compute(),
+                connector: self.connector(),
                 path: path
               })
               .done(entry => {

+ 27 - 27
desktop/core/src/desktop/js/apps/notebook2/snippet.js

@@ -45,11 +45,7 @@ import { EXECUTE_ACTIVE_EXECUTABLE_EVENT } from 'apps/notebook2/components/ko.ex
 import { UPDATE_HISTORY_EVENT } from 'apps/notebook2/components/ko.queryHistory';
 import { GET_KNOWN_CONFIG_EVENT } from 'utils/hueConfig';
 import { cancelActiveRequest } from 'api/apiUtils';
-import {
-  analyzeCompatibility,
-  analyzeRisk,
-  analyzeSimilarity
-} from 'catalog/optimizer/optimizerApiHelper';
+import { getOptimizer } from 'catalog/optimizer/optimizer';
 
 // TODO: Remove for ENABLE_NOTEBOOK_2. Temporary here for debug
 window.SqlExecutable = SqlExecutable;
@@ -915,10 +911,11 @@ export default class Snippet {
           return true;
         });
         if (unknownResponse) {
-          lastComplexityRequest = analyzeRisk({
-            notebookJson: await this.parentNotebook.toContextJson(),
-            snippetJson: this.toContextJson()
-          })
+          lastComplexityRequest = getOptimizer(this.connector())
+            .analyzeRisk({
+              notebookJson: await this.parentNotebook.toContextJson(),
+              snippetJson: this.toContextJson()
+            })
             .then(data => {
               knownResponses.unshift({
                 hash: hash,
@@ -1220,18 +1217,20 @@ export default class Snippet {
   async getSimilarQueries() {
     hueAnalytics.log('notebook', 'get_query_similarity');
 
-    analyzeSimilarity({
-      notebookJson: await this.parentNotebook.toContextJson(),
-      snippetJson: this.toContextJson(),
-      sourcePlatform: this.dialect()
-    }).then(data => {
-      if (data.status === 0) {
-        // eslint-disable-next-line no-restricted-syntax
-        console.log(data.statement_similarity);
-      } else {
-        $(document).trigger('error', data.message);
-      }
-    });
+    getOptimizer(this.connector())
+      .analyzeSimilarity({
+        notebookJson: await this.parentNotebook.toContextJson(),
+        snippetJson: this.toContextJson(),
+        sourcePlatform: this.dialect()
+      })
+      .then(data => {
+        if (data.status === 0) {
+          // eslint-disable-next-line no-restricted-syntax
+          console.log(data.statement_similarity);
+        } else {
+          $(document).trigger('error', data.message);
+        }
+      });
   }
 
   handleAjaxError(data, callback) {
@@ -1351,12 +1350,13 @@ export default class Snippet {
     this.hasSuggestion(null);
     const positionStatement = this.positionStatement();
 
-    this.lastCompatibilityRequest = analyzeCompatibility({
-      notebookJson: await this.parentNotebook.toContextJson(),
-      snippetJson: this.toContextJson(),
-      sourcePlatform: this.compatibilitySourcePlatform().value,
-      targetPlatform: this.compatibilityTargetPlatform().value
-    })
+    this.lastCompatibilityRequest = getOptimizer(this.connector())
+      .analyzeCompatibility({
+        notebookJson: await this.parentNotebook.toContextJson(),
+        snippetJson: this.toContextJson(),
+        sourcePlatform: this.compatibilitySourcePlatform().value,
+        targetPlatform: this.compatibilityTargetPlatform().value
+      })
       .then(data => {
         if (data.status === 0) {
           this.aceErrorsHolder([]);

+ 6 - 0
desktop/core/src/desktop/js/apps/table_browser/app.js

@@ -138,12 +138,18 @@ huePubSub.subscribe('app.dom.loaded', app => {
   huePubSub.publish(GET_KNOWN_CONFIG_EVENT, configUpdated);
   huePubSub.subscribe(CONFIG_REFRESHED_EVENT, configUpdated);
 
+  // TODO: Use connectors in the table browser
+  const connector = {};
+  if (viewModel.source().type === 'hive' || viewModel.source().type === 'impala') {
+    connector.optimizer = 'api';
+  }
   if (location.getParameter('refresh') === 'true') {
     dataCatalog
       .getEntry({
         namespace: viewModel.source().namespace().namespace,
         compute: viewModel.source().namespace().compute,
         sourceType: viewModel.source().type,
+        connector: connector,
         path: [],
         definition: { type: 'source' }
       })

+ 6 - 0
desktop/core/src/desktop/js/apps/table_browser/metastoreNamespace.js

@@ -61,10 +61,16 @@ class MetastoreNamespace {
         this.loading(false);
       });
 
+    // TODO: Use connectors in the table browser
+    const connector = {};
+    if (this.sourceType === 'hive' || this.sourceType === 'impala') {
+      connector.optimizer = 'api';
+    }
     dataCatalog
       .getEntry({
         namespace: this.namespace,
         compute: this.compute,
+        connector: connector,
         sourceType: this.sourceType,
         path: [],
         definition: { type: 'source' }

+ 3 - 1
desktop/core/src/desktop/js/catalog/catalogUtils.js

@@ -27,7 +27,9 @@ import apiHelper from 'api/apiHelper';
  */
 const fetchAndSave = (apiHelperFunction, attributeName, entry, apiOptions) => {
   const func =
-    typeof apiHelperFunction === 'string' ? apiHelper[apiHelperFunction] : apiHelperFunction;
+    typeof apiHelperFunction === 'string'
+      ? apiHelper[apiHelperFunction].bind(apiHelper)
+      : apiHelperFunction;
 
   return func({
     sourceType: entry.dataCatalog.sourceType,

+ 41 - 16
desktop/core/src/desktop/js/catalog/dataCatalog.js

@@ -22,7 +22,7 @@ import catalogUtils from 'catalog/catalogUtils';
 import DataCatalogEntry from 'catalog/dataCatalogEntry';
 import GeneralDataCatalog from 'catalog/generalDataCatalog';
 import MultiTableEntry from 'catalog/multiTableEntry';
-import { fetchPopularity } from './optimizer/optimizerApiHelper';
+import { getOptimizer } from './optimizer/optimizer';
 
 const STORAGE_POSTFIX = window.LOGGED_USERNAME;
 const DATA_CATALOG_VERSION = 5;
@@ -124,12 +124,14 @@ const mergeMultiTableEntry = function(multiTableCatalogEntry, storeEntry) {
 class DataCatalog {
   /**
    * @param {string} sourceType
+   * @param {Connector} connector
    *
    * @constructor
    */
-  constructor(sourceType) {
+  constructor(sourceType, connector) {
     const self = this;
     self.sourceType = sourceType;
+    self.connector = connector;
     self.entries = {};
     self.temporaryEntries = {};
     self.multiTableEntries = {};
@@ -161,8 +163,12 @@ class DataCatalog {
    * @return {boolean}
    */
   canHaveOptimizerMeta() {
-    const self = this;
-    return HAS_OPTIMIZER && (self.sourceType === 'hive' || self.sourceType === 'impala');
+    return (
+      HAS_OPTIMIZER &&
+      this.connector &&
+      this.connector.optimizer &&
+      this.connector.optimizer !== 'off'
+    );
   }
 
   /**
@@ -258,6 +264,7 @@ class DataCatalog {
    * @param {Object} options
    * @param {ContextNamespace} options.namespace - The context namespace
    * @param {ContextCompute} options.compute - The context compute
+   * @param {Connector} options.connector
    * @param {string[][]} options.paths
    * @param {boolean} [options.silenceErrors] - Default true
    * @param {boolean} [options.cancellable] - Default false
@@ -313,10 +320,11 @@ class DataCatalog {
       const loadDeferred = $.Deferred();
       if (pathsToLoad.length) {
         cancellablePromises.push(
-          fetchPopularity({
-            silenceErrors: options.silenceErrors,
-            paths: pathsToLoad
-          })
+          getOptimizer(options.connector)
+            .fetchPopularity({
+              silenceErrors: options.silenceErrors,
+              paths: pathsToLoad
+            })
             .done(data => {
               const perTable = {};
 
@@ -415,6 +423,7 @@ class DataCatalog {
    * @param {string} options.name
    * @param {ContextNamespace} options.namespace - The context namespace
    * @param {ContextCompute} options.compute - The context compute
+   * @param {Connector} options.connector
    *
    * @param {Object[]} options.columns
    * @param {string} options.columns[].name
@@ -715,6 +724,7 @@ class DataCatalog {
    * @param {Object} options
    * @param {ContextNamespace} options.namespace - The context namespace
    * @param {ContextCompute} options.compute - The context compute
+   * @param {Connector} options.connector
    * @param {string[][]} options.paths
    *
    * @return {Promise}
@@ -732,7 +742,11 @@ class DataCatalog {
     if (!cacheEnabled) {
       deferred
         .resolve(
-          new MultiTableEntry({ identifier: identifier, dataCatalog: self, paths: options.paths })
+          new MultiTableEntry({
+            identifier: identifier,
+            dataCatalog: self,
+            paths: options.paths
+          })
         )
         .promise();
     } else {
@@ -752,7 +766,11 @@ class DataCatalog {
         .catch(error => {
           console.warn(error);
           deferred.resolve(
-            new MultiTableEntry({ identifier: identifier, dataCatalog: self, paths: options.paths })
+            new MultiTableEntry({
+              identifier: identifier,
+              dataCatalog: self,
+              paths: options.paths
+            })
           );
         });
     }
@@ -795,15 +813,17 @@ const sourceBoundCatalogs = {};
  * Helper function to get the DataCatalog instance for a given data source.
  *
  * @param {string} sourceType
+ * @param {Connector} connector
+ *
  * @return {DataCatalog}
  */
-const getCatalog = function(sourceType) {
+const getCatalog = function(sourceType, connector) {
   if (!sourceType) {
     throw new Error('getCatalog called without sourceType');
   }
   return (
     sourceBoundCatalogs[sourceType] ||
-    (sourceBoundCatalogs[sourceType] = new DataCatalog(sourceType))
+    (sourceBoundCatalogs[sourceType] = new DataCatalog(sourceType, connector))
   );
 };
 
@@ -818,6 +838,7 @@ export default {
    * @param {string} options.sourceType
    * @param {ContextNamespace} options.namespace - The context namespace
    * @param {ContextCompute} options.compute - The context compute
+   * @param {Connector} options.connector
    * @param {string} options.name
    *
    * @param {Object[]} options.columns
@@ -828,7 +849,7 @@ export default {
    * @return {Object}
    */
   addTemporaryTable: function(options) {
-    return getCatalog(options.sourceType).addTemporaryTable(options);
+    return getCatalog(options.sourceType, options.connector).addTemporaryTable(options);
   },
 
   /**
@@ -836,6 +857,7 @@ export default {
    * @param {string} options.sourceType
    * @param {ContextNamespace} options.namespace - The context namespace
    * @param {ContextCompute} options.compute - The context compute
+   * @param {Connector} options.connector
    * @param {string|string[]} options.path
    * @param {Object} [options.definition] - Optional initial definition
    * @param {boolean} [options.temporaryOnly] - Default: false
@@ -843,7 +865,7 @@ export default {
    * @return {Promise}
    */
   getEntry: function(options) {
-    return getCatalog(options.sourceType).getEntry(options);
+    return getCatalog(options.sourceType, options.connector).getEntry(options);
   },
 
   /**
@@ -851,12 +873,13 @@ export default {
    * @param {string} options.sourceType
    * @param {ContextNamespace} options.namespace - The context namespace
    * @param {ContextCompute} options.compute - The context compute
+   * @param {Connector} options.connector
    * @param {string[][]} options.paths
    *
    * @return {Promise}
    */
   getMultiTableEntry: function(options) {
-    return getCatalog(options.sourceType).getMultiTableEntry(options);
+    return getCatalog(options.sourceType, options.connector).getMultiTableEntry(options);
   },
 
   /**
@@ -867,6 +890,7 @@ export default {
    * @param {string} options.sourceType
    * @param {ContextNamespace} options.namespace - The context namespace
    * @param {ContextCompute} options.compute - The context compute
+   * @param {Connector} options.connector
    * @param {string|string[]} options.path
    * @param {Object} [options.definition] - Optional initial definition of the parent entry
    * @param {boolean} [options.silenceErrors]
@@ -879,7 +903,7 @@ export default {
   getChildren: function(options) {
     const deferred = $.Deferred();
     const cancellablePromises = [];
-    getCatalog(options.sourceType)
+    getCatalog(options.sourceType, options.connector)
       .getEntry(options)
       .done(entry => {
         cancellablePromises.push(
@@ -895,6 +919,7 @@ export default {
 
   /**
    * @param {string} sourceType
+   * @param {Connector} connector
    *
    * @return {DataCatalog}
    */

+ 13 - 7
desktop/core/src/desktop/js/catalog/dataCatalogEntry.js

@@ -22,7 +22,7 @@ import CancellablePromise from 'api/cancellablePromise';
 import catalogUtils from 'catalog/catalogUtils';
 import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';
-import { fetchOptimizerMeta, fetchPopularity } from './optimizer/optimizerApiHelper';
+import { getOptimizer } from './optimizer/optimizer';
 
 /**
  * Helper function to reload the source meta for the given entry
@@ -155,7 +155,12 @@ const reloadOptimizerMeta = function(dataCatalogEntry, apiOptions) {
   if (dataCatalogEntry.dataCatalog.canHaveOptimizerMeta()) {
     return dataCatalogEntry.trackedPromise(
       'optimizerMetaPromise',
-      catalogUtils.fetchAndSave(fetchOptimizerMeta, 'optimizerMeta', dataCatalogEntry, apiOptions)
+      catalogUtils.fetchAndSave(
+        getOptimizer(dataCatalogEntry.dataCatalog.connector).fetchOptimizerMeta,
+        'optimizerMeta',
+        dataCatalogEntry,
+        apiOptions
+      )
     );
   }
   dataCatalogEntry.optimizerMetaPromise = $.Deferred.reject().promise();
@@ -753,11 +758,12 @@ class DataCatalogEntry {
       );
     } else if (self.isDatabase() || self.isTableOrView()) {
       cancellablePromises.push(
-        fetchPopularity({
-          silenceErrors: options && options.silenceErrors,
-          refreshCache: options && options.refreshCache,
-          paths: [self.path]
-        })
+        getOptimizer(self.dataCatalog.connector)
+          .fetchPopularity({
+            silenceErrors: options && options.silenceErrors,
+            refreshCache: options && options.refreshCache,
+            paths: [self.path]
+          })
           .done(data => {
             cancellablePromises.push(
               self

+ 29 - 14
desktop/core/src/desktop/js/catalog/multiTableEntry.js

@@ -17,12 +17,7 @@
 import $ from 'jquery';
 
 import catalogUtils from 'catalog/catalogUtils';
-import {
-  fetchTopAggs,
-  fetchTopColumns,
-  fetchTopFilters,
-  fetchTopJoins
-} from './optimizer/optimizerApiHelper';
+import { getOptimizer } from './optimizer/optimizer';
 
 /**
  * Helper function to reload a Optimizer multi table attribute, like topAggs or topFilters
@@ -185,8 +180,13 @@ class MultiTableEntry {
    * @return {CancellablePromise}
    */
   getTopAggs(options) {
-    const self = this;
-    return genericOptimizerGet(self, options, 'topAggsPromise', 'topAggs', fetchTopAggs);
+    return genericOptimizerGet(
+      this,
+      options,
+      'topAggsPromise',
+      'topAggs',
+      getOptimizer(this.dataCatalog.connector).fetchTopAggs
+    );
   }
 
   /**
@@ -201,8 +201,13 @@ class MultiTableEntry {
    * @return {CancellablePromise}
    */
   getTopColumns(options) {
-    const self = this;
-    return genericOptimizerGet(self, options, 'topColumnsPromise', 'topColumns', fetchTopColumns);
+    return genericOptimizerGet(
+      this,
+      options,
+      'topColumnsPromise',
+      'topColumns',
+      getOptimizer(this.dataCatalog.connector).fetchTopColumns
+    );
   }
 
   /**
@@ -217,8 +222,13 @@ class MultiTableEntry {
    * @return {CancellablePromise}
    */
   getTopFilters(options) {
-    const self = this;
-    return genericOptimizerGet(self, options, 'topFiltersPromise', 'topFilters', fetchTopFilters);
+    return genericOptimizerGet(
+      this,
+      options,
+      'topFiltersPromise',
+      'topFilters',
+      getOptimizer(this.dataCatalog.connector).fetchTopFilters
+    );
   }
 
   /**
@@ -233,8 +243,13 @@ class MultiTableEntry {
    * @return {CancellablePromise}
    */
   getTopJoins(options) {
-    const self = this;
-    return genericOptimizerGet(self, options, 'topJoinsPromise', 'topJoins', fetchTopJoins);
+    return genericOptimizerGet(
+      this,
+      options,
+      'topJoinsPromise',
+      'topJoins',
+      getOptimizer(this.dataCatalog.connector).fetchTopJoins
+    );
   }
 }
 

+ 164 - 0
desktop/core/src/desktop/js/catalog/optimizer/apiStrategy.js

@@ -0,0 +1,164 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import $ from 'jquery';
+import * as ko from 'knockout';
+
+import CancellablePromise from 'api/cancellablePromise';
+import { simplePost } from 'api/apiUtils';
+import BaseStrategy from './baseStrategy';
+
+const OPTIMIZER_URLS = {
+  COMPATIBILITY: '/notebook/api/optimizer/statement/compatibility',
+  RISK: '/notebook/api/optimizer/statement/risk',
+  SIMILARITY: '/notebook/api/optimizer/statement/similarity',
+  TOP_AGGS: '/metadata/api/optimizer/top_aggs',
+  TOP_COLUMNS: '/metadata/api/optimizer/top_columns',
+  TOP_FILTERS: '/metadata/api/optimizer/top_filters',
+  TOP_JOINS: '/metadata/api/optimizer/top_joins',
+  TOP_TABLES: '/metadata/api/optimizer/top_tables',
+  TABLE_DETAILS: '/metadata/api/optimizer/table_details'
+};
+
+/**
+ * Fetches the popularity for various aspects of the given tables
+ *
+ * @param {OptimizerOptions} options
+ * @param {string} url
+ * @return {CancellablePromise}
+ */
+const genericOptimizerMultiTableFetch = (options, url) => {
+  const deferred = $.Deferred();
+
+  const dbTables = {};
+  options.paths.forEach(path => {
+    dbTables[path.join('.')] = true;
+  });
+  const data = {
+    dbTables: ko.mapping.toJSON(Object.keys(dbTables))
+  };
+
+  const request = simplePost(url, data, {
+    silenceErrors: options.silenceErrors,
+    successCallback: data => {
+      data.hueTimestamp = Date.now();
+      deferred.resolve(data);
+    },
+    errorCallback: deferred.reject
+  });
+
+  return new CancellablePromise(deferred, request);
+};
+
+export default class ApiStrategy extends BaseStrategy {
+  analyzeCompatibility(options) {
+    return simplePost(OPTIMIZER_URLS.COMPATIBILITY, {
+      notebook: options.notebookJson,
+      snippet: options.snippetJson,
+      sourcePlatform: options.sourcePlatform,
+      targetPlatform: options.targetPlatform
+    });
+  }
+
+  analyzeRisk(options) {
+    return simplePost(OPTIMIZER_URLS.RISK, {
+      notebook: options.notebookJson,
+      snippet: options.snippetJson
+    });
+  }
+
+  analyzeSimilarity(options) {
+    return simplePost(OPTIMIZER_URLS.SIMILARITY, {
+      notebook: options.notebookJson,
+      snippet: options.snippetJson,
+      sourcePlatform: options.sourcePlatform
+    });
+  }
+
+  fetchPopularity(options) {
+    const deferred = $.Deferred();
+    let url, data;
+
+    if (options.paths.length === 1 && options.paths[0].length === 1) {
+      url = OPTIMIZER_URLS.TOP_TABLES;
+      data = {
+        database: options.paths[0][0]
+      };
+    } else {
+      url = OPTIMIZER_URLS.TOP_COLUMNS;
+      const dbTables = [];
+      options.paths.forEach(path => {
+        dbTables.push(path.join('.'));
+      });
+      data = {
+        dbTables: ko.mapping.toJSON(dbTables)
+      };
+    }
+
+    const request = simplePost(url, data, {
+      silenceErrors: options.silenceErrors,
+      successCallback: data => {
+        data.hueTimestamp = Date.now();
+        deferred.resolve(data);
+      },
+      errorCallback: deferred.reject
+    });
+
+    return new CancellablePromise(deferred, request);
+  }
+
+  fetchTopAggs(options) {
+    return genericOptimizerMultiTableFetch(options, OPTIMIZER_URLS.TOP_AGGS);
+  }
+
+  fetchTopColumns(options) {
+    return genericOptimizerMultiTableFetch(options, OPTIMIZER_URLS.TOP_COLUMNS);
+  }
+
+  fetchTopFilters(options) {
+    return genericOptimizerMultiTableFetch(options, OPTIMIZER_URLS.TOP_FILTERS);
+  }
+
+  fetchTopJoins(options) {
+    return genericOptimizerMultiTableFetch(options, OPTIMIZER_URLS.TOP_JOINS);
+  }
+
+  fetchOptimizerMeta(options) {
+    const deferred = $.Deferred();
+
+    const request = simplePost(
+      OPTIMIZER_URLS.TABLE_DETAILS,
+      {
+        databaseName: options.path[0],
+        tableName: options.path[1]
+      },
+      {
+        silenceErrors: options.silenceErrors,
+        successCallback: response => {
+          if (response.status === 0 && response.details) {
+            response.details.hueTimestamp = Date.now();
+            deferred.resolve(response.details);
+          } else {
+            deferred.reject();
+          }
+        },
+        errorCallback: deferred.reject
+      }
+    );
+
+    return new CancellablePromise(deferred, request);
+  }
+}

+ 102 - 0
desktop/core/src/desktop/js/catalog/optimizer/baseStrategy.js

@@ -0,0 +1,102 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import $ from 'jquery';
+
+import CancellablePromise from '/api/cancellablePromise';
+
+export default class BaseStrategy {
+  analyzeRisk(options) {
+    return $.Deferred().reject();
+  }
+
+  analyzeSimilarity(options) {
+    return $.Deferred().reject();
+  }
+
+  analyzeCompatibility(options) {
+    return $.Deferred().reject();
+  }
+
+  /**
+   * @typedef OptimizerOptions
+   * @property {boolean} [options.silenceErrors]
+   * @property {string[][]} options.paths
+   */
+
+  /**
+   * Fetches optimizer popularity for the children of the given path
+   *
+   * @param {OptimizerOptions} options
+   * @return {CancellablePromise}
+   */
+  fetchPopularity(options) {
+    return new CancellablePromise($.Deferred().reject());
+  }
+
+  /**
+   * Fetches the popular aggregate functions for the given tables
+   *
+   * @param {OptimizerOptions} options
+   * @return {CancellablePromise}
+   */
+  fetchTopAggs(options) {
+    return new CancellablePromise($.Deferred().reject());
+  }
+
+  /**
+   * Fetches the popular columns for the given tables
+   *
+   * @param {OptimizerOptions} options
+   * @return {CancellablePromise}
+   */
+  fetchTopColumns(options) {
+    return new CancellablePromise($.Deferred().reject());
+  }
+
+  /**
+   * Fetches the popular filters for the given tables
+   *
+   * @param {OptimizerOptions} options
+   * @return {CancellablePromise}
+   */
+  fetchTopFilters(options) {
+    return new CancellablePromise($.Deferred().reject());
+  }
+
+  /**
+   * Fetches the popular joins for the given tables
+   *
+   * @param {OptimizerOptions} options
+   * @return {CancellablePromise}
+   */
+  fetchTopJoins(options) {
+    return new CancellablePromise($.Deferred().reject());
+  }
+
+  /**
+   * Fetches optimizer meta for the given path, only possible for tables atm.
+   *
+   * @param {Object} options
+   * @param {boolean} [options.silenceErrors]
+   * @param {string[]} options.path
+   *
+   * @return {CancellablePromise}
+   */
+  fetchOptimizerMeta(options) {
+    return new CancellablePromise($.Deferred().reject());
+  }
+}

+ 30 - 0
desktop/core/src/desktop/js/catalog/optimizer/optimizer.js

@@ -0,0 +1,30 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import ApiStrategy from './apiStrategy';
+import BaseStrategy from './baseStrategy';
+
+const OPTIMIZER_STRATEGIES = {
+  api: new ApiStrategy(),
+  off: new BaseStrategy()
+};
+
+export const getOptimizer = connector => {
+  if (connector && connector.optimizer && OPTIMIZER_STRATEGIES[connector.optimizer]) {
+    return OPTIMIZER_STRATEGIES[connector.optimizer];
+  }
+  return OPTIMIZER_STRATEGIES.off;
+};

+ 0 - 205
desktop/core/src/desktop/js/catalog/optimizer/optimizerApiHelper.js

@@ -1,205 +0,0 @@
-// Licensed to Cloudera, Inc. under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  Cloudera, Inc. licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import $ from 'jquery';
-import * as ko from 'knockout';
-
-import CancellablePromise from 'api/cancellablePromise';
-import { simplePost } from 'api/apiUtils';
-
-const OPTIMIZER_URLS = {
-  COMPATIBILITY: '/notebook/api/optimizer/statement/compatibility',
-  RISK: '/notebook/api/optimizer/statement/risk',
-  SIMILARITY: '/notebook/api/optimizer/statement/similarity',
-  TOP_AGGS: '/metadata/api/optimizer/top_aggs',
-  TOP_COLUMNS: '/metadata/api/optimizer/top_columns',
-  TOP_FILTERS: '/metadata/api/optimizer/top_filters',
-  TOP_JOINS: '/metadata/api/optimizer/top_joins',
-  TOP_TABLES: '/metadata/api/optimizer/top_tables',
-  TABLE_DETAILS: '/metadata/api/optimizer/table_details'
-};
-
-/**
- * Fetches the popularity for various aspects of the given tables
- *
- * @param {Object} options
- * @param {boolean} [options.silenceErrors]
- * @param {string[][]} options.paths
- * @param {string} url
- * @return {CancellablePromise}
- */
-const genericOptimizerMultiTableFetch = (options, url) => {
-  const deferred = $.Deferred();
-
-  const dbTables = {};
-  options.paths.forEach(path => {
-    dbTables[path.join('.')] = true;
-  });
-  const data = {
-    dbTables: ko.mapping.toJSON(Object.keys(dbTables))
-  };
-
-  const request = simplePost(url, data, {
-    silenceErrors: options.silenceErrors,
-    successCallback: data => {
-      data.hueTimestamp = Date.now();
-      deferred.resolve(data);
-    },
-    errorCallback: deferred.reject
-  });
-
-  return new CancellablePromise(deferred, request);
-};
-
-export const analyzeRisk = options =>
-  simplePost(OPTIMIZER_URLS.RISK, {
-    notebook: options.notebookJson,
-    snippet: options.snippetJson
-  });
-
-export const analyzeCompatibility = options =>
-  simplePost(OPTIMIZER_URLS.COMPATIBILITY, {
-    notebook: options.notebookJson,
-    snippet: options.snippetJson,
-    sourcePlatform: options.sourcePlatform,
-    targetPlatform: options.targetPlatform
-  });
-
-export const analyzeSimilarity = options =>
-  simplePost(OPTIMIZER_URLS.SIMILARITY, {
-    notebook: options.notebookJson,
-    snippet: options.snippetJson,
-    sourcePlatform: options.sourcePlatform
-  });
-
-/**
- * Fetches optimizer popularity for the children of the given path
- *
- * @param {Object} options
- * @param {boolean} [options.silenceErrors]
- * @param {string[][]} options.paths
- * @return {CancellablePromise}
- */
-export const fetchPopularity = options => {
-  const deferred = $.Deferred();
-  let url, data;
-
-  if (options.paths.length === 1 && options.paths[0].length === 1) {
-    url = OPTIMIZER_URLS.TOP_TABLES;
-    data = {
-      database: options.paths[0][0]
-    };
-  } else {
-    url = OPTIMIZER_URLS.TOP_COLUMNS;
-    const dbTables = [];
-    options.paths.forEach(path => {
-      dbTables.push(path.join('.'));
-    });
-    data = {
-      dbTables: ko.mapping.toJSON(dbTables)
-    };
-  }
-
-  const request = simplePost(url, data, {
-    silenceErrors: options.silenceErrors,
-    successCallback: data => {
-      data.hueTimestamp = Date.now();
-      deferred.resolve(data);
-    },
-    errorCallback: deferred.reject
-  });
-
-  return new CancellablePromise(deferred, request);
-};
-
-/**
- * Fetches the popular aggregate functions for the given tables
- *
- * @param {Object} options
- * @param {boolean} [options.silenceErrors]
- * @param {string[][]} options.paths
- * @return {CancellablePromise}
- */
-export const fetchTopAggs = options =>
-  genericOptimizerMultiTableFetch(options, OPTIMIZER_URLS.TOP_AGGS);
-
-/**
- * Fetches the popular columns for the given tables
- *
- * @param {Object} options
- * @param {boolean} [options.silenceErrors]
- * @param {string[][]} options.paths
- * @return {CancellablePromise}
- */
-export const fetchTopColumns = options =>
-  genericOptimizerMultiTableFetch(options, OPTIMIZER_URLS.TOP_COLUMNS);
-
-/**
- * Fetches the popular filters for the given tables
- *
- * @param {Object} options
- * @param {boolean} [options.silenceErrors]
- * @param {string[][]} options.paths
- * @return {CancellablePromise}
- */
-export const fetchTopFilters = options =>
-  genericOptimizerMultiTableFetch(options, OPTIMIZER_URLS.TOP_FILTERS);
-
-/**
- * Fetches the popular joins for the given tables
- *
- * @param {Object} options
- * @param {boolean} [options.silenceErrors]
- * @param {string[][]} options.paths
- * @return {CancellablePromise}
- */
-export const fetchTopJoins = options =>
-  genericOptimizerMultiTableFetch(options, OPTIMIZER_URLS.TOP_JOINS);
-
-/**
- * Fetches optimizer meta for the given path, only possible for tables atm.
- *
- * @param {Object} options
- * @param {boolean} [options.silenceErrors]
- * @param {string[]} options.path
- *
- * @return {CancellablePromise}
- */
-export const fetchOptimizerMeta = options => {
-  const deferred = $.Deferred();
-
-  const request = simplePost(
-    OPTIMIZER_URLS.TABLE_DETAILS,
-    {
-      databaseName: options.path[0],
-      tableName: options.path[1]
-    },
-    {
-      silenceErrors: options.silenceErrors,
-      successCallback: response => {
-        if (response.status === 0 && response.details) {
-          response.details.hueTimestamp = Date.now();
-          deferred.resolve(response.details);
-        } else {
-          deferred.reject();
-        }
-      },
-      errorCallback: deferred.reject
-    }
-  );
-
-  return new CancellablePromise(deferred, request);
-};

+ 2 - 0
desktop/core/src/desktop/js/jquery/plugins/jquery.hiveautocomplete.js

@@ -348,6 +348,7 @@ Plugin.prototype.init = function() {
           sourceType: self.options.apiHelperType,
           namespace: namespace,
           compute: compute,
+          connector: {},
           path: [database]
         })
         .done(entry => {
@@ -364,6 +365,7 @@ Plugin.prototype.init = function() {
           sourceType: self.options.apiHelperType,
           namespace: namespace,
           compute: compute,
+          connector: {},
           path: [database, table]
         })
         .done(entry => {

+ 1 - 0
desktop/core/src/desktop/js/ko/bindings/ace/aceLocationHandler.js

@@ -185,6 +185,7 @@ class AceLocationHandler {
                         sourceType: self.dialect(),
                         namespace: self.snippet.namespace(),
                         compute: self.snippet.compute(),
+                        connector: self.snippet.connector(),
                         temporaryOnly: self.snippet.autocompleteSettings.temporaryOnly,
                         path: $.map(tableChain, identifier => {
                           return identifier.name;

+ 8 - 0
desktop/core/src/desktop/js/ko/bindings/ko.sqlContextPopover.js

@@ -48,6 +48,14 @@ ko.bindingHandlers.sqlContextPopover = {
       () => {
         return function() {
           const options = valueAccessor();
+
+          // TODO: Use connector for SQL context popover
+          if (
+            !options.connector &&
+            (options.sourceType === 'hive' || options.sourceType === 'impala')
+          ) {
+            options.connector = { optimizer: 'api' };
+          }
           dataCatalog.getEntry(options).done(entry => {
             const $source = $(element);
             const offset = $source.offset();

+ 3 - 0
desktop/core/src/desktop/js/ko/components/assist/assistDbNamespace.js

@@ -27,6 +27,7 @@ class AssistDbNamespace {
    * @param {Object} options
    * @param {Object} options.i18n
    * @param {string} options.sourceType
+   * @param {Connector} options.connector
    * @param {ContextNamespace} options.namespace
    * @param {boolean} options.nonSqlType - Optional, default false
    * @param {Object} options.navigationSettings
@@ -38,6 +39,7 @@ class AssistDbNamespace {
     self.i18n = options.i18n;
     self.navigationSettings = options.navigationSettings;
     self.sourceType = options.sourceType;
+    self.connector = options.connector;
     self.nonSqlType = options.nonSqlType;
 
     self.namespace = options.namespace;
@@ -218,6 +220,7 @@ class AssistDbNamespace {
           sourceType: self.sourceType,
           namespace: self.namespace,
           compute: self.compute(),
+          connector: self.connector,
           path: [],
           definition: { type: 'source' }
         })

+ 4 - 0
desktop/core/src/desktop/js/ko/components/assist/assistDbSource.js

@@ -29,6 +29,7 @@ class AssistDbSource {
    * @param {string} options.type
    * @param {ContextNamespace} [options.initialNamespace] - Optional initial namespace to use
    * @param {ContextCompute} [options.initialCompute] - Optional initial compute to use
+   * @param {Connector} options.connector
    * @param {string} options.name
    * @param {boolean} options.nonSqlType - Optional, default false
    * @param {Object} options.navigationSettings
@@ -38,6 +39,7 @@ class AssistDbSource {
     const self = this;
 
     self.sourceType = options.type;
+    self.connector = options.connector;
     self.name = options.name;
     self.i18n = options.i18n;
     self.nonSqlType = options.nonSqlType;
@@ -130,6 +132,7 @@ class AssistDbSource {
               newNamespaces.push(
                 new AssistDbNamespace({
                   sourceType: self.sourceType,
+                  connector: self.connector,
                   namespace: newNamespace,
                   i18n: self.i18n,
                   nonSqlType: self.nonSqlType,
@@ -169,6 +172,7 @@ class AssistDbSource {
           const assistNamespace = new AssistDbNamespace({
             sourceType: self.sourceType,
             namespace: namespace,
+            connector: self.connector,
             i18n: self.i18n,
             nonSqlType: self.nonSqlType,
             navigationSettings: self.navigationSettings

+ 2 - 0
desktop/core/src/desktop/js/ko/components/assist/ko.assistDashboardPanel.js

@@ -90,6 +90,7 @@ class AssistDashboardPanel {
             sourceType: sourceType,
             namespace: collection.activeNamespace,
             compute: collection.activeCompute,
+            connector: {}, // TODO: Use connectors in assist dashboard panel
             path: [fakeParentName],
             definition: { type: 'database' }
           })
@@ -107,6 +108,7 @@ class AssistDashboardPanel {
                 sourceType: sourceType,
                 namespace: collection.activeNamespace,
                 compute: collection.activeCompute,
+                connector: {}, // TODO: Use connectors in assist dashboard panel
                 path: [
                   fakeParentName,
                   collectionName.indexOf('.') > -1 ? collectionName.split('.')[1] : collectionName

+ 3 - 1
desktop/core/src/desktop/js/ko/components/assist/ko.assistDbPanel.js

@@ -23,7 +23,7 @@ import componentUtils from 'ko/components/componentUtils';
 import dataCatalog from 'catalog/dataCatalog';
 import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';
-import { CONFIG_REFRESHED_EVENT, GET_KNOWN_CONFIG_EVENT } from '../../../utils/hueConfig';
+import { CONFIG_REFRESHED_EVENT, GET_KNOWN_CONFIG_EVENT } from 'utils/hueConfig';
 
 const ASSIST_TABLE_TEMPLATES = `
   <script type="text/html" id="assist-no-database-entries">
@@ -649,6 +649,7 @@ class AssistDbPanel {
                 sourceType: 'solr',
                 namespace: assistDbNamespace.namespace,
                 compute: assistDbNamespace.compute(),
+                connector: {},
                 path: []
               })
               .done(entry => {
@@ -874,6 +875,7 @@ class AssistDbPanel {
                   i18n: self.i18n,
                   type: interpreter.type,
                   name: interpreter.name,
+                  connector: interpreter,
                   nonSqlType: false,
                   navigationSettings: navigationSettings
                 });

+ 2 - 0
desktop/core/src/desktop/js/ko/components/assist/ko.assistEditorContextPanel.js

@@ -423,6 +423,7 @@ class AssistEditorContextPanel {
                     sourceType: activeLocations.type,
                     namespace: activeLocations.namespace,
                     compute: activeLocations.compute,
+                    connector: {}, // TODO: User connectors in assist editor context panel
                     path: [database],
                     definition: { type: 'database' }
                   })
@@ -503,6 +504,7 @@ class AssistEditorContextPanel {
                                   sourceType: activeLocations.type,
                                   namespace: activeLocations.namespace,
                                   compute: activeLocations.compute,
+                                  connector: {}, // TODO: Use connectors in assist editor context panel
                                   path: []
                                 })
                                 .done(sourceEntry => {

+ 1 - 0
desktop/core/src/desktop/js/ko/components/contextPopover/asteriskContextTabs.js

@@ -98,6 +98,7 @@ class AsteriskData {
             sourceType: sourceType,
             namespace: namespace,
             compute: compute,
+            connector: {}, // TODO: Add connector to asteriskContextTabs
             path: path
           })
           .done(entry => {

+ 1 - 0
desktop/core/src/desktop/js/ko/components/contextPopover/dataCatalogContext.js

@@ -61,6 +61,7 @@ class DataCatalogContext {
                 .dataCatalog.getEntry({
                   namespace: self.catalogEntry().namespace,
                   compute: self.catalogEntry().compute,
+                  connector: self.catalogEntry().connector,
                   path: this.path,
                   temporaryOnly: self.catalogEntry().isTemporary
                 })

+ 6 - 1
desktop/core/src/desktop/js/ko/components/contextPopover/ko.contextPopover.js

@@ -933,12 +933,17 @@ class SqlContextContentsGlobalSearch {
 
     if (self.isCatalogEntry) {
       contextCatalog.getNamespaces({ sourceType: sourceType }).done(context => {
-        // TODO: Namespace and compute selection for global search results?
+        // TODO: Connector, Namespace and compute selection for global search results?
+        const connector = {}; // TODO: Add connector to global search
+        if (sourceType === 'hive' || sourceType === 'impala') {
+          connector.optimizer = 'api';
+        }
         dataCatalog
           .getEntry({
             sourceType: sourceType,
             namespace: context.namespaces[0],
             compute: context.namespaces[0].computes[0],
+            connector: connector,
             path: path,
             definition: { type: params.data.type.toLowerCase() }
           })

+ 2 - 0
desktop/core/src/desktop/js/ko/components/ko.contextSelector.js

@@ -403,6 +403,7 @@ HueContextSelector.prototype.reloadDatabases = function() {
   const self = this;
   if (self.database && !self.hideDatabases) {
     self.loadingDatabases(true);
+    const connector = {}; // TODO: Add connectors to the context selector
     $.when(self[TYPES_INDEX.namespace.lastPromise], self[TYPES_INDEX.compute.lastPromise]).done(
       () => {
         window.clearTimeout(self.reloadDatabaseThrottle);
@@ -417,6 +418,7 @@ HueContextSelector.prototype.reloadDatabases = function() {
               sourceType: ko.unwrap(self.sourceType),
               namespace: self[TYPES_INDEX.namespace.name](),
               compute: self[TYPES_INDEX.compute.name](),
+              connector: connector,
               path: [],
               definition: { type: 'source' }
             })

+ 1 - 0
desktop/core/src/desktop/js/ko/components/ko.historyPanel.js

@@ -276,6 +276,7 @@ class HistoryPanel {
                       sourceType: snippet.type(),
                       namespace: snippet.namespace(),
                       compute: snippet.compute(),
+                      connector: snippet.connector(),
                       path: []
                     })
                     .done(entry => {

+ 1 - 0
desktop/core/src/desktop/js/ko/components/ko.pollingCatalogEntriesList.js

@@ -143,6 +143,7 @@ class PollingCatalogEntriesList {
         sourceType: ko.unwrap(self.sourceType),
         namespace: ko.unwrap(self.namespace),
         compute: ko.unwrap(self.compute),
+        connector: {}, // TODO: Use connectors in polling catalog entries list
         path: ko.unwrap(self.path)
       })
       .done(catalogEntry => {

+ 11 - 2
desktop/core/src/desktop/js/sql/autocompleteResults.js

@@ -551,6 +551,7 @@ class AutocompleteResults {
         sourceType: self.dialect(),
         namespace: self.snippet.namespace(),
         compute: self.snippet.compute(),
+        connector: self.snippet.connector(),
         path: [],
         temporaryOnly: self.temporaryOnly
       })
@@ -812,6 +813,7 @@ class AutocompleteResults {
             sourceType: self.dialect(),
             namespace: self.snippet.namespace(),
             compute: self.snippet.compute(),
+            connector: self.snippet.connector(),
             path: [database],
             temporaryOnly: self.temporaryOnly
           })
@@ -1413,6 +1415,7 @@ class AutocompleteResults {
             sourceType: self.dialect(),
             namespace: self.snippet.namespace(),
             compute: self.snippet.compute(),
+            connector: self.snippet.connector(),
             paths: paths
           })
           .done(multiTableEntry => {
@@ -1533,6 +1536,7 @@ class AutocompleteResults {
             sourceType: self.dialect(),
             namespace: self.snippet.namespace(),
             compute: self.snippet.compute(),
+            connector: self.snippet.connector(),
             paths: paths
           })
           .done(multiTableEntry => {
@@ -1622,6 +1626,7 @@ class AutocompleteResults {
             sourceType: self.dialect(),
             namespace: self.snippet.namespace(),
             compute: self.snippet.compute(),
+            connector: self.snippet.connector(),
             paths: paths
           })
           .done(multiTableEntry => {
@@ -1737,7 +1742,7 @@ class AutocompleteResults {
 
     self.cancellablePromises.push(
       dataCatalog
-        .getCatalog(self.dialect())
+        .getCatalog(self.dialect(), self.snippet.connector())
         .loadOptimizerPopularityForTables({
           namespace: self.snippet.namespace(),
           compute: self.snippet.compute(),
@@ -1851,6 +1856,7 @@ class AutocompleteResults {
             sourceType: self.dialect(),
             namespace: self.snippet.namespace(),
             compute: self.snippet.compute(),
+            connector: self.snippet.connector(),
             paths: paths
           })
           .done(multiTableEntry => {
@@ -1942,6 +1948,7 @@ class AutocompleteResults {
           sourceType: self.dialect(),
           namespace: self.snippet.namespace(),
           compute: self.snippet.compute(),
+          connector: self.snippet.connector(),
           path: [db],
           temporaryOnly: self.temporaryOnly
         })
@@ -2026,7 +2033,7 @@ class AutocompleteResults {
 
       self.cancellablePromises.push(
         dataCatalog
-          .getCatalog(self.dialect())
+          .getCatalog(self.dialect(), self.snippet.connector())
           .loadOptimizerPopularityForTables({
             namespace: self.snippet.namespace(),
             compute: self.snippet.compute(),
@@ -2253,6 +2260,7 @@ class AutocompleteResults {
           sourceType: self.dialect(),
           namespace: self.snippet.namespace(),
           compute: self.snippet.compute(),
+          connector: self.snippet.connector(),
           path: fetchedPath,
           temporaryOnly: self.temporaryOnly
         })
@@ -2299,6 +2307,7 @@ class AutocompleteResults {
           sourceType: self.dialect(),
           namespace: self.snippet.namespace(),
           compute: self.snippet.compute(),
+          connector: self.snippet.connector(),
           path: [],
           temporaryOnly: self.temporaryOnly
         })

+ 1 - 0
desktop/core/src/desktop/js/sql/autocompleteResults.test.js

@@ -235,6 +235,7 @@ describe('AutocompleteResults.js', () => {
       type: function() {
         return 'hive';
       },
+      connector: function() {},
       database: function() {
         return 'default';
       },

+ 2 - 0
desktop/core/src/desktop/models.py

@@ -42,6 +42,7 @@ from django.utils.translation import ugettext as _, ugettext_lazy as _t
 
 from dashboard.conf import get_engines, HAS_REPORT_ENABLED
 from kafka.conf import has_kafka
+from metadata.conf import has_optimizer
 from notebook.conf import DEFAULT_LIMIT, SHOW_NOTEBOOKS, get_ordered_interpreters
 from useradmin.models import User, Group, get_organization
 from useradmin.organization import _fitered_queryset
@@ -1790,6 +1791,7 @@ class ClusterConfig(object):
           'displayName': interpreter['name'],
           'buttonName': _('Query'),
           'tooltip': _('%s Query') % interpreter['type'].title(),
+          'optimizer': 'api' if has_optimizer() else 'off',  # TODO: Change to proper values
           'page': '/editor/?type=%(type)s' % interpreter,
           'is_sql': interpreter['is_sql'],
           'is_batchable': interpreter['dialect'] in ['hive', 'impala'] or interpreter['interface'] in ['oozie', 'sqlalchemy'],

+ 5 - 2
desktop/core/src/desktop/static/desktop/js/jquery.hiveautocomplete.js

@@ -279,10 +279,13 @@
       })
     };
 
+    // TODO: Use connector for hive autocomplete
+    const connector = {};
+
     self.getTables = function (database, callback) {
       var self = this;
       $.when(self.namespaceDeferred, self.computeDeferred).done(function (namespace, compute) {
-        dataCatalog.getEntry({ sourceType: self.options.apiHelperType, namespace: namespace, compute: compute, path: [ database ] }).done(function (entry) {
+        dataCatalog.getEntry({ sourceType: self.options.apiHelperType, connector: connector, namespace: namespace, compute: compute, path: [ database ] }).done(function (entry) {
           entry.getSourceMeta().done(callback)
         });
       });
@@ -291,7 +294,7 @@
     self.getColumns = function (database, table, callback) {
       var self = this;
       $.when(self.namespaceDeferred, self.computeDeferred).done(function (namespace, compute) {
-        dataCatalog.getEntry({ sourceType: self.options.apiHelperType, namespace: namespace, compute: compute, path: [ database, table ] }).done(function (entry) {
+        dataCatalog.getEntry({ sourceType: self.options.apiHelperType, connector: connector, namespace: namespace, compute: compute, path: [ database, table ] }).done(function (entry) {
           entry.getSourceMeta().done(callback)
         });
       });

+ 5 - 2
desktop/libs/indexer/src/indexer/templates/importer.mako

@@ -1669,6 +1669,7 @@ ${ commonheader(_("Importer"), "indexer", user, request, "60px") | n,unicode }
             sourceType: self.sourceType,
             namespace: self.namespace(),
             compute: self.compute(),
+            connector: { type: self.sourceType }, // TODO: Migrate importer to connectors
             name: tableName,
             columns: temporaryColumns,
             sample: self.sample()
@@ -2142,6 +2143,7 @@ ${ commonheader(_("Importer"), "indexer", user, request, "60px") | n,unicode }
             dataCatalog.getEntry({
               sourceType: self.sourceType,
               compute: wizard.compute(),
+              connector: {}, // TODO: Use connectors in the importer
               namespace: wizard.namespace(),
               path: self.outputFormat() === 'table' ? [self.databaseName(), self.tableName()] : [],
             }).done(function (catalogEntry) {
@@ -2854,15 +2856,16 @@ ${ commonheader(_("Importer"), "indexer", user, request, "60px") | n,unicode }
                   if (!snippet.result.handle().has_more_statements) {
                     if (self.editorVM.selectedNotebook().onSuccessUrl()) {
                       var match = snippet.statement_raw().match(/CREATE TABLE `([^`]+)`/i);
+                      const connector = {} // TODO: Use connector in importer
                       if (match) {
                         var db = match[1];
-                        dataCatalog.getEntry({ sourceType: snippet.type(), namespace: self.namespace(), compute: self.compute(), path: [ db ]}).done(function (dbEntry) {
+                        dataCatalog.getEntry({ sourceType: snippet.type(), connector: connector, namespace: self.namespace(), compute: self.compute(), path: [ db ]}).done(function (dbEntry) {
                           dbEntry.clearCache({ invalidate: 'invalidate', silenceErrors: true }).done(function () {
                             huePubSub.publish('open.link', self.editorVM.selectedNotebook().onSuccessUrl());
                           })
                         });
                       } else {
-                        dataCatalog.getEntry({ sourceType: snippet.type(), namespace: self.namespace(), compute: self.compute(), path: []}).done(function (sourceEntry) {
+                        dataCatalog.getEntry({ sourceType: snippet.type(), connector: connector, namespace: self.namespace(), compute: self.compute(), path: []}).done(function (sourceEntry) {
                           sourceEntry.clearCache({ silenceErrors: true }).done(function () {
                             huePubSub.publish('open.link', self.editorVM.selectedNotebook().onSuccessUrl());
                           })