You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kylin.apache.org by ka...@apache.org on 2017/07/07 08:38:40 UTC

[38/50] [abbrv] kylin git commit: minor, rename adhoc to pushdown in KYLIN

http://git-wip-us.apache.org/repos/asf/kylin/blob/7002dd86/server-base/src/main/java/org/apache/kylin/rest/service/QueryService.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/QueryService.java b/server-base/src/main/java/org/apache/kylin/rest/service/QueryService.java
index 8d18901..f3402ef 100644
--- a/server-base/src/main/java/org/apache/kylin/rest/service/QueryService.java
+++ b/server-base/src/main/java/org/apache/kylin/rest/service/QueryService.java
@@ -94,7 +94,7 @@ import org.apache.kylin.rest.request.PrepareSqlRequest;
 import org.apache.kylin.rest.request.SQLRequest;
 import org.apache.kylin.rest.response.SQLResponse;
 import org.apache.kylin.rest.util.AclUtil;
-import org.apache.kylin.rest.util.AdHocUtil;
+import org.apache.kylin.rest.util.PushDownUtil;
 import org.apache.kylin.rest.util.TableauInterceptor;
 import org.apache.kylin.storage.hybrid.HybridInstance;
 import org.slf4j.Logger;
@@ -220,7 +220,8 @@ public class QueryService extends BasicService {
             return null;
         }
         List<Query> queries = new ArrayList<Query>();
