You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by nc...@apache.org on 2017/01/27 18:17:48 UTC

[37/49] ambari git commit: AMBARI-19628. Hive View 2.0: Ability to view and create table and column statistics. (dipayanb)

AMBARI-19628. Hive View 2.0: Ability to view and create table and column statistics. (dipayanb)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/22d4e181
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/22d4e181
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/22d4e181

Branch: refs/heads/branch-dev-patch-upgrade
Commit: 22d4e18168935d9a9400591ebcd1a05c2b2ebf7d
Parents: f88aca8
Author: Dipayan Bhowmick <di...@gmail.com>
Authored: Fri Jan 27 12:03:24 2017 +0530
Committer: Dipayan Bhowmick <di...@gmail.com>
Committed: Fri Jan 27 12:03:56 2017 +0530

----------------------------------------------------------------------
 .../src/main/resources/ui/app/adapters/table.js |  27 +++-
 .../ui/app/components/table-statistics.js       | 120 +++++++++++++++
 .../main/resources/ui/app/models/table-info.js  |   1 +
 .../routes/databases/database/tables/table.js   |   4 +
 .../src/main/resources/ui/app/services/jobs.js  |  10 +-
 .../resources/ui/app/services/stats-service.js  |  76 +++++++++
 .../src/main/resources/ui/app/styles/app.scss   |  23 +--
 .../templates/components/table-statistics.hbs   | 153 +++++++++++++++++++
 .../app/templates/databases/database/tables.hbs |   2 +-
 .../databases/database/tables/table/stats.hbs   |   4 +-
 10 files changed, 399 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/22d4e181/contrib/views/hive20/src/main/resources/ui/app/adapters/table.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive20/src/main/resources/ui/app/adapters/table.js b/contrib/views/hive20/src/main/resources/ui/app/adapters/table.js
