You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kylin.apache.org by li...@apache.org on 2017/06/05 05:23:42 UTC
[54/67] [abbrv] kylin git commit: KYLIN-2575 translate computed
column back to expression when sending to adhoc
KYLIN-2575 translate computed column back to expression when sending to adhoc
Project: http://git-wip-us.apache.org/repos/asf/kylin/repo
Commit: http://git-wip-us.apache.org/repos/asf/kylin/commit/7c381487
Tree: http://git-wip-us.apache.org/repos/asf/kylin/tree/7c381487
Diff: http://git-wip-us.apache.org/repos/asf/kylin/diff/7c381487
Branch: refs/heads/master
Commit: 7c381487069d2bcf3adb07b38dc0b0803d13a8b4
Parents: 508fc23
Author: Hongbin Ma <ma...@apache.org>
Authored: Tue May 30 19:14:46 2017 +0800
Committer: hongbin ma <ma...@kyligence.io>
Committed: Tue May 30 23:47:23 2017 +0800
----------------------------------------------------------------------
assembly/pom.xml | 6 -
.../apache/kylin/metadata/MetadataManager.java | 12 +-
.../kylin/metadata/model/DataModelDesc.java | 8 +-
.../org/apache/kylin/query/KylinTestBase.java | 33 ++--
pom.xml | 4 +
server-base/pom.xml | 12 +-
.../apache/kylin/rest/service/QueryService.java | 2 +-
.../org/apache/kylin/rest/util/AdHocUtil.java | 154 ++++++++++++++++++-
.../apache/kylin/rest/util/AdHocUtilTest.java | 98 ++++++++++++
9 files changed, 296 insertions(+), 33 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/kylin/blob/7c381487/assembly/pom.xml
----------------------------------------------------------------------
diff --git a/assembly/pom.xml b/assembly/pom.xml
index dae7152..0a64dde 100644
--- a/assembly/pom.xml
+++ b/assembly/pom.xml
@@ -91,12 +91,6 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>org.apache.mrunit</groupId>
- <artifactId>mrunit</artifactId>
- <classifier>hadoop2</classifier>
- <scope>test</scope>
- </dependency>
- <dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-common</artifactId>
<scope>provided</scope>
http://git-wip-us.apache.org/repos/asf/kylin/blob/7c381487/core-metadata/src/main/java/org/apache/kylin/metadata/MetadataManager.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/MetadataManager.java b/core-metadata/src/main/java/org/apache/kylin/metadata/MetadataManager.java
index f8e6832..2a894b9 100644
--- a/core-metadata/src/main/java/org/apache/kylin/metadata/MetadataManager.java
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/MetadataManager.java
@@ -116,13 +116,21 @@ public class MetadataManager {
private CaseInsensitiveStringCache<ExternalFilterDesc> extFilterMap;
public static class CCInfo {
- public ComputedColumnDesc computedColumnDesc;
- public Set<DataModelDesc> dataModelDescs;
+ private ComputedColumnDesc computedColumnDesc;
+ private Set<DataModelDesc> dataModelDescs;
public CCInfo(ComputedColumnDesc computedColumnDesc, Set<DataModelDesc> dataModelDescs) {
this.computedColumnDesc = computedColumnDesc;
this.dataModelDescs = dataModelDescs;
}
+
+ public ComputedColumnDesc getComputedColumnDesc() {
+ return computedColumnDesc;
+ }
+
+ public Set<DataModelDesc> getDataModelDescs() {
+ return dataModelDescs;
+ }
}
private Map<String, CCInfo> ccInfoMap = Maps.newHashMap();// this is to check any two models won't conflict computed columns
http://git-wip-us.apache.org/repos/asf/kylin/blob/7c381487/core-metadata/src/main/java/org/apache/kylin/metadata/model/DataModelDesc.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/model/DataModelDesc.java b/core-metadata/src/main/java/org/apache/kylin/metadata/model/DataModelDesc.java
index e759bdf..f5092a8 100644
--- a/core-metadata/src/main/java/org/apache/kylin/metadata/model/DataModelDesc.java
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/DataModelDesc.java
@@ -476,13 +476,13 @@ public class DataModelDesc extends RootPersistentEntity {
}
CCInfo other = ccInfoMap.get(computedColumnDesc.getFullName());
- if (other == null || (other.dataModelDescs.size() == 1 && other.dataModelDescs.contains(this))) {
+ if (other == null || (other.getDataModelDescs().size() == 1 && other.getDataModelDescs().contains(this))) {
ccInfoMap.put(computedColumnDesc.getFullName(), new CCInfo(computedColumnDesc, Sets.<DataModelDesc> newHashSet(this)));
- } else if (other.computedColumnDesc.equals(computedColumnDesc)) {
- other.dataModelDescs.add(this);
+ } else if (other.getComputedColumnDesc().equals(computedColumnDesc)) {
+ other.getDataModelDescs().add(this);
} else {
throw new IllegalStateException(String.format("Computed column named %s is already defined in other models: %s. Please change another name, or try to keep consistent definition", //
- computedColumnDesc.getFullName(), other.dataModelDescs));
+ computedColumnDesc.getFullName(), other.getDataModelDescs()));
}
}
}
http://git-wip-us.apache.org/repos/asf/kylin/blob/7c381487/kylin-it/src/test/java/org/apache/kylin/query/KylinTestBase.java
----------------------------------------------------------------------
diff --git a/kylin-it/src/test/java/org/apache/kylin/query/KylinTestBase.java b/kylin-it/src/test/java/org/apache/kylin/query/KylinTestBase.java
index 42f3a44..0db5388 100644
--- a/kylin-it/src/test/java/org/apache/kylin/query/KylinTestBase.java
+++ b/kylin-it/src/test/java/org/apache/kylin/query/KylinTestBase.java
@@ -42,15 +42,14 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.logging.LogManager;
-import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.kylin.common.KylinConfig;
import org.apache.kylin.common.util.HBaseMetadataTestCase;
import org.apache.kylin.common.util.Pair;
import org.apache.kylin.metadata.project.ProjectInstance;
+import org.apache.kylin.metadata.querymeta.SelectedColumnMeta;
import org.apache.kylin.query.relnode.OLAPContext;
import org.apache.kylin.query.routing.rules.RemoveBlackoutRealizationsRule;
-import org.apache.kylin.metadata.querymeta.SelectedColumnMeta;
import org.apache.kylin.rest.util.AdHocUtil;
import org.dbunit.DatabaseUnitException;
import org.dbunit.database.DatabaseConfig;
@@ -70,6 +69,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
import com.google.common.io.Files;
/**
@@ -110,7 +110,8 @@ public class KylinTestBase {
// h2 (BIGINT)
public static class TestH2DataTypeFactory extends H2DataTypeFactory {
@Override
- public DataType createDataType(int sqlType, String sqlTypeName, String tableName, String columnName) throws DataTypeException {
+ public DataType createDataType(int sqlType, String sqlTypeName, String tableName, String columnName)
+ throws DataTypeException {
if ((columnName.startsWith("COL") || columnName.startsWith("col")) && sqlType == Types.BIGINT) {
return DataType.INTEGER;
@@ -223,7 +224,8 @@ public class KylinTestBase {
// ////////////////////////////////////////////////////////////////////////////////////////
// execute
- protected ITable executeQuery(IDatabaseConnection dbConn, String queryName, String sql, boolean needSort) throws Exception {
+ protected ITable executeQuery(IDatabaseConnection dbConn, String queryName, String sql, boolean needSort)
+ throws Exception {
// change join type to match current setting
sql = changeJoinType(sql, joinType);
@@ -258,9 +260,9 @@ public class KylinTestBase {
return output(resultSet, needDisplay);
} catch (SQLException sqlException) {
- List<List<String>> results = Lists.newArrayList();
+ List<List<String>> results = Lists.newArrayList();
List<SelectedColumnMeta> columnMetas = Lists.newArrayList();
- AdHocUtil.doAdHocQuery(sql, results, columnMetas, sqlException);
+ AdHocUtil.doAdHocQuery(ProjectInstance.DEFAULT_PROJECT_NAME, sql, results, columnMetas, sqlException);
return results.size();
} finally {
if (resultSet != null) {
@@ -280,7 +282,8 @@ public class KylinTestBase {
}
}
- protected ITable executeDynamicQuery(IDatabaseConnection dbConn, String queryName, String sql, List<String> parameters, boolean needSort) throws Exception {
+ protected ITable executeDynamicQuery(IDatabaseConnection dbConn, String queryName, String sql,
+ List<String> parameters, boolean needSort) throws Exception {
// change join type to match current setting
sql = changeJoinType(sql, joinType);
@@ -316,7 +319,8 @@ public class KylinTestBase {
String[] tokens = StringUtils.split(sql, null);// split white spaces
for (int i = 0; i < tokens.length - 1; ++i) {
- if ((tokens[i].equalsIgnoreCase("inner") || tokens[i].equalsIgnoreCase("left")) && tokens[i + 1].equalsIgnoreCase("join")) {
+ if ((tokens[i].equalsIgnoreCase("inner") || tokens[i].equalsIgnoreCase("left"))
+ && tokens[i + 1].equalsIgnoreCase("join")) {
tokens[i] = targetType.toLowerCase();
}
}
@@ -407,7 +411,8 @@ public class KylinTestBase {
}
}
- protected void execAndCompResultSize(String queryFolder, String[] exclusiveQuerys, boolean needSort) throws Exception {
+ protected void execAndCompResultSize(String queryFolder, String[] exclusiveQuerys, boolean needSort)
+ throws Exception {
logger.info("---------- test folder: " + queryFolder);
Set<String> exclusiveSet = buildExclusiveSet(exclusiveQuerys);
@@ -504,7 +509,6 @@ public class KylinTestBase {
logger.info("Queries appended with limit: " + appendLimitQueries);
}
-
protected void execAndCompQuery(String queryFolder, String[] exclusiveQuerys, boolean needSort) throws Exception {
execAndCompQuery(queryFolder, exclusiveQuerys, needSort, new ICompareQueryTranslator() {
@Override
@@ -518,7 +522,8 @@ public class KylinTestBase {
});
}
- protected void execAndCompQuery(String queryFolder, String[] exclusiveQuerys, boolean needSort, ICompareQueryTranslator translator) throws Exception {
+ protected void execAndCompQuery(String queryFolder, String[] exclusiveQuerys, boolean needSort,
+ ICompareQueryTranslator translator) throws Exception {
logger.info("---------- test folder: " + new File(queryFolder).getAbsolutePath());
Set<String> exclusiveSet = buildExclusiveSet(exclusiveQuerys);
@@ -557,7 +562,8 @@ public class KylinTestBase {
}
}
- protected void execAndCompDynamicQuery(String queryFolder, String[] exclusiveQuerys, boolean needSort) throws Exception {
+ protected void execAndCompDynamicQuery(String queryFolder, String[] exclusiveQuerys, boolean needSort)
+ throws Exception {
logger.info("---------- test folder: " + queryFolder);
Set<String> exclusiveSet = buildExclusiveSet(exclusiveQuerys);
@@ -683,7 +689,8 @@ public class KylinTestBase {
cubeConnection = QueryDataSource.create(ProjectInstance.DEFAULT_PROJECT_NAME, config).getConnection();
//setup h2
- h2Connection = DriverManager.getConnection("jdbc:h2:mem:db" + (h2InstanceCount++) + ";CACHE_SIZE=32072", "sa", "");
+ h2Connection = DriverManager.getConnection("jdbc:h2:mem:db" + (h2InstanceCount++) + ";CACHE_SIZE=32072", "sa",
+ "");
// Load H2 Tables (inner join)
H2Database h2DB = new H2Database(h2Connection, config);
h2DB.loadAllTables();
http://git-wip-us.apache.org/repos/asf/kylin/blob/7c381487/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 2fcc6fa..f887c8d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -93,6 +93,8 @@
<h2.version>1.4.192</h2.version>
<jetty.version>9.2.20.v20161216</jetty.version>
<jamm.version>0.3.1</jamm.version>
+ <mockito.version>2.7.14</mockito.version>
+
<!-- Commons -->
<commons-lang3.version>3.4</commons-lang3.version>
@@ -730,6 +732,8 @@
<artifactId>opensaml</artifactId>
<version>${opensaml.version}</version>
</dependency>
+
+
<!-- Spring Core -->
<dependency>
<groupId>org.springframework</groupId>
http://git-wip-us.apache.org/repos/asf/kylin/blob/7c381487/server-base/pom.xml
----------------------------------------------------------------------
diff --git a/server-base/pom.xml b/server-base/pom.xml
index b165b99..c7247a5 100644
--- a/server-base/pom.xml
+++ b/server-base/pom.xml
@@ -17,7 +17,8 @@
limitations under the License.
-->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
@@ -47,7 +48,7 @@
</exclusion>
</exclusions>
</dependency>
-
+
<!-- these plug-in modules, should not have API dependencies -->
<dependency>
<groupId>org.apache.kylin</groupId>
@@ -134,6 +135,13 @@
<type>test-jar</type>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ <!--MRUnit relies on older version of mockito, so cannot manage it globally-->
+ <version>${mockito.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
http://git-wip-us.apache.org/repos/asf/kylin/blob/7c381487/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 5130e55..06f9d80 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
@@ -729,7 +729,7 @@ public class QueryService extends BasicService {
results.add(oneRow);
}
} catch (SQLException sqlException) {
- isAdHoc = AdHocUtil.doAdHocQuery(correctedSql, results, columnMetas, sqlException);
+ isAdHoc = AdHocUtil.doAdHocQuery(sqlRequest.getProject(), correctedSql, results, columnMetas, sqlException);
} finally {
close(resultSet, stat, conn);
}
http://git-wip-us.apache.org/repos/asf/kylin/blob/7c381487/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
index 678e58e..8221790 100644
--- 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
@@ -18,11 +18,25 @@
package org.apache.kylin.rest.util;
+import static org.apache.kylin.metadata.MetadataManager.CCInfo;
+
import java.sql.SQLException;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.annotation.Nullable;
+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.metadata.MetadataManager;
+import org.apache.kylin.metadata.model.DataModelDesc;
+import org.apache.kylin.metadata.project.ProjectInstance;
+import org.apache.kylin.metadata.project.ProjectManager;
import org.apache.kylin.metadata.querymeta.SelectedColumnMeta;
import org.apache.kylin.query.routing.NoRealizationFoundException;
import org.apache.kylin.rest.exception.InternalErrorException;
@@ -31,11 +45,19 @@ import org.apache.kylin.storage.adhoc.IAdhocConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
public class AdHocUtil {
private static final Logger logger = LoggerFactory.getLogger(AdHocUtil.class);
- public static boolean doAdHocQuery(String sql, List<List<String>> results, List<SelectedColumnMeta> columnMetas, SQLException sqlException) throws Exception {
- boolean isExpectedCause = (ExceptionUtils.getRootCause(sqlException).getClass().equals(NoRealizationFoundException.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();
Boolean isAdHoc = false;
@@ -62,9 +84,10 @@ public class AdHocUtil {
runner.init();
try {
- String adhocSql = converter.convert(sql);
- if (!sql.equals(adhocSql)) {
- logger.info("the original query is converted to {} before delegating to adhoc", adhocSql);
+ String expandCC = restoreComputedColumnToExpr(sql, project);
+ String adhocSql = converter.convert(expandCC);
+ if (!adhocSql.equals(adhocSql)) {
+ logger.info("before delegating to adhoc, the query is converted to {} ", adhocSql);
}
runner.executeQuery(adhocSql, results, columnMetas);
@@ -78,4 +101,125 @@ public class AdHocUtil {
return isAdHoc;
}
+
+ 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) {
+ MetadataManager metadataManager = MetadataManager.getInstance(KylinConfig.getInstanceFromEnv());
+ Map<String, CCInfo> ccInfoMap = metadataManager.getCcInfoMap();
+ final ProjectInstance projectInstance = ProjectManager.getInstance(KylinConfig.getInstanceFromEnv())
+ .getProject(project);
+
+ Iterable<CCInfo> projectCCInfo = Iterables.filter(ccInfoMap.values(), new Predicate<CCInfo>() {
+ @Override
+ public boolean apply(@Nullable CCInfo ccInfo) {
+ return Iterables.any(ccInfo.getDataModelDescs(), new Predicate<DataModelDesc>() {
+ @Override
+ public boolean apply(@Nullable DataModelDesc model) {
+ return projectInstance.containsModel(model.getName());
+ }
+ });
+ }
+ });
+
+ String afterSql = beforeSql;
+ for (CCInfo ccInfo : projectCCInfo) {
+ afterSql = restoreComputedColumnToExpr(afterSql, ccInfo);
+ }
+
+ if (!StringUtils.equals(beforeSql, afterSql)) {
+ logger.info("computed column in sql is expanded before sending to adhoc engine: " + afterSql);
+ }
+ return afterSql;
+ }
+
+ static String restoreComputedColumnToExpr(String sql, CCInfo ccInfo) {
+
+ String ccName = ccInfo.getComputedColumnDesc().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(ccInfo.getComputedColumnDesc().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(ccInfo.getComputedColumnDesc().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(
+ ccInfo.getComputedColumnDesc().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(ccInfo.getComputedColumnDesc().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;
+ }
+
+ // identifier in expr must be DB.TABLE.COLUMN, all TABLE in expr should be guaranteed to be same
+ static String replaceIdentifierInExpr(String expr, String tableAlias, boolean quoted) {
+ List<Triple<Integer, Integer, String>> replacements = Lists.newArrayList();
+ Matcher matcher = identifierInExprPattern.matcher(expr);
+ while (matcher.find()) {
+
+ String t = tableAlias == null ? StringUtils.strip(matcher.group(3), ".") : tableAlias;
+ String c = matcher.group(4);
+
+ String replacement = quoted ? "\"" + t.toUpperCase() + "\".\"" + c.toUpperCase() + "\"" : t + "." + c;
+ replacements.add(Triple.of(matcher.start(1), matcher.end(1), replacement));
+ }
+
+ Collections.reverse(replacements);
+ for (Triple<Integer, Integer, String> triple : replacements) {
+ expr = expr.substring(0, triple.getLeft()) + triple.getRight() + expr.substring(triple.getMiddle());
+ }
+ return expr;
+ }
}
http://git-wip-us.apache.org/repos/asf/kylin/blob/7c381487/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
new file mode 100644
index 0000000..b93e2d3
--- /dev/null
+++ b/server-base/src/test/java/org/apache/kylin/rest/util/AdHocUtilTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.apache.kylin.metadata.MetadataManager.CCInfo;
+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("a.b.x * a.b.y", null, false);
+ Assert.assertEquals("b.x * b.y", ret);
+ }
+ {
+ String ret = AdHocUtil.replaceIdentifierInExpr("a_1.b_2.x_3 * a_1.b_2.y_3", null, false);
+ Assert.assertEquals("b_2.x_3 * b_2.y_3", ret);
+ }
+ {
+ String ret = AdHocUtil.replaceIdentifierInExpr("a.b.x * a.b.y", "c", false);
+ Assert.assertEquals("c.x * c.y", ret);
+ }
+ {
+ String ret = AdHocUtil.replaceIdentifierInExpr("a.b.x * a.b.y", "c", true);
+ Assert.assertEquals("\"C\".\"X\" * \"C\".\"Y\"", ret);
+ }
+ {
+ String ret = AdHocUtil.replaceIdentifierInExpr("substr(a.b.x,1,3)>a.b.y", "c", true);
+ Assert.assertEquals("substr(\"C\".\"X\",1,3)>\"C\".\"Y\"", ret);
+ }
+ {
+ String ret = AdHocUtil.replaceIdentifierInExpr("strcmp(substr(a.b.x,1,3),a.b.y) > 0", "c", true);
+ Assert.assertEquals("strcmp(substr(\"C\".\"X\",1,3),\"C\".\"Y\") > 0", ret);
+ }
+ {
+ String ret = AdHocUtil.replaceIdentifierInExpr("strcmp(substr(a.b.x,1,3),a.b.y) > 0", null, true);
+ Assert.assertEquals("strcmp(substr(\"B\".\"X\",1,3),\"B\".\"Y\") > 0", ret);
+ }
+ {
+ String ret = AdHocUtil.replaceIdentifierInExpr("strcmp(substr(a.b.x, 1, 3),a.b.y) > 0", null, false);
+ Assert.assertEquals("strcmp(substr(b.x, 1, 3),b.y) > 0", ret);
+ }
+ }
+
+ @Test
+ public void testRestoreComputedColumnToExpr() {
+
+ ComputedColumnDesc computedColumnDesc = mock(ComputedColumnDesc.class);
+ when(computedColumnDesc.getColumnName()).thenReturn("DEAL_AMOUNT");
+ when(computedColumnDesc.getExpression()).thenReturn("DB.TABLE.price * DB.TABLE.number");
+
+ CCInfo ccInfo = mock(CCInfo.class);
+ when(ccInfo.getComputedColumnDesc()).thenReturn(computedColumnDesc);
+
+ {
+ String ret = AdHocUtil.restoreComputedColumnToExpr(
+ "select DEAL_AMOUNT from DB.TABLE group by date order by DEAL_AMOUNT", ccInfo);
+ Assert.assertEquals(
+ "select (TABLE.price * TABLE.number) from DB.TABLE group by date order by (TABLE.price * TABLE.number)",
+ ret);
+ }
+ {
+ String ret = AdHocUtil.restoreComputedColumnToExpr(
+ "select DEAL_AMOUNT as DEAL_AMOUNT from DB.TABLE group by date order by DEAL_AMOUNT", ccInfo);
+ Assert.assertEquals(
+ "select (TABLE.price * TABLE.number) as DEAL_AMOUNT from DB.TABLE group by date order by (TABLE.price * TABLE.number)",
+ ret);
+ }
+ {
+ String ret = AdHocUtil.restoreComputedColumnToExpr(
+ "select \"DEAL_AMOUNT\" AS deal_amount from DB.TABLE group by date order by DEAL_AMOUNT", ccInfo);
+ Assert.assertEquals(
+ "select (\"TABLE\".\"PRICE\" * \"TABLE\".\"NUMBER\") AS deal_amount from DB.TABLE group by date order by (TABLE.price * TABLE.number)",
+ ret);
+ }
+ }
+}