-        QueryRecord record = queryStore.getResource(getQueryKeyById(creator), QueryRecord.class, QueryRecordSerializer.getInstance());
+        QueryRecord record = queryStore.getResource(getQueryKeyById(creator), QueryRecord.class,
+                QueryRecordSerializer.getInstance());
         if (record != null) {
             for (Query query : record.getQueries()) {
                 queries.add(query);
@@ -235,7 +236,7 @@ public class QueryService extends BasicService {
         final Set<Long> cuboidIds = new HashSet<Long>();
         float duration = response.getDuration() / (float) 1000;
         boolean storageCacheUsed = response.isStorageCacheUsed();
-        boolean isAdHoc = response.isAdHoc();
+        boolean isPushDown = response.isPushDown();
 
         if (!response.isHitExceptionCache() && null != OLAPContext.getThreadLocalContexts()) {
             for (OLAPContext ctx : OLAPContext.getThreadLocalContexts()) {
@@ -276,7 +277,7 @@ public class QueryService extends BasicService {
         stringBuilder.append("Is Partial Result: ").append(response.isPartial()).append(newLine);
         stringBuilder.append("Hit Exception Cache: ").append(response.isHitExceptionCache()).append(newLine);
         stringBuilder.append("Storage cache used: ").append(storageCacheUsed).append(newLine);
-        stringBuilder.append("Is Ad-hoc Query: ").append(isAdHoc).append(newLine);
+        stringBuilder.append("Is Query Push-Down: ").append(isPushDown).append(newLine);
         stringBuilder.append("Message: ").append(response.getExceptionMessage()).append(newLine);
         stringBuilder.append("==========================[QUERY]===============================").append(newLine);
 
@@ -292,14 +293,16 @@ public class QueryService extends BasicService {
                 return;
             }
         } catch (AccessDeniedException e) {
-            logger.warn("Current user {} has no READ permission on current project {}, please ask Administrator for permission granting.");
+            logger.warn(
+                    "Current user {} has no READ permission on current project {}, please ask Administrator for permission granting.");
             //just continue
         }
 
         String realizationsStr = sqlResponse.getCube();//CUBE[name=abc],HYBRID[name=xyz]
 
         if (StringUtils.isEmpty(realizationsStr)) {
-            throw new AccessDeniedException("Ad-hoc query requires having READ permission on project, please ask Administrator to grant you permissions");
+            throw new AccessDeniedException(
+                    "Query pushdown requires having READ permission on project, please ask Administrator to grant you permissions");
         }
 
         String[] splits = StringUtils.split(realizationsStr, ",");
@@ -344,7 +347,8 @@ public class QueryService extends BasicService {
 
         KylinConfig kylinConfig = KylinConfig.getInstanceFromEnv();
         String serverMode = kylinConfig.getServerMode();
-        if (!(Constant.SERVER_MODE_QUERY.equals(serverMode.toLowerCase()) || Constant.SERVER_MODE_ALL.equals(serverMode.toLowerCase()))) {
+        if (!(Constant.SERVER_MODE_QUERY.equals(serverMode.toLowerCase())
+                || Constant.SERVER_MODE_ALL.equals(serverMode.toLowerCase()))) {
             throw new BadRequestException(String.format(msg.getQUERY_NOT_ALLOWED(), serverMode));
         }
         if (StringUtils.isBlank(sqlRequest.getProject())) {
@@ -370,7 +374,8 @@ public class QueryService extends BasicService {
             long startTime = System.currentTimeMillis();
 
             SQLResponse sqlResponse = null;
-            boolean queryCacheEnabled = checkCondition(kylinConfig.isQueryCacheEnabled(), "query cache disabled in KylinConfig") && //
+            boolean queryCacheEnabled = checkCondition(kylinConfig.isQueryCacheEnabled(),
+                    "query cache disabled in KylinConfig") && //
                     checkCondition(!BackdoorToggles.getDisableCache(), "query cache disabled in BackdoorToggles");
 
             if (queryCacheEnabled) {
@@ -386,13 +391,22 @@ public class QueryService extends BasicService {
                     long scanBytesThreshold = kylinConfig.getQueryScanBytesCacheThreshold();
                     sqlResponse.setDuration(System.currentTimeMillis() - startTime);
                     logger.info("Stats of SQL response: isException: {}, duration: {}, total scan count {}", //
-                            String.valueOf(sqlResponse.getIsException()), String.valueOf(sqlResponse.getDuration()), String.valueOf(sqlResponse.getTotalScanCount()));
+                            String.valueOf(sqlResponse.getIsException()), String.valueOf(sqlResponse.getDuration()),
+                            String.valueOf(sqlResponse.getTotalScanCount()));
                     if (checkCondition(queryCacheEnabled, "query cache is disabled") //
                             && checkCondition(!sqlResponse.getIsException(), "query has exception") //
-                            && checkCondition(sqlResponse.getDuration() > durationThreshold || sqlResponse.getTotalScanCount() > scanCountThreshold || sqlResponse.getTotalScanBytes() > scanBytesThreshold, //
-                                    "query is too lightweight with duration: {} (threshold {}), scan count: {} (threshold {}), scan bytes: {} (threshold {})", sqlResponse.getDuration(), durationThreshold, sqlResponse.getTotalScanCount(), scanCountThreshold, sqlResponse.getTotalScanBytes(), scanBytesThreshold)
-                            && checkCondition(sqlResponse.getResults().size() < kylinConfig.getLargeQueryThreshold(), "query response is too large: {} ({})", sqlResponse.getResults().size(), kylinConfig.getLargeQueryThreshold())) {
-                        cacheManager.getCache(SUCCESS_QUERY_CACHE).put(new Element(sqlRequest.getCacheKey(), sqlResponse));
+                            && checkCondition(
+                                    sqlResponse.getDuration() > durationThreshold
+                                            || sqlResponse.getTotalScanCount() > scanCountThreshold
+                                            || sqlResponse.getTotalScanBytes() > scanBytesThreshold, //
+                                    "query is too lightweight with duration: {} (threshold {}), scan count: {} (threshold {}), scan bytes: {} (threshold {})",
+                                    sqlResponse.getDuration(), durationThreshold, sqlResponse.getTotalScanCount(),
+                                    scanCountThreshold, sqlResponse.getTotalScanBytes(), scanBytesThreshold)
+                            && checkCondition(sqlResponse.getResults().size() < kylinConfig.getLargeQueryThreshold(),
+                                    "query response is too large: {} ({})", sqlResponse.getResults().size(),
+                                    kylinConfig.getLargeQueryThreshold())) {
+                        cacheManager.getCache(SUCCESS_QUERY_CACHE)
+                                .put(new Element(sqlRequest.getCacheKey(), sqlResponse));
                     }
 
                 } else {
@@ -411,7 +425,8 @@ public class QueryService extends BasicService {
                 sqlResponse.setTotalScanCount(queryContext.getScannedRows());
                 sqlResponse.setTotalScanBytes(queryContext.getScannedBytes());
 
-                if (queryCacheEnabled && e.getCause() != null && ExceptionUtils.getRootCause(e) instanceof ResourceLimitExceededException) {
+                if (queryCacheEnabled && e.getCause() != null
+                        && ExceptionUtils.getRootCause(e) instanceof ResourceLimitExceededException) {
                     Cache exceptionCache = cacheManager.getCache(EXCEPTION_QUERY_CACHE);
                     exceptionCache.put(new Element(sqlRequest.getCacheKey(), sqlResponse));
                 }
@@ -459,7 +474,8 @@ public class QueryService extends BasicService {
 
     private SQLResponse queryWithSqlMassage(SQLRequest sqlRequest) throws Exception {
         String userInfo = SecurityContextHolder.getContext().getAuthentication().getName();
-        final Collection<? extends GrantedAuthority> grantedAuthorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
+        final Collection<? extends GrantedAuthority> grantedAuthorities = SecurityContextHolder.getContext()
+                .getAuthentication().getAuthorities();
         for (GrantedAuthority grantedAuthority : grantedAuthorities) {
             userInfo += ",";
             userInfo += grantedAuthority.getAuthority();
@@ -514,9 +530,12 @@ public class QueryService extends BasicService {
                 String schemaName = JDBCTableMeta.getString(2);
 
                 // Not every JDBC data provider offers full 10 columns, e.g., PostgreSQL has only 5
-                TableMeta tblMeta = new TableMeta(catalogName == null ? Constant.FakeCatalogName : catalogName, schemaName == null ? Constant.FakeSchemaName : schemaName, JDBCTableMeta.getString(3), JDBCTableMeta.getString(4), JDBCTableMeta.getString(5), null, null, null, null, null);
+                TableMeta tblMeta = new TableMeta(catalogName == null ? Constant.FakeCatalogName : catalogName,
+                        schemaName == null ? Constant.FakeSchemaName : schemaName, JDBCTableMeta.getString(3),
+                        JDBCTableMeta.getString(4), JDBCTableMeta.getString(5), null, null, null, null, null);
 
-                if (!cubedOnly || getProjectManager().isExposedTable(project, schemaName + "." + tblMeta.getTABLE_NAME())) {
+                if (!cubedOnly
+                        || getProjectManager().isExposedTable(project, schemaName + "." + tblMeta.getTABLE_NAME())) {
                     tableMetas.add(tblMeta);
                     tableMap.put(tblMeta.getTABLE_SCHEM() + "#" + tblMeta.getTABLE_NAME(), tblMeta);
                 }
@@ -529,9 +548,18 @@ public class QueryService extends BasicService {
                 String schemaName = columnMeta.getString(2);
 
                 // kylin(optiq) is not strictly following JDBC specification
-                ColumnMeta colmnMeta = new ColumnMeta(catalogName == null ? Constant.FakeCatalogName : catalogName, schemaName == null ? Constant.FakeSchemaName : schemaName, columnMeta.getString(3), columnMeta.getString(4), columnMeta.getInt(5), columnMeta.getString(6), columnMeta.getInt(7), getInt(columnMeta.getString(8)), columnMeta.getInt(9), columnMeta.getInt(10), columnMeta.getInt(11), columnMeta.getString(12), columnMeta.getString(13), getInt(columnMeta.getString(14)), getInt(columnMeta.getString(15)), columnMeta.getInt(16), columnMeta.getInt(17), columnMeta.getString(18), columnMeta.getString(19), columnMeta.getString(20), columnMeta.getString(21), getShort(columnMeta.getString(22)), columnMeta.getString(23));
-
-                if (!cubedOnly || getProjectManager().isExposedColumn(project, schemaName + "." + colmnMeta.getTABLE_NAME(), colmnMeta.getCOLUMN_NAME())) {
+                ColumnMeta colmnMeta = new ColumnMeta(catalogName == null ? Constant.FakeCatalogName : catalogName,
+                        schemaName == null ? Constant.FakeSchemaName : schemaName, columnMeta.getString(3),
+                        columnMeta.getString(4), columnMeta.getInt(5), columnMeta.getString(6), columnMeta.getInt(7),
+                        getInt(columnMeta.getString(8)), columnMeta.getInt(9), columnMeta.getInt(10),
+                        columnMeta.getInt(11), columnMeta.getString(12), columnMeta.getString(13),
+                        getInt(columnMeta.getString(14)), getInt(columnMeta.getString(15)), columnMeta.getInt(16),
+                        columnMeta.getInt(17), columnMeta.getString(18), columnMeta.getString(19),
+                        columnMeta.getString(20), columnMeta.getString(21), getShort(columnMeta.getString(22)),
+                        columnMeta.getString(23));
+
+                if (!cubedOnly || getProjectManager().isExposedColumn(project,
+                        schemaName + "." + colmnMeta.getTABLE_NAME(), colmnMeta.getCOLUMN_NAME())) {
                     tableMap.get(colmnMeta.getTABLE_SCHEM() + "#" + colmnMeta.getTABLE_NAME()).addColumn(colmnMeta);
                 }
             }
@@ -551,7 +579,8 @@ public class QueryService extends BasicService {
     }
 
     @SuppressWarnings("checkstyle:methodlength")
-    protected List<TableMetaWithType> getMetadataV2(CubeManager cubeMgr, String project, boolean cubedOnly) throws SQLException, IOException {
+    protected List<TableMetaWithType> getMetadataV2(CubeManager cubeMgr, String project, boolean cubedOnly)
+            throws SQLException, IOException {
         //Message msg = MsgPicker.getMsg();
 
         Connection conn = null;
@@ -578,9 +607,13 @@ public class QueryService extends BasicService {
                 String schemaName = JDBCTableMeta.getString(2);
 
                 // Not every JDBC data provider offers full 10 columns, e.g., PostgreSQL has only 5
-                TableMetaWithType tblMeta = new TableMetaWithType(catalogName == null ? Constant.FakeCatalogName : catalogName, schemaName == null ? Constant.FakeSchemaName : schemaName, JDBCTableMeta.getString(3), JDBCTableMeta.getString(4), JDBCTableMeta.getString(5), null, null, null, null, null);
+                TableMetaWithType tblMeta = new TableMetaWithType(
+                        catalogName == null ? Constant.FakeCatalogName : catalogName,
+                        schemaName == null ? Constant.FakeSchemaName : schemaName, JDBCTableMeta.getString(3),
+                        JDBCTableMeta.getString(4), JDBCTableMeta.getString(5), null, null, null, null, null);
 
-                if (!cubedOnly || getProjectManager().isExposedTable(project, schemaName + "." + tblMeta.getTABLE_NAME())) {
+                if (!cubedOnly
+                        || getProjectManager().isExposedTable(project, schemaName + "." + tblMeta.getTABLE_NAME())) {
                     tableMetas.add(tblMeta);
                     tableMap.put(tblMeta.getTABLE_SCHEM() + "#" + tblMeta.getTABLE_NAME(), tblMeta);
                 }
@@ -593,11 +626,22 @@ public class QueryService extends BasicService {
                 String schemaName = columnMeta.getString(2);
 
                 // kylin(optiq) is not strictly following JDBC specification
-                ColumnMetaWithType colmnMeta = new ColumnMetaWithType(catalogName == null ? Constant.FakeCatalogName : catalogName, schemaName == null ? Constant.FakeSchemaName : schemaName, columnMeta.getString(3), columnMeta.getString(4), columnMeta.getInt(5), columnMeta.getString(6), columnMeta.getInt(7), getInt(columnMeta.getString(8)), columnMeta.getInt(9), columnMeta.getInt(10), columnMeta.getInt(11), columnMeta.getString(12), columnMeta.getString(13), getInt(columnMeta.getString(14)), getInt(columnMeta.getString(15)), columnMeta.getInt(16), columnMeta.getInt(17), columnMeta.getString(18), columnMeta.getString(19), columnMeta.getString(20), columnMeta.getString(21), getShort(columnMeta.getString(22)), columnMeta.getString(23));
-
-                if (!cubedOnly || getProjectManager().isExposedColumn(project, schemaName + "." + colmnMeta.getTABLE_NAME(), colmnMeta.getCOLUMN_NAME())) {
+                ColumnMetaWithType colmnMeta = new ColumnMetaWithType(
+                        catalogName == null ? Constant.FakeCatalogName : catalogName,
+                        schemaName == null ? Constant.FakeSchemaName : schemaName, columnMeta.getString(3),
+                        columnMeta.getString(4), columnMeta.getInt(5), columnMeta.getString(6), columnMeta.getInt(7),
+                        getInt(columnMeta.getString(8)), columnMeta.getInt(9), columnMeta.getInt(10),
+                        columnMeta.getInt(11), columnMeta.getString(12), columnMeta.getString(13),
+                        getInt(columnMeta.getString(14)), getInt(columnMeta.getString(15)), columnMeta.getInt(16),
+                        columnMeta.getInt(17), columnMeta.getString(18), columnMeta.getString(19),
+                        columnMeta.getString(20), columnMeta.getString(21), getShort(columnMeta.getString(22)),
+                        columnMeta.getString(23));
+
+                if (!cubedOnly || getProjectManager().isExposedColumn(project,
+                        schemaName + "." + colmnMeta.getTABLE_NAME(), colmnMeta.getCOLUMN_NAME())) {
                     tableMap.get(colmnMeta.getTABLE_SCHEM() + "#" + colmnMeta.getTABLE_NAME()).addColumn(colmnMeta);
-                    columnMap.put(colmnMeta.getTABLE_SCHEM() + "#" + colmnMeta.getTABLE_NAME() + "#" + colmnMeta.getCOLUMN_NAME(), colmnMeta);
+                    columnMap.put(colmnMeta.getTABLE_SCHEM() + "#" + colmnMeta.getTABLE_NAME() + "#"
+                            + colmnMeta.getCOLUMN_NAME(), colmnMeta);
                 }
             }
 
@@ -638,7 +682,8 @@ public class QueryService extends BasicService {
                 for (JoinTableDesc joinTableDesc : dataModelDesc.getJoinTables()) {
                     JoinDesc joinDesc = joinTableDesc.getJoin();
                     for (String pk : joinDesc.getPrimaryKey()) {
-                        String columnIdentity = (dataModelDesc.findTable(pk.substring(0, pk.indexOf("."))).getTableIdentity() + pk.substring(pk.indexOf("."))).replace('.', '#');
+                        String columnIdentity = (dataModelDesc.findTable(pk.substring(0, pk.indexOf(".")))
+                                .getTableIdentity() + pk.substring(pk.indexOf("."))).replace('.', '#');
                         if (columnMap.containsKey(columnIdentity)) {
                             columnMap.get(columnIdentity).getTYPE().add(ColumnMetaWithType.columnTypeEnum.PK);
                         } else {
@@ -647,7 +692,8 @@ public class QueryService extends BasicService {
                     }
 
                     for (String fk : joinDesc.getForeignKey()) {
-                        String columnIdentity = (dataModelDesc.findTable(fk.substring(0, fk.indexOf("."))).getTableIdentity() + fk.substring(fk.indexOf("."))).replace('.', '#');
+                        String columnIdentity = (dataModelDesc.findTable(fk.substring(0, fk.indexOf(".")))
+                                .getTableIdentity() + fk.substring(fk.indexOf("."))).replace('.', '#');
                         if (columnMap.containsKey(columnIdentity)) {
                             columnMap.get(columnIdentity).getTYPE().add(ColumnMetaWithType.columnTypeEnum.FK);
                         } else {
@@ -660,7 +706,8 @@ public class QueryService extends BasicService {
                 List<ModelDimensionDesc> dimensions = dataModelDesc.getDimensions();
                 for (ModelDimensionDesc dimension : dimensions) {
                     for (String column : dimension.getColumns()) {
-                        String columnIdentity = (dataModelDesc.findTable(dimension.getTable()).getTableIdentity() + "." + column).replace('.', '#');
+                        String columnIdentity = (dataModelDesc.findTable(dimension.getTable()).getTableIdentity() + "."
+                                + column).replace('.', '#');
                         if (columnMap.containsKey(columnIdentity)) {
                             columnMap.get(columnIdentity).getTYPE().add(ColumnMetaWithType.columnTypeEnum.DIMENSION);
                         } else {
@@ -672,7 +719,8 @@ public class QueryService extends BasicService {
 
                 String[] measures = dataModelDesc.getMetrics();
                 for (String measure : measures) {
-                    String columnIdentity = (dataModelDesc.findTable(measure.substring(0, measure.indexOf("."))).getTableIdentity() + measure.substring(measure.indexOf("."))).replace('.', '#');
+                    String columnIdentity = (dataModelDesc.findTable(measure.substring(0, measure.indexOf(".")))
+                            .getTableIdentity() + measure.substring(measure.indexOf("."))).replace('.', '#');
                     if (columnMap.containsKey(columnIdentity)) {
                         columnMap.get(columnIdentity).getTYPE().add(ColumnMetaWithType.columnTypeEnum.MEASURE);
                     } else {
@@ -703,7 +751,7 @@ public class QueryService extends BasicService {
         Connection conn = null;
         Statement stat = null;
         ResultSet resultSet = null;
-        Boolean isAdHoc = false;
+        Boolean isPushDown = false;
 
         List<List<String>> results = Lists.newArrayList();
         List<SelectedColumnMeta> columnMetas = Lists.newArrayList();
@@ -713,7 +761,7 @@ public class QueryService extends BasicService {
 
             // special case for prepare query. 
             if (BackdoorToggles.getPrepareOnly()) {
-                return getPrepareOnlySqlResponse(correctedSql, conn, isAdHoc, results, columnMetas);
+                return getPrepareOnlySqlResponse(correctedSql, conn, isPushDown, results, columnMetas);
             }
 
             stat = conn.createStatement();
@@ -725,7 +773,13 @@ public class QueryService extends BasicService {
 
             // Fill in selected column meta
             for (int i = 1; i <= columnCount; ++i) {
-                columnMetas.add(new SelectedColumnMeta(metaData.isAutoIncrement(i), metaData.isCaseSensitive(i), metaData.isSearchable(i), metaData.isCurrency(i), metaData.isNullable(i), metaData.isSigned(i), metaData.getColumnDisplaySize(i), metaData.getColumnLabel(i), metaData.getColumnName(i), metaData.getSchemaName(i), metaData.getCatalogName(i), metaData.getTableName(i), metaData.getPrecision(i), metaData.getScale(i), metaData.getColumnType(i), metaData.getColumnTypeName(i), metaData.isReadOnly(i), metaData.isWritable(i), metaData.isDefinitelyWritable(i)));
+                columnMetas.add(new SelectedColumnMeta(metaData.isAutoIncrement(i), metaData.isCaseSensitive(i),
+                        metaData.isSearchable(i), metaData.isCurrency(i), metaData.isNullable(i), metaData.isSigned(i),
+                        metaData.getColumnDisplaySize(i), metaData.getColumnLabel(i), metaData.getColumnName(i),
+                        metaData.getSchemaName(i), metaData.getCatalogName(i), metaData.getTableName(i),
+                        metaData.getPrecision(i), metaData.getScale(i), metaData.getColumnType(i),
+                        metaData.getColumnTypeName(i), metaData.isReadOnly(i), metaData.isWritable(i),
+                        metaData.isDefinitelyWritable(i)));
             }
 
             // fill in results
@@ -738,17 +792,17 @@ public class QueryService extends BasicService {
                 results.add(oneRow);
             }
         } catch (SQLException sqlException) {
-            isAdHoc = AdHocUtil.doAdHocQuery(sqlRequest.getProject(), correctedSql, results, columnMetas, sqlException);
+            isPushDown = PushDownUtil.doPushDownQuery(sqlRequest.getProject(), correctedSql, results, columnMetas,
+                    sqlException);
         } finally {
             close(resultSet, stat, conn);
         }
 
-        return getSqlResponse(isAdHoc, results, columnMetas);
+        return getSqlResponse(isPushDown, results, columnMetas);
     }
 
-    private SQLResponse getPrepareOnlySqlResponse(String correctedSql, Connection conn,
-        Boolean isAdHoc, List<List<String>> results, List<SelectedColumnMeta> columnMetas)
-        throws SQLException {
+    private SQLResponse getPrepareOnlySqlResponse(String correctedSql, Connection conn, Boolean isPushDown,
+            List<List<String>> results, List<SelectedColumnMeta> columnMetas) throws SQLException {
 
         CalcitePrepareImpl.KYLIN_ONLY_PREPARE.set(true);
 
@@ -772,7 +826,12 @@ public class QueryService extends BasicService {
                     String columnName = field.getKey();
                     BasicSqlType basicSqlType = (BasicSqlType) field.getValue();
 
-                    columnMetas.add(new SelectedColumnMeta(false, config.caseSensitive(), false, false, basicSqlType.isNullable() ? 1 : 0, true, basicSqlType.getPrecision(), columnName, columnName, null, null, null, basicSqlType.getPrecision(), basicSqlType.getScale() < 0 ? 0 : basicSqlType.getScale(), basicSqlType.getSqlTypeName().getJdbcOrdinal(), basicSqlType.getSqlTypeName().getName(), true, false, false));
+                    columnMetas.add(new SelectedColumnMeta(false, config.caseSensitive(), false, false,
+                            basicSqlType.isNullable() ? 1 : 0, true, basicSqlType.getPrecision(), columnName,
+                            columnName, null, null, null, basicSqlType.getPrecision(),
+                            basicSqlType.getScale() < 0 ? 0 : basicSqlType.getScale(),
+                            basicSqlType.getSqlTypeName().getJdbcOrdinal(), basicSqlType.getSqlTypeName().getName(),
+                            true, false, false));
                 }
 
             } else {
@@ -782,10 +841,11 @@ public class QueryService extends BasicService {
             CalcitePrepareImpl.KYLIN_ONLY_PREPARE.set(false);
         }
 
-        return getSqlResponse(isAdHoc, results, columnMetas);
+        return getSqlResponse(isPushDown, results, columnMetas);
     }
 
-    private SQLResponse getSqlResponse(Boolean isAdHoc, List<List<String>> results, List<SelectedColumnMeta> columnMetas) {
+    private SQLResponse getSqlResponse(Boolean isPushDown, List<List<String>> results,
+            List<SelectedColumnMeta> columnMetas) {
 
         boolean isPartialResult = false;
         StringBuilder cubeSb = new StringBuilder();
@@ -804,7 +864,8 @@ public class QueryService extends BasicService {
         }
         logger.info(logSb.toString());
 
-        SQLResponse response = new SQLResponse(columnMetas, results, cubeSb.toString(), 0, false, null, isPartialResult, isAdHoc);
+        SQLResponse response = new SQLResponse(columnMetas, results, cubeSb.toString(), 0, false, null, isPartialResult,
+                isPushDown);
         response.setTotalScanCount(QueryContext.current().getScannedRows());
         response.setTotalScanBytes(QueryContext.current().getScannedBytes());
         return response;
@@ -815,7 +876,8 @@ public class QueryService extends BasicService {
      * @param param
      * @throws SQLException
      */
-    private void setParam(PreparedStatement preparedState, int index, PrepareSqlRequest.StateParam param) throws SQLException {
+    private void setParam(PreparedStatement preparedState, int index, PrepareSqlRequest.StateParam param)
+            throws SQLException {
         boolean isNull = (null == param.getValue());
 
         Class<?> clazz;

http://git-wip-us.apache.org/repos/asf/kylin/blob/7002dd86/server-base/src/main/java/org/apache/kylin/rest/util/AdHocUtil.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/util/AdHocUtil.java b/server-base/src/main/java/org/apache/kylin/rest/util/AdHocUtil.java
deleted file mode 100644
index 0eff508..0000000
--- a/server-base/src/main/java/org/apache/kylin/rest/util/AdHocUtil.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * 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.
-*/
-
-package org.apache.kylin.rest.util;
-
-import java.sql.SQLException;
-import java.util.Collections;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.commons.lang.StringUtils;
-import org.apache.commons.lang3.exception.ExceptionUtils;
-import org.apache.commons.lang3.tuple.Triple;
-import org.apache.kylin.common.KylinConfig;
-import org.apache.kylin.common.util.ClassUtil;
-import org.apache.kylin.metadata.MetadataManager;
-import org.apache.kylin.metadata.model.ComputedColumnDesc;
-import org.apache.kylin.metadata.model.DataModelDesc;
-import org.apache.kylin.metadata.model.tool.CalciteParser;
-import org.apache.kylin.metadata.querymeta.SelectedColumnMeta;
-import org.apache.kylin.query.routing.NoRealizationFoundException;
-import org.apache.kylin.source.adhocquery.IAdHocConverter;
-import org.apache.kylin.source.adhocquery.IAdHocRunner;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-
-public class AdHocUtil {
-    private static final Logger logger = LoggerFactory.getLogger(AdHocUtil.class);
-
-    public static boolean doAdHocQuery(String project, String sql, List<List<String>> results,
-            List<SelectedColumnMeta> columnMetas, SQLException sqlException) throws Exception {
-
-        boolean isExpectedCause = (ExceptionUtils.getRootCause(sqlException).getClass()
-                .equals(NoRealizationFoundException.class));
-        KylinConfig kylinConfig = KylinConfig.getInstanceFromEnv();
-
-        if (isExpectedCause && kylinConfig.isAdhocEnabled()) {
-
-            logger.info("Query failed to utilize pre-calculation, routing to other engines", sqlException);
-            IAdHocRunner runner = (IAdHocRunner) ClassUtil.newInstance(kylinConfig.getAdHocRunnerClassName());
-            IAdHocConverter converter = (IAdHocConverter) ClassUtil
-                    .newInstance(kylinConfig.getAdHocConverterClassName());
-
-            runner.init(kylinConfig);
-
-            logger.debug("Ad-hoc query runner {}", runner);
-
-            String expandCC = restoreComputedColumnToExpr(sql, project);
-            if (!StringUtils.equals(expandCC, sql)) {
-                logger.info("computed column in sql is expanded to:  " + expandCC);
-            }
-            String adhocSql = converter.convert(expandCC);
-            if (!adhocSql.equals(expandCC)) {
-                logger.info("the query is converted to {} according to kylin.query.ad-hoc.converter-class-name",
-                        adhocSql);
-            }
-
-            runner.executeQuery(adhocSql, results, columnMetas);
-
-            return true;
-        } else {
-            throw sqlException;
-        }
-    }
-
-    private final static Pattern identifierInSqlPattern = Pattern.compile(
-            //find pattern like "table"."column" or "column"
-            "((?<![\\p{L}_0-9\\.\\\"])(\\\"[\\p{L}_0-9]+\\\"\\.)?(\\\"[\\p{L}_0-9]+\\\")(?![\\p{L}_0-9\\.\\\"]))" + "|"
-            //find pattern like table.column or column
-                    + "((?<![\\p{L}_0-9\\.\\\"])([\\p{L}_0-9]+\\.)?([\\p{L}_0-9]+)(?![\\p{L}_0-9\\.\\\"]))");
-
-    private final static Pattern identifierInExprPattern = Pattern.compile(
-            // a.b.c
-            "((?<![\\p{L}_0-9\\.\\\"])([\\p{L}_0-9]+\\.)([\\p{L}_0-9]+\\.)([\\p{L}_0-9]+)(?![\\p{L}_0-9\\.\\\"]))");
-
-    private final static Pattern endWithAsPattern = Pattern.compile("\\s+as\\s+$", Pattern.CASE_INSENSITIVE);
-
-    public static String restoreComputedColumnToExpr(String beforeSql, String project) {
-        final MetadataManager metadataManager = MetadataManager.getInstance(KylinConfig.getInstanceFromEnv());
-        List<DataModelDesc> dataModelDescs = metadataManager.getModels(project);
-
-        String afterSql = beforeSql;
-        for (DataModelDesc dataModelDesc : dataModelDescs) {
-            for (ComputedColumnDesc computedColumnDesc : dataModelDesc.getComputedColumnDescs()) {
-                afterSql = restoreComputedColumnToExpr(afterSql, computedColumnDesc);
-            }
-        }
-
-        return afterSql;
-    }
-
-    static String restoreComputedColumnToExpr(String sql, ComputedColumnDesc computedColumnDesc) {
-
-        String ccName = computedColumnDesc.getColumnName();
-        List<Triple<Integer, Integer, String>> replacements = Lists.newArrayList();
-        Matcher matcher = identifierInSqlPattern.matcher(sql);
-
-        while (matcher.find()) {
-            if (matcher.group(1) != null) { //with quote case: "TABLE"."COLUMN"
-
-                String quotedColumnName = matcher.group(3);
-                Preconditions.checkNotNull(quotedColumnName);
-                String columnName = StringUtils.strip(quotedColumnName, "\"");
-                if (!columnName.equalsIgnoreCase(ccName)) {
-                    continue;
-                }
-
-                if (matcher.group(2) != null) { // table name exist 
-                    String quotedTableAlias = StringUtils.strip(matcher.group(2), ".");
-                    String tableAlias = StringUtils.strip(quotedTableAlias, "\"");
-                    replacements.add(Triple.of(matcher.start(1), matcher.end(1),
-                            replaceIdentifierInExpr(computedColumnDesc.getExpression(), tableAlias, true)));
-                } else { //only column
-                    if (endWithAsPattern.matcher(sql.substring(0, matcher.start(1))).find()) {
-                        //select DEAL_AMOUNT as "deal_amount" case
-                        continue;
-                    }
-                    replacements.add(Triple.of(matcher.start(1), matcher.end(1),
-                            replaceIdentifierInExpr(computedColumnDesc.getExpression(), null, true)));
-                }
-            } else if (matcher.group(4) != null) { //without quote case: table.column or simply column
-                String columnName = matcher.group(6);
-                Preconditions.checkNotNull(columnName);
-                if (!columnName.equalsIgnoreCase(ccName)) {
-                    continue;
-                }
-
-                if (matcher.group(5) != null) { //table name exist
-                    String tableAlias = StringUtils.strip(matcher.group(5), ".");
-                    replacements.add(Triple.of(matcher.start(4), matcher.end(4),
-                            replaceIdentifierInExpr(computedColumnDesc.getExpression(), tableAlias, false)));
-
-                } else { //only column 
-                    if (endWithAsPattern.matcher(sql.substring(0, matcher.start(4))).find()) {
-                        //select DEAL_AMOUNT as deal_amount case
-                        continue;
-                    }
-                    replacements.add(Triple.of(matcher.start(4), matcher.end(4),
-                            replaceIdentifierInExpr(computedColumnDesc.getExpression(), null, false)));
-                }
-            }
-        }
-
-        Collections.reverse(replacements);
-        for (Triple<Integer, Integer, String> triple : replacements) {
-            sql = sql.substring(0, triple.getLeft()) + "(" + triple.getRight() + ")"
-                    + sql.substring(triple.getMiddle());
-        }
-        return sql;
-    }
-
-    static String replaceIdentifierInExpr(String expr, String tableAlias, boolean quoted) {
-        if (tableAlias == null) {
-            return expr;
-        }
-
-        return CalciteParser.insertAliasInExpr(expr, tableAlias);
-    }
-}

http://git-wip-us.apache.org/repos/asf/kylin/blob/7002dd86/server-base/src/main/java/org/apache/kylin/rest/util/PushDownUtil.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/util/PushDownUtil.java b/server-base/src/main/java/org/apache/kylin/rest/util/PushDownUtil.java
new file mode 100644
index 0000000..5d7f47a
--- /dev/null
+++ b/server-base/src/main/java/org/apache/kylin/rest/util/PushDownUtil.java
@@ -0,0 +1,177 @@
+/*
+ * 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.
+*/
+
+package org.apache.kylin.rest.util;
+
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.apache.commons.lang3.tuple.Triple;
+import org.apache.kylin.common.KylinConfig;
+import org.apache.kylin.common.util.ClassUtil;
+import org.apache.kylin.metadata.MetadataManager;
+import org.apache.kylin.metadata.model.ComputedColumnDesc;
+import org.apache.kylin.metadata.model.DataModelDesc;
+import org.apache.kylin.metadata.model.tool.CalciteParser;
+import org.apache.kylin.metadata.querymeta.SelectedColumnMeta;
+import org.apache.kylin.query.routing.NoRealizationFoundException;
+import org.apache.kylin.source.adhocquery.IPushDownConverter;
+import org.apache.kylin.source.adhocquery.IPushDownRunner;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+
+public class PushDownUtil {
+    private static final Logger logger = LoggerFactory.getLogger(PushDownUtil.class);
+
+    public static boolean doPushDownQuery(String project, String sql, List<List<String>> results,
+            List<SelectedColumnMeta> columnMetas, SQLException sqlException) throws Exception {
+
+        boolean isExpectedCause = (ExceptionUtils.getRootCause(sqlException).getClass()
+                .equals(NoRealizationFoundException.class));
+        KylinConfig kylinConfig = KylinConfig.getInstanceFromEnv();
+
+        if (isExpectedCause && kylinConfig.isPushDownEnabled()) {
+
+            logger.info("Query failed to utilize pre-calculation, routing to other engines", sqlException);
+            IPushDownRunner runner = (IPushDownRunner) ClassUtil.newInstance(kylinConfig.getPushDownRunnerClassName());
+            IPushDownConverter converter = (IPushDownConverter) ClassUtil
+                    .newInstance(kylinConfig.getPushDownConverterClassName());
+
+            runner.init(kylinConfig);
+
+            logger.debug("Query pushdown runner {}", runner);
+
+            String expandCC = restoreComputedColumnToExpr(sql, project);
+            if (!StringUtils.equals(expandCC, sql)) {
+                logger.info("computed column in sql is expanded to:  " + expandCC);
+            }
+            String adhocSql = converter.convert(expandCC);
+            if (!adhocSql.equals(expandCC)) {
+                logger.info("the query is converted to {} according to kylin.query.pushdown.converter-class-name",
+                        adhocSql);
+            }
+
+            runner.executeQuery(adhocSql, results, columnMetas);
+
+            return true;
+        } else {
+            throw sqlException;
+        }
+    }
+
+    private final static Pattern identifierInSqlPattern = Pattern.compile(
+            //find pattern like "table"."column" or "column"
+            "((?<![\\p{L}_0-9\\.\\\"])(\\\"[\\p{L}_0-9]+\\\"\\.)?(\\\"[\\p{L}_0-9]+\\\")(?![\\p{L}_0-9\\.\\\"]))" + "|"
+            //find pattern like table.column or column
+                    + "((?<![\\p{L}_0-9\\.\\\"])([\\p{L}_0-9]+\\.)?([\\p{L}_0-9]+)(?![\\p{L}_0-9\\.\\\"]))");
+
+    private final static Pattern identifierInExprPattern = Pattern.compile(
+            // a.b.c
+            "((?<![\\p{L}_0-9\\.\\\"])([\\p{L}_0-9]+\\.)([\\p{L}_0-9]+\\.)([\\p{L}_0-9]+)(?![\\p{L}_0-9\\.\\\"]))");
+
+    private final static Pattern endWithAsPattern = Pattern.compile("\\s+as\\s+$", Pattern.CASE_INSENSITIVE);
+
+    public static String restoreComputedColumnToExpr(String beforeSql, String project) {
+        final MetadataManager metadataManager = MetadataManager.getInstance(KylinConfig.getInstanceFromEnv());
+        List<DataModelDesc> dataModelDescs = metadataManager.getModels(project);
+
+        String afterSql = beforeSql;
+        for (DataModelDesc dataModelDesc : dataModelDescs) {
+            for (ComputedColumnDesc computedColumnDesc : dataModelDesc.getComputedColumnDescs()) {
+                afterSql = restoreComputedColumnToExpr(afterSql, computedColumnDesc);
+            }
+        }
+        return afterSql;
+    }
+
+    static String restoreComputedColumnToExpr(String sql, ComputedColumnDesc computedColumnDesc) {
+
+        String ccName = computedColumnDesc.getColumnName();
+        List<Triple<Integer, Integer, String>> replacements = Lists.newArrayList();
+        Matcher matcher = identifierInSqlPattern.matcher(sql);
+
+        while (matcher.find()) {
+            if (matcher.group(1) != null) { //with quote case: "TABLE"."COLUMN"
+
+                String quotedColumnName = matcher.group(3);
+                Preconditions.checkNotNull(quotedColumnName);
+                String columnName = StringUtils.strip(quotedColumnName, "\"");
+                if (!columnName.equalsIgnoreCase(ccName)) {
+                    continue;
+                }
+
+                if (matcher.group(2) != null) { // table name exist 
+                    String quotedTableAlias = StringUtils.strip(matcher.group(2), ".");
+                    String tableAlias = StringUtils.strip(quotedTableAlias, "\"");
+                    replacements.add(Triple.of(matcher.start(1), matcher.end(1),
+                            replaceIdentifierInExpr(computedColumnDesc.getExpression(), tableAlias, true)));
+                } else { //only column
+                    if (endWithAsPattern.matcher(sql.substring(0, matcher.start(1))).find()) {
+                        //select DEAL_AMOUNT as "deal_amount" case
+                        continue;
+                    }
+                    replacements.add(Triple.of(matcher.start(1), matcher.end(1),
+                            replaceIdentifierInExpr(computedColumnDesc.getExpression(), null, true)));
+                }
+            } else if (matcher.group(4) != null) { //without quote case: table.column or simply column
+                String columnName = matcher.group(6);
+                Preconditions.checkNotNull(columnName);
+                if (!columnName.equalsIgnoreCase(ccName)) {
+                    continue;
+                }
+
+                if (matcher.group(5) != null) { //table name exist
+                    String tableAlias = StringUtils.strip(matcher.group(5), ".");
+                    replacements.add(Triple.of(matcher.start(4), matcher.end(4),
+                            replaceIdentifierInExpr(computedColumnDesc.getExpression(), tableAlias, false)));
+
+                } else { //only column 
+                    if (endWithAsPattern.matcher(sql.substring(0, matcher.start(4))).find()) {
+                        //select DEAL_AMOUNT as deal_amount case
+                        continue;
+                    }
+                    replacements.add(Triple.of(matcher.start(4), matcher.end(4),
+                            replaceIdentifierInExpr(computedColumnDesc.getExpression(), null, false)));
+                }
+            }
+        }
+
+        Collections.reverse(replacements);
+        for (Triple<Integer, Integer, String> triple : replacements) {
+            sql = sql.substring(0, triple.getLeft()) + "(" + triple.getRight() + ")"
+                    + sql.substring(triple.getMiddle());
+        }
+        return sql;
+    }
+
+    static String replaceIdentifierInExpr(String expr, String tableAlias, boolean quoted) {
+        if (tableAlias == null) {
+            return expr;
+        }
+
+        return CalciteParser.insertAliasInExpr(expr, tableAlias);
+    }
+}

http://git-wip-us.apache.org/repos/asf/kylin/blob/7002dd86/server-base/src/test/java/org/apache/kylin/rest/util/AdHocUtilTest.java
----------------------------------------------------------------------
diff --git a/server-base/src/test/java/org/apache/kylin/rest/util/AdHocUtilTest.java b/server-base/src/test/java/org/apache/kylin/rest/util/AdHocUtilTest.java
deleted file mode 100644
index 05b135a..0000000
--- a/server-base/src/test/java/org/apache/kylin/rest/util/AdHocUtilTest.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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.
-*/
-package org.apache.kylin.rest.util;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import org.apache.kylin.metadata.model.ComputedColumnDesc;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class AdHocUtilTest {
-
-    @Test
-    public void testReplaceIdentifierInExpr() {
-        {
-            String ret = AdHocUtil.replaceIdentifierInExpr("x * y", null, false);
-            Assert.assertEquals("x * y", ret);
-        }
-        {
-            String ret = AdHocUtil.replaceIdentifierInExpr("x_3 * y_3", "b_2", false);
-            Assert.assertEquals("b_2.x_3 * b_2.y_3", ret);
-        }
-        {
-            String ret = AdHocUtil.replaceIdentifierInExpr("substr(x,1,3)>y", "c", true);
-            Assert.assertEquals("substr(c.x,1,3)>c.y", ret);
-        }
-        {
-            String ret = AdHocUtil.replaceIdentifierInExpr("strcmp(substr(x,1,3),y)", "c", true);
-            Assert.assertEquals("strcmp(substr(c.x,1,3),c.y)", ret);
-        }
-        {
-            String ret = AdHocUtil.replaceIdentifierInExpr("strcmp(substr(x,1,3),y)", null, true);
-            Assert.assertEquals("strcmp(substr(x,1,3),y)", ret);
-        }
-        {
-            String ret = AdHocUtil.replaceIdentifierInExpr("strcmp(substr(x,1,3),y)", null, false);
-            Assert.assertEquals("strcmp(substr(x,1,3),y)", ret);
-        }
-    }
-
-    @Test
-    public void testRestoreComputedColumnToExpr() {
-
-        ComputedColumnDesc computedColumnDesc = mock(ComputedColumnDesc.class);
-        when(computedColumnDesc.getColumnName()).thenReturn("DEAL_AMOUNT");
-        when(computedColumnDesc.getExpression()).thenReturn("price * number");
-
-        {
-            String ret = AdHocUtil.restoreComputedColumnToExpr(
-                    "select DEAL_AMOUNT from DB.TABLE group by date order by DEAL_AMOUNT", computedColumnDesc);
-            Assert.assertEquals("select (price * number) from DB.TABLE group by date order by (price * number)", ret);
-        }
-        {
-            String ret = AdHocUtil.restoreComputedColumnToExpr(
-                    "select DEAL_AMOUNT as DEAL_AMOUNT from DB.TABLE group by date order by DEAL_AMOUNT",
-                    computedColumnDesc);
-            Assert.assertEquals(
-                    "select (price * number) as DEAL_AMOUNT from DB.TABLE group by date order by (price * number)",
-                    ret);
-        }
-        {
-            String ret = AdHocUtil.restoreComputedColumnToExpr(
-                    "select \"DEAL_AMOUNT\" AS deal_amount from DB.TABLE group by date order by DEAL_AMOUNT",
-                    computedColumnDesc);
-            Assert.assertEquals(
-                    "select (price * number) AS deal_amount from DB.TABLE group by date order by (price * number)",
-                    ret);
-        }
-        {
-            String ret = AdHocUtil.restoreComputedColumnToExpr(
-                    "select x.DEAL_AMOUNT AS deal_amount from DB.TABLE x group by date order by x.DEAL_AMOUNT",
-                    computedColumnDesc);
-            Assert.assertEquals(
-                    "select (x.price * x.number) AS deal_amount from DB.TABLE x group by date order by (x.price * x.number)",
-                    ret);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/kylin/blob/7002dd86/server-base/src/test/java/org/apache/kylin/rest/util/PushDownUtilTest.java
----------------------------------------------------------------------
diff --git a/server-base/src/test/java/org/apache/kylin/rest/util/PushDownUtilTest.java b/server-base/src/test/java/org/apache/kylin/rest/util/PushDownUtilTest.java
new file mode 100644
index 0000000..5302a70
--- /dev/null
+++ b/server-base/src/test/java/org/apache/kylin/rest/util/PushDownUtilTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+*/
+package org.apache.kylin.rest.util;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.apache.kylin.metadata.model.ComputedColumnDesc;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class PushDownUtilTest {
+
+    @Test
+    public void testReplaceIdentifierInExpr() {
+        {
+            String ret = PushDownUtil.replaceIdentifierInExpr("x * y", null, false);
+            Assert.assertEquals("x * y", ret);
+        }
+        {
+            String ret = PushDownUtil.replaceIdentifierInExpr("x_3 * y_3", "b_2", false);
+            Assert.assertEquals("b_2.x_3 * b_2.y_3", ret);
+        }
+        {
+            String ret = PushDownUtil.replaceIdentifierInExpr("substr(x,1,3)>y", "c", true);
+            Assert.assertEquals("substr(c.x,1,3)>c.y", ret);
+        }
+        {
+            String ret = PushDownUtil.replaceIdentifierInExpr("strcmp(substr(x,1,3),y)", "c", true);
+            Assert.assertEquals("strcmp(substr(c.x,1,3),c.y)", ret);
+        }
+        {
+            String ret = PushDownUtil.replaceIdentifierInExpr("strcmp(substr(x,1,3),y)", null, true);
+            Assert.assertEquals("strcmp(substr(x,1,3),y)", ret);
+        }
+        {
+            String ret = PushDownUtil.replaceIdentifierInExpr("strcmp(substr(x,1,3),y)", null, false);
+            Assert.assertEquals("strcmp(substr(x,1,3),y)", ret);
+        }
+    }
+
+    @Test
+    public void testRestoreComputedColumnToExpr() {
+
+        ComputedColumnDesc computedColumnDesc = mock(ComputedColumnDesc.class);
+        when(computedColumnDesc.getColumnName()).thenReturn("DEAL_AMOUNT");
+        when(computedColumnDesc.getExpression()).thenReturn("price * number");
+
+        {
+            String ret = PushDownUtil.restoreComputedColumnToExpr(
+                    "select DEAL_AMOUNT from DB.TABLE group by date order by DEAL_AMOUNT", computedColumnDesc);
+            Assert.assertEquals("select (price * number) from DB.TABLE group by date order by (price * number)", ret);
+        }
+        {
+            String ret = PushDownUtil.restoreComputedColumnToExpr(
+                    "select DEAL_AMOUNT as DEAL_AMOUNT from DB.TABLE group by date order by DEAL_AMOUNT",
+                    computedColumnDesc);
+            Assert.assertEquals(
+                    "select (price * number) as DEAL_AMOUNT from DB.TABLE group by date order by (price * number)",
+                    ret);
+        }
+        {
+            String ret = PushDownUtil.restoreComputedColumnToExpr(
+                    "select \"DEAL_AMOUNT\" AS deal_amount from DB.TABLE group by date order by DEAL_AMOUNT",
+                    computedColumnDesc);
+            Assert.assertEquals(
+                    "select (price * number) AS deal_amount from DB.TABLE group by date order by (price * number)",
+                    ret);
+        }
+        {
+            String ret = PushDownUtil.restoreComputedColumnToExpr(
+                    "select x.DEAL_AMOUNT AS deal_amount from DB.TABLE x group by date order by x.DEAL_AMOUNT",
+                    computedColumnDesc);
+            Assert.assertEquals(
+                    "select (x.price * x.number) AS deal_amount from DB.TABLE x group by date order by (x.price * x.number)",
+                    ret);
+        }
+    }
+}