index 9a4692d..e878899 100644
--- a/contrib/views/hive20/src/main/resources/ui/app/adapters/table.js
+++ b/contrib/views/hive20/src/main/resources/ui/app/adapters/table.js
@@ -22,14 +22,14 @@ import DDLAdapter from './ddl';
 export default DDLAdapter.extend({
   buildURL(modelName, id, snapshot, requestType, query) {
     // Check if the query is to find all tables for a particular database
-    if(Ember.isEmpty(id) && (requestType === 'query' || requestType == 'queryRecord')) {
+    if (Ember.isEmpty(id) && (requestType === 'query' || requestType == 'queryRecord')) {
       let dbId = query.databaseId;
       let tableName = query.tableName;
       let origFindAllUrl = this._super(...arguments);
       let prefix = origFindAllUrl.substr(0, origFindAllUrl.lastIndexOf("/"));
       delete query.databaseId;
       delete query.tableName;
-      if(Ember.isEmpty(tableName)) {
+      if (Ember.isEmpty(tableName)) {
         return `${prefix}/databases/${dbId}/tables`;
       } else {
         return `${prefix}/databases/${dbId}/tables/${tableName}`;
@@ -40,12 +40,29 @@ export default DDLAdapter.extend({
 
 
   createTable(tableMetaInfo) {
-    let postURL = this.buildURL('table', null, null, 'query', {databaseId: tableMetaInfo.database});
-    return this.ajax(postURL, 'POST', { data: {tableInfo: tableMetaInfo} });
+    let postURL = this.buildURL('table', null, null, 'query', { databaseId: tableMetaInfo.database });
+    return this.ajax(postURL, 'POST', { data: { tableInfo: tableMetaInfo } });
   },
 
   deleteTable(database, tableName) {
-    let deletURL = this.buildURL('table', null, null, 'query', {databaseId: database, tableName: tableName});
+    let deletURL = this.buildURL('table', null, null, 'query', { databaseId: database, tableName: tableName });
     return this.ajax(deletURL, 'DELETE');
+  },
+
+  analyseTable(databaseName, tableName, withColumns = false) {
+    let analyseUrl = this.buildURL('table', null, null, 'query', { databaseId: databaseName, tableName: tableName }) +
+      '/analyze' +
+      (withColumns ? '?analyze_columns=true' : '');
+    return this.ajax(analyseUrl, 'PUT');
+  },
+
+  generateColumnStats(databaseName, tableName, columnName) {
+    let url = this.buildURL('table', null, null, 'query', {databaseId: databaseName, tableName: tableName}) + `/column/${columnName}/stats`;
+    return this.ajax(url, 'GET');
+  },
+
+  fetchColumnStats(databaseName, tableName, columnName, jobId) {
+    let url = this.buildURL('table', null, null, 'query', {databaseId: databaseName, tableName: tableName}) + `/column/${columnName}/fetch_stats?job_id=${jobId}`;
+    return this.ajax(url, 'GET');
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/22d4e181/contrib/views/hive20/src/main/resources/ui/app/components/table-statistics.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive20/src/main/resources/ui/app/components/table-statistics.js b/contrib/views/hive20/src/main/resources/ui/app/components/table-statistics.js
new file mode 100644
index 0000000..d53a41f
--- /dev/null
+++ b/contrib/views/hive20/src/main/resources/ui/app/components/table-statistics.js
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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 Ember from 'ember';
+
+export default Ember.Component.extend({
+  statsService: Ember.inject.service(),
+
+  analyseWithStatistics: false,
+
+  tableStats: Ember.computed.oneWay('table.tableStats'),
+  tableStatisticsEnabled: Ember.computed.oneWay('table.tableStats.isTableStatsEnabled'),
+
+  columnStatsAccurate: Ember.computed('table.tableStats.columnStatsAccurate', function () {
+    let columnStatsJson = this.get('table.tableStats.columnStatsAccurate');
+    return JSON.parse(columnStatsJson.replace(/\\\"/g, '"'));
+  }),
+
+  columnsWithStatistics: Ember.computed('columnStatsAccurate', function () {
+    let stats = this.get('columnStatsAccurate.COLUMN_STATS');
+    return !stats ? [] : Object.keys(stats);
+  }),
+
+  columns: Ember.computed('table.columns', 'columnsWithStatistics', function () {
+    let cols = this.get('table.columns');
+    let colsWithStatistics = this.get('columnsWithStatistics');
+    return cols.map((col) => {
+      let copy = Ember.Object.create(col);
+      copy.set('hasStatistics', colsWithStatistics.contains(copy.name));
+      copy.set('isFetchingStats', false);
+      copy.set('statsError', false);
+      copy.set('showStats', true);
+      return copy;
+    });
+  }),
+
+  allColumnsHasStatistics: Ember.computed('table.columns', 'columnsWithStatistics', function () {
+    let colsNames = this.get('table.columns').getEach('name');
+    let colsWithStatistics = this.get('columnsWithStatistics');
+
+    let colsNotIn = colsNames.filter((item) => !colsWithStatistics.contains(item));
+    return colsNotIn.length === 0;
+  }),
+
+  performTableAnalysis(withColumns = false) {
+    const tableName = this.get('table.table');
+    const databaseName = this.get('table.database');
+
+    let title = `Analyse table` + (withColumns ? ' for columns' : '');
+    this.set('analyseTitle', title);
+    this.set('analyseMessage', `Submitting job to generate statistics for table '${tableName}'`);
+
+    this.set('showAnalyseModal', true);
+
+    this.get('statsService').generateStatistics(databaseName, tableName, withColumns)
+      .then((job) => {
+        this.set('analyseMessage', 'Waiting for the job to complete');
+        return this.get('statsService').waitForStatsGenerationToComplete(job);
+      }).then(() => {
+      this.set('analyseMessage', 'Finished analysing table for statistics');
+      Ember.run.later(() => this.closeAndRefresh(), 2 * 1000);
+    }).catch((err) => {
+      this.set('analyseMessage', 'Job failed for analysing statistics of table');
+      Ember.run.later(() => this.closeAndRefresh(), 2 * 1000);
+    });
+  },
+
+  fetchColumnStats(column) {
+    const tableName = this.get('table.table');
+    const databaseName = this.get('table.database');
+
+    column.set('isFetchingStats', true);
+
+    this.get('statsService').generateColumnStatistics(databaseName, tableName, column.name).then((job) => {
+      return this.get('statsService').waitForStatsGenerationToComplete(job, false);
+    }).then((job) => {
+      return this.get('statsService').fetchColumnStatsResult(databaseName, tableName, column.name, job);
+    }).then((data) => {
+      column.set('isFetchingStats', false);
+      column.set('stats', data);
+    }).catch((err) => {
+      column.set('isFetchingStats', false);
+      column.set('statsError', true);
+    });
+  },
+
+  closeAndRefresh() {
+    this.set('showAnalyseModal', false);
+    this.sendAction('refresh');
+  },
+
+  actions: {
+    analyseTable() {
+      this.performTableAnalysis(this.get('analyseWithStatistics'));
+    },
+
+    fetchStats(column) {
+      this.fetchColumnStats(column);
+    },
+
+    toggleShowStats(column) {
+      column.toggleProperty('showStats');
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/22d4e181/contrib/views/hive20/src/main/resources/ui/app/models/table-info.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive20/src/main/resources/ui/app/models/table-info.js b/contrib/views/hive20/src/main/resources/ui/app/models/table-info.js
index 85306b6..ea51ab6 100644
--- a/contrib/views/hive20/src/main/resources/ui/app/models/table-info.js
+++ b/contrib/views/hive20/src/main/resources/ui/app/models/table-info.js
@@ -25,6 +25,7 @@ export default DS.Model.extend({
   ddl: DS.attr('string'),
   partitionInfo: DS.attr(),
   detailedInfo: DS.attr(),
+  tableStats: DS.attr(),
   storageInfo: DS.attr(),
   viewInfo: DS.attr()
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/22d4e181/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/table.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/table.js b/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/table.js
index 88f1e7e..1066bc1 100644
--- a/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/table.js
+++ b/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/table.js
@@ -48,6 +48,10 @@ export default Ember.Route.extend({
 
     editTable(table) {
       console.log("Edit table");
+    },
+
+    refreshTableInfo() {
+      this.refresh();
     }
   },
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/22d4e181/contrib/views/hive20/src/main/resources/ui/app/services/jobs.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive20/src/main/resources/ui/app/services/jobs.js b/contrib/views/hive20/src/main/resources/ui/app/services/jobs.js
index ca058f2..5d7ce77 100644
--- a/contrib/views/hive20/src/main/resources/ui/app/services/jobs.js
+++ b/contrib/views/hive20/src/main/resources/ui/app/services/jobs.js
@@ -26,18 +26,22 @@ export default Ember.Service.extend({
     })
   },
 
-  waitForJobToComplete(jobId, after) {
+  waitForJobToComplete(jobId, after, fetchDummyResult = true) {
+    console.log()
     return new Ember.RSVP.Promise((resolve, reject) => {
       Ember.run.later(() => {
-        this.get('store').findRecord('job', jobId, {reload: true})
+        this.get('store').findRecord('job', jobId, { reload: true })
           .then((job) => {
             let status = job.get('status').toLowerCase();
             if (status === 'succeeded') {
+              if (fetchDummyResult) {
+                this._fetchDummyResult(jobId);
+              }
               resolve(status);
             } else if (status === 'error') {
               reject(status)
             } else {
-              resolve(this.waitForJobToComplete(jobId, after));
+              resolve(this.waitForJobToComplete(jobId, after, fetchDummyResult));
             }
           }, (error) => {
             reject(error);

http://git-wip-us.apache.org/repos/asf/ambari/blob/22d4e181/contrib/views/hive20/src/main/resources/ui/app/services/stats-service.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive20/src/main/resources/ui/app/services/stats-service.js b/contrib/views/hive20/src/main/resources/ui/app/services/stats-service.js
new file mode 100644
index 0000000..bb3ed3e
--- /dev/null
+++ b/contrib/views/hive20/src/main/resources/ui/app/services/stats-service.js
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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 Ember from 'ember';
+
+const columnStatsKeys = [
+  {dataKey: 'min', label: 'MIN'},
+  {dataKey: 'max', label: 'MAX'},
+  {dataKey: 'numNulls', label: 'NUMBER OF NULLS'},
+  {dataKey: 'distinctCount', label: 'DISTINCT COUNT'},
+  {dataKey: 'avgColLen', label: 'AVERAGE COLUMN LENGTH'},
+  {dataKey: 'maxColLen', label: 'MAX COLUMN LENGTH'},
+  {dataKey: 'numTrues', label: 'NUMBER OF TRUE'},
+  {dataKey: 'numFalse', label: 'NUMBER OF FALSE'},
+  ];
+
+export default Ember.Service.extend({
+  jobs: Ember.inject.service(),
+  store: Ember.inject.service(),
+
+  generateStatistics(databaseName, tableName, withColumns = false) {
+    return new Promise((resolve, reject) => {
+      this.get('store').adapterFor('table').analyseTable(databaseName, tableName, withColumns).then((data) => {
+        this.get('store').pushPayload(data);
+        resolve(this.get('store').peekRecord('job', data.job.id));
+      }, (err) => {
+        reject(err);
+      });
+    });
+  },
+
+  generateColumnStatistics(databaseName, tableName, columnName) {
+    return new Promise((resolve, reject) => {
+      this.get('store').adapterFor('table').generateColumnStats(databaseName, tableName, columnName).then((data) => {
+        this.get('store').pushPayload(data);
+        resolve(this.get('store').peekRecord('job', data.job.id));
+      }, (err) => {
+        reject(err);
+      });
+    });
+  },
+
+  waitForStatsGenerationToComplete(job, fetchDummyResult = true) {
+    return new Promise((resolve, reject) => {
+      this.get('jobs').waitForJobToComplete(job.get('id'), 5 * 1000, fetchDummyResult).then((data) => {
+        resolve(job);
+      }, (err) => {
+        reject(err);
+      });
+    });
+  },
+
+  fetchColumnStatsResult(databaseName, tableName, columnName, job) {
+    return this.get('store').adapterFor('table').fetchColumnStats(databaseName, tableName, columnName, job.get('id')).then((data) => {
+      let columnStats = data.columnStats;
+      return columnStatsKeys.map((item) => {
+        return {label: item.label, value: columnStats[item.dataKey]};
+      });
+    });
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/22d4e181/contrib/views/hive20/src/main/resources/ui/app/styles/app.scss
----------------------------------------------------------------------
diff --git a/contrib/views/hive20/src/main/resources/ui/app/styles/app.scss b/contrib/views/hive20/src/main/resources/ui/app/styles/app.scss
index 5ae65d1..e178222 100644
--- a/contrib/views/hive20/src/main/resources/ui/app/styles/app.scss
+++ b/contrib/views/hive20/src/main/resources/ui/app/styles/app.scss
@@ -194,6 +194,10 @@ $table-info-background: lighten($body-bg, 10%);
       }
     }
   }
+
+  .alert {
+    margin: 0;
+  }
 }
 
 .table-name-input {
@@ -209,13 +213,7 @@ pre {
   }
 }
 
-.dipayan {
-  .CodeMirror {
-    height: 100vh;
-  }
-}
-
-.dipayan123 {
+.scroll-fix {
   background-color: lighten($body-bg, 5%);
   height: calc(100vh - 180px);
   overflow-y: scroll;
@@ -810,10 +808,12 @@ pre {
   padding-bottom: 25px;
 }
 
-.authorizations {
-  &.alert {
-    margin: 0;
-  }
+.stats-section {
+  margin-top: 20px;
+}
+
+.col-stats-details {
+  padding-top: 10px;
 }
 
 
@@ -825,3 +825,4 @@ pre {
 
 
 
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/22d4e181/contrib/views/hive20/src/main/resources/ui/app/templates/components/table-statistics.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive20/src/main/resources/ui/app/templates/components/table-statistics.hbs b/contrib/views/hive20/src/main/resources/ui/app/templates/components/table-statistics.hbs
new file mode 100644
index 0000000..0ee3b13
--- /dev/null
+++ b/contrib/views/hive20/src/main/resources/ui/app/templates/components/table-statistics.hbs
@@ -0,0 +1,153 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF 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.
+}}
+
+<div class="row">
+  <div class="alert">
+    <p class="lead">
+      {{fa-icon "line-chart" size=2}}&nbsp;&nbsp;&nbsp;&nbsp; STATISTICS
+      <div class="pull-right">
+        <button class="btn btn-success"
+          {{action "analyseTable"}}>{{fa-icon "location-arrow"}} {{#if (not tableStatisticsEnabled)}}
+          Compute {{else}} Recompute {{/if}}</button>
+
+        <label>
+          {{input type="checkbox" checked=analyseWithStatistics}}
+          <small>include columns</small>
+        </label>
+      </div>
+    </p>
+
+  </div>
+</div>
+<div class="row">
+  {{#if tableStatisticsEnabled}}
+    <div class="stats-section">
+      <p><strong>TABLE STATISTICS</strong></p>
+      <table class="table table-bordered table-hover">
+        <thead>
+        <tr>
+          <td width="50%">STATS NAME</td>
+          <td width="50%">VALUE</td>
+        </tr>
+        </thead>
+        <tbody>
+        <tr>
+          <td>Number of Files</td>
+          <td>{{tableStats.numFiles}}</td>
+        </tr>
+        <tr>
+          <td>Raw Data Size</td>
+          <td>{{tableStats.rawDataSize}}</td>
+        </tr>
+        <tr>
+          <td>Total Size</td>
+          <td>{{tableStats.totalSize}}</td>
+        </tr>
+        </tbody>
+      </table>
+    </div>
+  {{else}}
+    <div class="alert alert-danger">
+      <p>Table statistics are not computed</p>
+    </div>
+  {{/if}}
+
+</div>
+
+<div class="row stats-section">
+  <p><strong>COLUMNS STATISTICS</strong></p>
+  <table class="table table-bordered table-hover">
+    <thead>
+    <tr>
+      <td width="50%">COLUMN NAME</td>
+      <td width="50%">STATISTICS</td>
+    </tr>
+    </thead>
+    <tbody>
+    {{#each columns as |column|}}
+      <tr>
+        <td>{{column.name}}</td>
+        <td>
+          {{#if column.hasStatistics}}
+            {{#if column.stats}}
+              <button class="btn btn-success btn-sm" {{action "toggleShowStats" column}}>
+                {{fa-icon "location-arrow"}}
+                {{#if column.showStats}}
+                  Hide
+                {{else}}
+                  Show
+                {{/if}}
+              </button>
+            {{else}}
+              <button class="btn btn-success btn-sm" disabled={{or column.isFetchingStats column.stats}} {{action "fetchStats" column}}>
+                {{#if column.isFetchingStats}}
+                  {{fa-icon "cog" spin=true}} Fetching Stats
+                {{else}}
+                  {{fa-icon "location-arrow"}} Show
+                {{/if}}
+              </button>
+            {{/if}}
+
+            {{#if (and column.stats column.showStats)}}
+              <div class="col-stats-details">
+                <table class="table table-bordered table-hover ">
+                  <thead>
+                  <tr>
+                    <td width="50%">KEY</td>
+                    <td width="50%">VALUE</td>
+                  </tr>
+                  </thead>
+                  <tbody>
+                  {{#each column.stats as |stat| }}
+                    <tr>
+                      <td>{{stat.label}}</td>
+                      <td>{{stat.value}}</td>
+                    </tr>
+                  {{/each}}
+                  </tbody>
+                </table>
+              </div>
+            {{/if}}
+          {{else}}
+            No statistics computed
+          {{/if}}
+        </td>
+      </tr>
+    {{/each}}
+    </tbody>
+  </table>
+</div>
+
+{{#if showAnalyseModal}}
+  {{#modal-dialog
+    translucentOverlay=true
+    clickOutsideToClose=false
+    container-class="modal-dialog"}}
+    <div class="modal-content">
+      <div class="modal-header">
+        <p class="modal-title">{{fa-icon "location-arrow" size="lg"}} {{analyseTitle}}</p>
+      </div>
+      <div class="modal-body text-center">
+        <div class="alert alert-danger">
+          Analyse table statistics operation may take long time
+        </div>
+        <p><strong>{{analyseMessage}}</strong></p>
+      </div>
+    </div>
+  {{/modal-dialog}}
+{{/if}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/22d4e181/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables.hbs b/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables.hbs
index 1f98b97..c2d845b 100644
--- a/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables.hbs
+++ b/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables.hbs
@@ -16,7 +16,7 @@
 * limitations under the License.
 }}
 
-<div class="dipayan123 clearfix">
+<div class="scroll-fix clearfix">
   <div class="col-md-3">
     <div class="row">
       <div class="hv-dropdown tables-dropdown">

http://git-wip-us.apache.org/repos/asf/ambari/blob/22d4e181/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table/stats.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table/stats.hbs b/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table/stats.hbs
index 6671b8b..c522ac8 100644
--- a/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table/stats.hbs
+++ b/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table/stats.hbs
@@ -14,4 +14,6 @@
 * 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.
-}}
\ No newline at end of file
+}}
+
+{{table-statistics table=table refresh="refreshTableInfo"}}
\ No newline at end of file