Browse Source

HUE-8768 [editor] Switch to showing the results from the executor in notebook 2

Johan Ahlen 6 năm trước cách đây
mục cha
commit
4d0fcf6375

+ 71 - 30
desktop/core/src/desktop/js/api/apiHelper.js

@@ -407,6 +407,7 @@ class ApiHelper {
    * @param {function} [options.successCallback]
    * @param {function} [options.successCallback]
    * @param {function} [options.errorCallback]
    * @param {function} [options.errorCallback]
    * @param {boolean} [options.silenceErrors]
    * @param {boolean} [options.silenceErrors]
+   * @param {string} [options.dataType] - Default: Intelligent Guess (xml, json, script, text, html)
    *
    *
    * @return {Promise}
    * @return {Promise}
    */
    */
@@ -414,16 +415,22 @@ class ApiHelper {
     const self = this;
     const self = this;
     const deferred = $.Deferred();
     const deferred = $.Deferred();
 
 
-    const request = $.post(url, data, data => {
-      if (self.successResponseIsError(data)) {
-        deferred.reject(self.assistErrorCallback(options)(data));
-        return;
-      }
-      if (options && options.successCallback) {
-        options.successCallback(data);
-      }
-      deferred.resolve(data);
-    }).fail(self.assistErrorCallback(options));
+    const request = $.post({
+      url: url,
+      data: data,
+      dataType: options.dataType
+    })
+      .done(data => {
+        if (self.successResponseIsError(data)) {
+          deferred.reject(self.assistErrorCallback(options)(data));
+          return;
+        }
+        if (options && options.successCallback) {
+          options.successCallback(data);
+        }
+        deferred.resolve(data);
+      })
+      .fail(self.assistErrorCallback(options));
 
 
     request.fail(data => {
     request.fail(data => {
       deferred.reject(self.assistErrorCallback(options)(data));
       deferred.reject(self.assistErrorCallback(options)(data));
@@ -1816,12 +1823,12 @@ class ApiHelper {
       variables: [],
       variables: [],
       compute: executable.compute,
       compute: executable.compute,
       database: executable.database,
       database: executable.database,
-      properties: { settings: [] },
+      properties: { settings: [] }
     };
     };
 
 
     const notebook = {
     const notebook = {
       type: executable.sourceType,
       type: executable.sourceType,
-      snippets: [ snippet ],
+      snippets: [snippet],
       id: executable.notebookId,
       id: executable.notebookId,
       name: '',
       name: '',
       isSaved: false,
       isSaved: false,
@@ -1864,13 +1871,15 @@ class ApiHelper {
     const url = EXECUTE_API_PREFIX + executable.sourceType;
     const url = EXECUTE_API_PREFIX + executable.sourceType;
     const deferred = $.Deferred();
     const deferred = $.Deferred();
 
 
-    this.simplePost(url, ApiHelper.adaptExecutableToNotebook(executable), options).done(response => {
-      if (response.handle) {
-        deferred.resolve(response.handle);
-      } else {
-        deferred.reject('No handle in execute response');
-      }
-    }).fail(deferred.reject);
+    this.simplePost(url, ApiHelper.adaptExecutableToNotebook(executable), options)
+      .done(response => {
+        if (response.handle) {
+          deferred.resolve(response.handle);
+        } else {
+          deferred.reject('No handle in execute response');
+        }
+      })
+      .fail(deferred.reject);
 
 
     const promise = deferred.promise();
     const promise = deferred.promise();
 
 
@@ -1901,19 +1910,19 @@ class ApiHelper {
   checkExecutionStatus(options) {
   checkExecutionStatus(options) {
     const deferred = $.Deferred();
     const deferred = $.Deferred();
 
 
-    let request = this.simplePost(
+    const request = this.simplePost(
       '/notebook/api/check_status',
       '/notebook/api/check_status',
       ApiHelper.adaptExecutableToNotebook(options.executable),
       ApiHelper.adaptExecutableToNotebook(options.executable),
       options
       options
-    ).done(response => {
-      deferred.resolve(response.query_status)
-    }).fail(deferred.reject);
+    )
+      .done(response => {
+        deferred.resolve(response.query_status.status);
+      })
+      .fail(deferred.reject);
 
 
     return new CancellablePromise(deferred, request);
     return new CancellablePromise(deferred, request);
-
   }
   }
 
 
-
   /**
   /**
    *
    *
    * @param {Object} options
    * @param {Object} options
@@ -1958,15 +1967,47 @@ class ApiHelper {
    */
    */
   async fetchResults(options) {
   async fetchResults(options) {
     return new Promise((resolve, reject) => {
     return new Promise((resolve, reject) => {
-
       const data = ApiHelper.adaptExecutableToNotebook(options.executable);
       const data = ApiHelper.adaptExecutableToNotebook(options.executable);
       data.rows = options.rows;
       data.rows = options.rows;
       data.startOver = !!options.startOver;
       data.startOver = !!options.startOver;
 
 
-      this.simplePost('/notebook/api/fetch_result_data', data, options).done((response) => {
-        resolve(response.result);
-      }).fail(reject);
-    })
+      this.simplePost(
+        '/notebook/api/fetch_result_data',
+        data,
+        {
+          silenceErrors: options.silenceErrors,
+          dataType: 'text'
+        },
+        options
+      )
+        .done(response => {
+          const data = JSON.bigdataParse(response);
+          resolve(data.result);
+        })
+        .fail(reject);
+    });
+  }
+
+  /**
+   *
+   * @param {Object} options
+   * @param {boolean} [options.silenceErrors]
+   * @param {ExecutableStatement} options.executable
+   *
+   * @return {Promise<ResultResponse>}
+   */
+  async fetchResultSize(options) {
+    return new Promise((resolve, reject) => {
+      this.simplePost(
+        '/notebook/api/fetch_result_size',
+        ApiHelper.adaptExecutableToNotebook(options.executable),
+        options
+      )
+        .done(response => {
+          resolve(response.result);
+        })
+        .fail(reject);
+    });
   }
   }
 
 
   /**
   /**

+ 54 - 44
desktop/core/src/desktop/js/apps/notebook2/execution/executableStatement.js

@@ -15,7 +15,7 @@
 // limitations under the License.
 // limitations under the License.
 
 
 import apiHelper from 'api/apiHelper';
 import apiHelper from 'api/apiHelper';
-import { ExecutionResult } from "apps/notebook2/execution/executionResult";
+import { ExecutionResult } from 'apps/notebook2/execution/executionResult';
 import hueAnalytics from 'utils/hueAnalytics';
 import hueAnalytics from 'utils/hueAnalytics';
 
 
 /**
 /**
@@ -38,7 +38,6 @@ const EXECUTION_STATUS = {
 };
 };
 
 
 class ExecutableStatement {
 class ExecutableStatement {
-
   /**
   /**
    * @param options
    * @param options
    * @param {string} options.sourceType
    * @param {string} options.sourceType
@@ -79,38 +78,47 @@ class ExecutableStatement {
       let statusCheckCount = 0;
       let statusCheckCount = 0;
       let checkStatusTimeout = -1;
       let checkStatusTimeout = -1;
 
 
-      const checkStatus = () => new Promise( (resolve, reject) => {
-        statusCheckCount++;
-        this.lastCancellable = apiHelper.checkExecutionStatus({ executable: this }).done(queryStatus => {
-          switch (queryStatus) {
-            case 'success':
-              this.progress = 99; // TODO: why 99 here (from old code)?
-              resolve();
-              break;
-            case 'available':
-              this.progress = 100;
-              resolve();
-              break;
-            case 'expired':
-              reject();
-              break;
-            case 'running':
-            case 'starting':
-            case 'waiting':
-              checkStatusTimeout = window.setTimeout(() => {
-                checkStatus().then(resolve).catch(reject);
-              }, statusCheckCount > 45 ? 5000 : 1000);
-              break;
-            default:
-              console.warn('Got unknown status ' + queryStatus);
-              reject();
-          }
-        }).fail(reject);
+      const checkStatus = () =>
+        new Promise((statusResolve, statusReject) => {
+          statusCheckCount++;
+          this.lastCancellable = apiHelper
+            .checkExecutionStatus({ executable: this })
+            .done(queryStatus => {
+              switch (queryStatus) {
+                case 'success':
+                  this.progress = 99; // TODO: why 99 here (from old code)?
+                  statusResolve();
+                  break;
+                case 'available':
+                  this.progress = 100;
+                  statusResolve();
+                  break;
+                case 'expired':
+                  statusReject();
+                  break;
+                case 'running':
+                case 'starting':
+                case 'waiting':
+                  checkStatusTimeout = window.setTimeout(
+                    () => {
+                      checkStatus()
+                        .then(statusResolve)
+                        .catch(statusReject);
+                    },
+                    statusCheckCount > 45 ? 5000 : 1000
+                  );
+                  break;
+                default:
+                  console.warn('Got unknown status ' + queryStatus);
+                  statusReject();
+              }
+            })
+            .fail(statusReject);
 
 
-        this.lastCancellable.onCancel(() => {
-          window.clearTimeout(checkStatusTimeout);
-        })
-      });
+          this.lastCancellable.onCancel(() => {
+            window.clearTimeout(checkStatusTimeout);
+          });
+        });
 
 
       hueAnalytics.log('notebook', 'execute/' + this.sourceType);
       hueAnalytics.log('notebook', 'execute/' + this.sourceType);
       this.status = EXECUTION_STATUS.executing;
       this.status = EXECUTION_STATUS.executing;
@@ -122,20 +130,22 @@ class ExecutableStatement {
         .done(handle => {
         .done(handle => {
           this.handle = handle;
           this.handle = handle;
 
 
-          checkStatus().then(() => {
-            this.result = new ExecutionResult(this);
-            this.status = EXECUTION_STATUS.done;
-            resolve(this.result);
-          }).catch(error => {
-            this.status = EXECUTION_STATUS.fail;
-            reject(error);
-          });
+          checkStatus()
+            .then(() => {
+              this.result = new ExecutionResult(this);
+              this.status = EXECUTION_STATUS.done;
+              resolve(this.result);
+            })
+            .catch(error => {
+              this.status = EXECUTION_STATUS.fail;
+              reject(error);
+            });
         })
         })
         .fail(error => {
         .fail(error => {
           this.status = EXECUTION_STATUS.fail;
           this.status = EXECUTION_STATUS.fail;
           reject(error);
           reject(error);
         });
         });
-    })
+    });
   }
   }
 
 
   async cancel() {
   async cancel() {
@@ -153,11 +163,11 @@ class ExecutableStatement {
       }
       }
     });
     });
   }
   }
-  
+
   async close() {
   async close() {
     return new Promise(resolve => {
     return new Promise(resolve => {
       if (this.status === EXECUTION_STATUS.executing) {
       if (this.status === EXECUTION_STATUS.executing) {
-        this.cancel().finally(resolve)
+        this.cancel().finally(resolve);
       } else if (this.status === EXECUTION_STATUS.done) {
       } else if (this.status === EXECUTION_STATUS.done) {
         apiHelper.closeStatement({ executable: this }).finally(resolve);
         apiHelper.closeStatement({ executable: this }).finally(resolve);
       }
       }

+ 39 - 20
desktop/core/src/desktop/js/apps/notebook2/execution/executionResult.js

@@ -15,7 +15,6 @@
 // limitations under the License.
 // limitations under the License.
 
 
 import apiHelper from 'api/apiHelper';
 import apiHelper from 'api/apiHelper';
-import hueAnalytics from 'utils/hueAnalytics';
 
 
 /**
 /**
  *  available +----> fetching +----> done
  *  available +----> fetching +----> done
@@ -38,7 +37,6 @@ const RESULT_STATUS = {
 };
 };
 
 
 class ExecutionResult {
 class ExecutionResult {
-
   /**
   /**
    *
    *
    * @param {ExecutableStatement} executable
    * @param {ExecutableStatement} executable
@@ -48,6 +46,24 @@ class ExecutionResult {
     this.status = executable.handle.has_result_set ? RESULT_STATUS.available : RESULT_STATUS.done;
     this.status = executable.handle.has_result_set ? RESULT_STATUS.available : RESULT_STATUS.done;
   }
   }
 
 
+  async fetchResultSize(options) {
+    return new Promise((resolve, reject) => {
+      if (this.status === RESULT_STATUS.fail) {
+        reject();
+        return;
+      }
+      apiHelper
+        .fetchResultSize({
+          executable: this.executable
+        })
+        .then(resultSizeResponse => {
+          console.log(resultSizeResponse);
+          resolve(resultSizeResponse);
+        })
+        .catch(reject);
+    });
+  }
+
   /**
   /**
    * Fetches additional rows
    * Fetches additional rows
    *
    *
@@ -57,30 +73,33 @@ class ExecutionResult {
    *
    *
    * @return {Promise}
    * @return {Promise}
    */
    */
-  async fetch(options) {
+  async fetchRows(options) {
     return new Promise((resolve, reject) => {
     return new Promise((resolve, reject) => {
       if (this.status !== RESULT_STATUS.available) {
       if (this.status !== RESULT_STATUS.available) {
         reject();
         reject();
         return;
         return;
       }
       }
       this.status = RESULT_STATUS.fetching;
       this.status = RESULT_STATUS.fetching;
-      apiHelper.fetchResults({
-        executable: this.executable,
-        rows: options.rows,
-        startOver: !!options.startOver
-      }).then((resultResponse) => {
-        if (resultResponse.has_more) {
-          this.status = RESULT_STATUS.available;
-        } else {
-          this.status = RESULT_STATUS.done;
-        }
-        console.log(resultResponse);
-        resolve(resultResponse);
-      }).catch((error) => {
-        this.status = RESULT_STATUS.fail;
-        reject(error);
-      })
-    })
+      apiHelper
+        .fetchResults({
+          executable: this.executable,
+          rows: options.rows,
+          startOver: !!options.startOver
+        })
+        .then(resultResponse => {
+          if (resultResponse.has_more) {
+            this.status = RESULT_STATUS.available;
+          } else {
+            this.status = RESULT_STATUS.done;
+          }
+          console.log(resultResponse);
+          resolve(resultResponse);
+        })
+        .catch(error => {
+          this.status = RESULT_STATUS.fail;
+          reject(error);
+        });
+    });
   }
   }
 }
 }
 
 

+ 19 - 15
desktop/core/src/desktop/js/apps/notebook2/execution/executor.js

@@ -16,7 +16,7 @@
 
 
 import { EXECUTION_STATUS, ExecutableStatement } from './executableStatement';
 import { EXECUTION_STATUS, ExecutableStatement } from './executableStatement';
 import sqlStatementsParser from 'parse/sqlStatementsParser';
 import sqlStatementsParser from 'parse/sqlStatementsParser';
-import huePubSub from "utils/huePubSub";
+import huePubSub from 'utils/huePubSub';
 
 
 const EXECUTION_FLOW = {
 const EXECUTION_FLOW = {
   step: 'step',
   step: 'step',
@@ -108,24 +108,28 @@ class Executor {
     return new Promise((resolve, reject) => {
     return new Promise((resolve, reject) => {
       const executeBatch = () => {
       const executeBatch = () => {
         if (this.toExecute.length === 0) {
         if (this.toExecute.length === 0) {
-          this.setStatus(EXECUTION_STATUS.success);
-          resolve(this.status);
+          reject();
         } else {
         } else {
           this.currentExecutable = this.toExecute.shift();
           this.currentExecutable = this.toExecute.shift();
           this.setStatus(EXECUTION_STATUS.running);
           this.setStatus(EXECUTION_STATUS.running);
-          this.currentExecutable.execute().then(() => {
-            this.executed.push(this.currentExecutable);
-            this.currentExecutable = undefined;
+          this.currentExecutable
+            .execute()
+            .then(executionResult => {
+              this.executed.push(this.currentExecutable);
+              this.currentExecutable = undefined;
 
 
-            if (this.canExecuteNextInBatch()) {
-              this.executeNext()
-                .then(executeBatch)
-                .catch(reject);
-            } else  {
-              this.setStatus(this.toExecute.length ? EXECUTION_STATUS.ready : EXECUTION_STATUS.success);
-              resolve(this.status);
-            }
-          }).catch(reject);
+              if (this.canExecuteNextInBatch()) {
+                this.executeNext()
+                  .then(executeBatch)
+                  .catch(reject);
+              } else {
+                this.setStatus(
+                  this.toExecute.length ? EXECUTION_STATUS.ready : EXECUTION_STATUS.success
+                );
+                resolve(executionResult);
+              }
+            })
+            .catch(reject);
         }
         }
       };
       };
 
 

+ 10 - 9
desktop/core/src/desktop/js/apps/notebook2/execution/sessionManager.js

@@ -14,15 +14,16 @@
 // See the License for the specific language governing permissions and
 // See the License for the specific language governing permissions and
 // limitations under the License.
 // limitations under the License.
 
 
-import { EXECUTION_STATUS, ExecutableStatement } from './executableStatement';
-import sqlStatementsParser from 'parse/sqlStatementsParser';
-import huePubSub from "utils/huePubSub";
+const knownSessions = {};
 
 
-const knownSessions {
-  impala: {...},
-  presto: {...}
-}
+class SessionManager {
+  constructor() {
+    console.warn('Not implemented yet.');
+  }
 
 
-getSession(interpreter) {
+  getSessions(interpreter) {
+    return knownSessions[interpreter];
+  }
+}
 
 
-}
+export default SessionManager;

+ 2 - 0
desktop/core/src/desktop/js/apps/notebook2/execution/spec/executableStatementSpec.js

@@ -14,6 +14,8 @@
 // See the License for the specific language governing permissions and
 // See the License for the specific language governing permissions and
 // limitations under the License.
 // limitations under the License.
 
 
+import $ from 'jquery';
+
 import ApiHelper from 'api/apiHelper';
 import ApiHelper from 'api/apiHelper';
 import { ExecutableStatement, STATUS } from '../executableStatement';
 import { ExecutableStatement, STATUS } from '../executableStatement';
 
 

+ 2 - 0
desktop/core/src/desktop/js/apps/notebook2/execution/spec/executorSpec.js

@@ -14,6 +14,8 @@
 // See the License for the specific language governing permissions and
 // See the License for the specific language governing permissions and
 // limitations under the License.
 // limitations under the License.
 
 
+import $ from 'jquery';
+
 import ApiHelper from 'api/apiHelper';
 import ApiHelper from 'api/apiHelper';
 import { STATUS } from '../executableStatement';
 import { STATUS } from '../executableStatement';
 import Executor from '../executor';
 import Executor from '../executor';

+ 100 - 2
desktop/core/src/desktop/js/apps/notebook2/result.js

@@ -14,10 +14,10 @@
 // See the License for the specific language governing permissions and
 // See the License for the specific language governing permissions and
 // limitations under the License.
 // limitations under the License.
 
 
-import $ from 'jquery';
 import ko from 'knockout';
 import ko from 'knockout';
 
 
 import hueUtils from 'utils/hueUtils';
 import hueUtils from 'utils/hueUtils';
+import huePubSub from '../../utils/huePubSub';
 
 
 const adaptMeta = meta => {
 const adaptMeta = meta => {
   meta.forEach((item, index) => {
   meta.forEach((item, index) => {
@@ -50,7 +50,7 @@ const isStringColumn = type =>
   !isNumericColumn(type) && !isDateTimeColumn(type) && !isComplexColumn(type);
   !isNumericColumn(type) && !isDateTimeColumn(type) && !isComplexColumn(type);
 
 
 class Result {
 class Result {
-  constructor(result) {
+  constructor(result, snippet) {
     const self = this;
     const self = this;
 
 
     self.id = ko.observable(result.id || hueUtils.UUID());
     self.id = ko.observable(result.id || hueUtils.UUID());
@@ -58,6 +58,7 @@ class Result {
     self.hasResultset = ko.observable(result.hasResultset !== false).extend('throttle', 100);
     self.hasResultset = ko.observable(result.hasResultset !== false).extend('throttle', 100);
     self.handle = ko.observable(result.handle || {});
     self.handle = ko.observable(result.handle || {});
     self.meta = ko.observableArray(result.meta || []);
     self.meta = ko.observableArray(result.meta || []);
+    self.snippet = snippet;
 
 
     adaptMeta(self.meta());
     adaptMeta(self.meta());
     self.meta.subscribe(() => {
     self.meta.subscribe(() => {
@@ -232,6 +233,103 @@ class Result {
       handle: self.handle
       handle: self.handle
     };
     };
   }
   }
+
+  applyResultResponse(resultResponse) {}
+
+  /**
+   *
+   * @param {ExecutionResult} executionResult
+   * @return {Promise<*>}
+   */
+  async update(executionResult) {
+    this.executionResult = executionResult;
+
+    window.setTimeout(() => {
+      this.executionResult.fetchResultSize().then(rows => {
+        console.log(rows);
+      });
+    }, 2000);
+
+    await this.fetchMoreRows(100, false);
+
+    // TODO: load additional 100 in background
+    /*
+
+     if (result.has_more && rows > 0) {
+      setTimeout(() => {
+        self.fetchResultData(rows, false);
+      }, 500);
+    } else if (
+
+     */
+  }
+
+  async fetchMoreRows(rowCount, startOver) {
+    return new Promise((resolve, reject) => {
+      if (!this.executionResult) {
+        reject();
+      }
+      this.executionResult
+        .fetchRows({
+          rows: rowCount,
+          startOver: !!startOver
+        })
+        .then(resultResponse => {
+          const initialIndex = this.data().length;
+          const tempData = [];
+
+          resultResponse.data.forEach((row, index) => {
+            row.unshift(initialIndex + index + 1);
+            this.data.push(row);
+            tempData.push(row);
+          });
+
+          if (this.rows() == null || (this.rows() + '').indexOf('+') !== -1) {
+            this.rows(this.data().length + (resultResponse.has_more ? '+' : ''));
+          }
+
+          this.images(resultResponse.images || []);
+
+          huePubSub.publish('editor.render.data', {
+            data: tempData,
+            snippet: this.snippet,
+            initial: initialIndex === 0
+          });
+
+          if (!this.fetchedOnce()) {
+            resultResponse.meta.unshift({ type: 'INT_TYPE', name: '', comment: null });
+            this.meta(resultResponse.meta);
+            this.type(resultResponse.type);
+            this.fetchedOnce(true);
+          }
+
+          this.meta().forEach(meta => {
+            switch (meta.type) {
+              case 'TINYINT_TYPE':
+              case 'SMALLINT_TYPE':
+              case 'INT_TYPE':
+              case 'BIGINT_TYPE':
+              case 'FLOAT_TYPE':
+              case 'DOUBLE_TYPE':
+              case 'DECIMAL_TYPE':
+                meta.cssClass = 'sort-numeric';
+                break;
+              case 'TIMESTAMP_TYPE':
+              case 'DATE_TYPE':
+              case 'DATETIME_TYPE':
+                meta.cssClass = 'sort-date';
+                break;
+              default:
+                meta.cssClass = 'sort-string';
+            }
+          });
+
+          this.hasMore(resultResponse.has_more);
+          resolve();
+        })
+        .catch(reject);
+    });
+  }
 }
 }
 
 
 export default Result;
 export default Result;

+ 83 - 125
desktop/core/src/desktop/js/apps/notebook2/snippet.js

@@ -732,7 +732,7 @@ class Snippet {
       }
       }
     );
     );
 
 
-    self.result = new Result(snippet.result);
+    self.result = new Result(snippet.result, self);
     if (!self.result.hasSomeResults()) {
     if (!self.result.hasSomeResults()) {
       self.currentQueryTab('queryHistory');
       self.currentQueryTab('queryHistory');
     }
     }
@@ -842,7 +842,6 @@ class Snippet {
       $(document).trigger('forceChartDraw', self);
       $(document).trigger('forceChartDraw', self);
     });
     });
 
 
-
     self.previousChartOptions = {};
     self.previousChartOptions = {};
 
 
     self.result.meta.subscribe(() => {
     self.result.meta.subscribe(() => {
@@ -1112,7 +1111,7 @@ class Snippet {
 
 
     huePubSub.subscribe('hue.executor.progress.updated', executor => {
     huePubSub.subscribe('hue.executor.progress.updated', executor => {
       updateExecutorObservable(executor, 'progress');
       updateExecutorObservable(executor, 'progress');
-    })
+    });
   }
   }
 
 
   ace(newVal) {
   ace(newVal) {
@@ -1347,6 +1346,10 @@ class Snippet {
 
 
     this.currentQueryTab('queryHistory');
     this.currentQueryTab('queryHistory');
 
 
+    if (this.executor && this.executor.isRunning()) {
+      this.executor.cancel();
+    }
+
     this.executor = new Executor({
     this.executor = new Executor({
       compute: this.compute(),
       compute: this.compute(),
       database: this.database(),
       database: this.database(),
@@ -1354,7 +1357,16 @@ class Snippet {
       namespace: this.namespace(),
       namespace: this.namespace(),
       statement: this.statement(),
       statement: this.statement(),
       isSqlEngine: this.isSqlDialect()
       isSqlEngine: this.isSqlDialect()
-    }).executeNext();
+    });
+
+    this.executor.executeNext().then(executionResult => {
+      this.stopLongOperationTimeout();
+      this.result.update(executionResult).then(() => {
+        if (this.result.data().length) {
+          this.currentQueryTab('queryResults');
+        }
+      });
+    });
   }
   }
 
 
   explain() {
   explain() {
@@ -1436,59 +1448,56 @@ class Snippet {
     });
     });
   }
   }
 
 
+  // TODO: Switch to result.fetchMoreRows in ko mako
   fetchResult(rows, startOver) {
   fetchResult(rows, startOver) {
-    const self = this;
-    if (typeof startOver === 'undefined') {
-      startOver = true;
-    }
-    self.fetchResultData(rows, startOver);
-    //self.fetchResultMetadata(rows);
+    this.result.fetchMoreRows(rows, startOver);
   }
   }
 
 
-  fetchResultData(rows, startOver) {
-    const self = this;
-    if (!self.isFetchingData) {
-      if (self.status() === STATUS.available) {
-        self.startLongOperationTimeout();
-        self.isFetchingData = true;
-        hueAnalytics.log('notebook', 'fetchResult/' + rows + '/' + startOver);
-        $.post(
-          '/notebook/api/fetch_result_data',
-          {
-            notebook: komapping.toJSON(self.parentNotebook.getContext(), NOTEBOOK_MAPPING),
-            snippet: komapping.toJSON(self.getContext()),
-            rows: rows,
-            startOver: startOver
-          },
-          data => {
-            self.stopLongOperationTimeout();
-            data = JSON.bigdataParse(data);
-            if (data.status === 0) {
-              self.showExecutionAnalysis(true);
-              self.loadData(data.result, rows);
-            } else {
-              self.handleAjaxError(data, () => {
-                self.isFetchingData = false;
-                self.fetchResultData(rows, startOver);
-              });
-              $(document).trigger('renderDataError', { snippet: self });
-            }
-          },
-          'text'
-        )
-          .fail(xhr => {
-            if (xhr.status !== 502) {
-              $(document).trigger('error', xhr.responseText);
-            }
-          })
-          .always(() => {
-            self.isFetchingData = false;
-          });
-      } else {
-        huePubSub.publish('editor.snippet.result.normal', self);
-      }
-    }
-  }
+  // fetchResultData(rows, startOver) {
+  //   console.log('fetchResultData');
+  //   const self = this;
+  //   if (!self.isFetchingData) {
+  //     if (self.status() === STATUS.available) {
+  //       self.startLongOperationTimeout();
+  //       self.isFetchingData = true;
+  //       hueAnalytics.log('notebook', 'fetchResult/' + rows + '/' + startOver);
+  //       $.post(
+  //         '/notebook/api/fetch_result_data',
+  //         {
+  //           notebook: komapping.toJSON(self.parentNotebook.getContext(), NOTEBOOK_MAPPING),
+  //           snippet: komapping.toJSON(self.getContext()),
+  //           rows: rows,
+  //           startOver: startOver
+  //         },
+  //         data => {
+  //           self.stopLongOperationTimeout();
+  //           data = JSON.bigdataParse(data);
+  //           if (data.status === 0) {
+  //             self.showExecutionAnalysis(true);
+  //             self.loadData(data.result, rows);
+  //           } else {
+  //             self.handleAjaxError(data, () => {
+  //               self.isFetchingData = false;
+  //               self.fetchResultData(rows, startOver);
+  //             });
+  //             $(document).trigger('renderDataError', { snippet: self });
+  //           }
+  //         },
+  //         'text'
+  //       )
+  //         .fail(xhr => {
+  //           if (xhr.status !== 502) {
+  //             $(document).trigger('error', xhr.responseText);
+  //           }
+  //         })
+  //         .always(() => {
+  //           self.isFetchingData = false;
+  //         });
+  //     } else {
+  //       huePubSub.publish('editor.snippet.result.normal', self);
+  //     }
+  //   }
+  // }
 
 
   fetchResultMetadata() {
   fetchResultMetadata() {
     const self = this;
     const self = this;
@@ -1927,77 +1936,26 @@ class Snippet {
     }
     }
   }
   }
 
 
-  loadData(result, rows) {
-    const self = this;
-    rows -= result.data.length;
-
-    if (result.data.length > 0) {
-      self.currentQueryTab('queryResults');
-    }
-
-    const _initialIndex = self.result.data().length;
-    const _tempData = [];
-    $.each(result.data, (index, row) => {
-      row.unshift(_initialIndex + index + 1);
-      self.result.data.push(row);
-      _tempData.push(row);
-    });
-
-    if (self.result.rows() == null || (self.result.rows() + '').indexOf('+') !== -1) {
-      self.result.rows(self.result.data().length + (result.has_more ? '+' : ''));
-    }
-
-    self.result.images(
-      typeof result.images != 'undefined' && result.images != null ? result.images : []
-    );
-
-    huePubSub.publish('editor.render.data', {
-      data: _tempData,
-      snippet: self,
-      initial: _initialIndex === 0
-    });
-
-    if (!self.result.fetchedOnce()) {
-      result.meta.unshift({ type: 'INT_TYPE', name: '', comment: null });
-      self.result.meta(result.meta);
-      self.result.type(result.type);
-      self.result.fetchedOnce(true);
-    }
-
-    self.result.meta().forEach(meta => {
-      if (
-        [
-          'TINYINT_TYPE',
-          'SMALLINT_TYPE',
-          'INT_TYPE',
-          'BIGINT_TYPE',
-          'FLOAT_TYPE',
-          'DOUBLE_TYPE',
-          'DECIMAL_TYPE'
-        ].indexOf(meta.type) !== -1
-      ) {
-        meta.cssClass = 'sort-numeric';
-      } else if (['TIMESTAMP_TYPE', 'DATE_TYPE', 'DATETIME_TYPE'].indexOf(meta.type) !== -1) {
-        meta.cssClass = 'sort-date';
-      } else {
-        meta.cssClass = 'sort-string';
-      }
-    });
-
-    self.result.hasMore(result.has_more);
-
-    if (result.has_more && rows > 0) {
-      setTimeout(() => {
-        self.fetchResultData(rows, false);
-      }, 500);
-    } else if (
-      !self.parentVm.editorMode() &&
-      !self.parentNotebook.isPresentationMode() &&
-      self.parentNotebook.snippets()[self.parentNotebook.snippets().length - 1] === self
-    ) {
-      self.parentNotebook.newSnippet();
-    }
-  }
+  // loadData(result, rows) {
+  //   const self = this;
+  //   rows -= result.data.length;
+  //
+  //   if (result.data.length > 0) {
+  //     self.currentQueryTab('queryResults');
+  //   }
+  //
+  //   if (result.has_more && rows > 0) {
+  //     setTimeout(() => {
+  //       self.fetchResultData(rows, false);
+  //     }, 500);
+  //   } else if (
+  //     !self.parentVm.editorMode() &&
+  //     !self.parentNotebook.isPresentationMode() &&
+  //     self.parentNotebook.snippets()[self.parentNotebook.snippets().length - 1] === self
+  //   ) {
+  //     self.parentNotebook.newSnippet();
+  //   }
+  // }
 
 
   nextQueriesPage() {
   nextQueriesPage() {
     const self = this;
     const self = this;

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

@@ -138,7 +138,6 @@ ko.bindingHandlers.autocomplete = {
           ul.css('min-width', options.minWidth || $element.outerWidth(true));
           ul.css('min-width', options.minWidth || $element.outerWidth(true));
           ul.css('min-height', options.minHeight || '20px');
           ul.css('min-height', options.minHeight || '20px');
 
 
-
           ul.addClass(this.options.classPrefix + 'autocomplete');
           ul.addClass(this.options.classPrefix + 'autocomplete');
           $.each(items, (index, item) => {
           $.each(items, (index, item) => {
             self._renderItemData(ul, item);
             self._renderItemData(ul, item);