KYLIN-2108 refactor massageSql(), disable KeywordDefaultDirtyHack by default
Project: http://git-wip-us.apache.org/repos/asf/kylin/repo Commit: http://git-wip-us.apache.org/repos/asf/kylin/commit/6366215a Tree: http://git-wip-us.apache.org/repos/asf/kylin/tree/6366215a Diff: http://git-wip-us.apache.org/repos/asf/kylin/diff/6366215a Branch: refs/heads/KYLIN-1971 Commit: 6366215aef53514d070b9e54abd3716c5abc3615 Parents: ac1d7d6 Author: Li Yang <liy...@apache.org> Authored: Thu Oct 20 15:08:14 2016 +0800 Committer: Li Yang <liy...@apache.org> Committed: Thu Oct 20 15:10:25 2016 +0800 ---------------------------------------------------------------------- .../apache/kylin/common/KylinConfigBase.java | 4 + .../test_case_data/localmeta/kylin.properties | 2 + .../apache/kylin/rest/service/QueryService.java | 3 +- .../rest/util/KeywordDefaultDirtyHack.java | 34 +++ .../org/apache/kylin/rest/util/QueryUtil.java | 231 ++++++++----------- .../kylin/rest/util/TableauInterceptor.java | 115 +++++++++ .../apache/kylin/rest/util/QueryUtilTest.java | 30 ++- 7 files changed, 274 insertions(+), 145 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/kylin/blob/6366215a/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java ---------------------------------------------------------------------- diff --git a/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java b/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java index 5a06813..f49127b 100644 --- a/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java +++ b/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java @@ -508,6 +508,10 @@ abstract public class KylinConfigBase implements Serializable { public boolean getBadQueryPersistentEnabled() { return Boolean.parseBoolean(getOptional("kylin.query.badquery.persistent.enable", "true")); } + + public String[] getQueryTransformers() { + return getOptionalStringArray("kylin.query.transformers", new String[0]); + } public int getCachedDictMaxEntrySize() { return Integer.parseInt(getOptional("kylin.dict.cache.max.entry", "3000")); http://git-wip-us.apache.org/repos/asf/kylin/blob/6366215a/examples/test_case_data/localmeta/kylin.properties ---------------------------------------------------------------------- diff --git a/examples/test_case_data/localmeta/kylin.properties b/examples/test_case_data/localmeta/kylin.properties index 678da39..5270e07 100644 --- a/examples/test_case_data/localmeta/kylin.properties +++ b/examples/test_case_data/localmeta/kylin.properties @@ -78,6 +78,8 @@ kylin.job.yarn.app.rest.check.interval.seconds=10 ### QUERY ### +kylin.query.transformers=org.apache.kylin.rest.util.KeywordDefaultDirtyHack + ### SECURITY ### # Spring security profile, options: testing, ldap, saml http://git-wip-us.apache.org/repos/asf/kylin/blob/6366215a/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 42136f9..94f2dd5 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 @@ -75,6 +75,7 @@ import org.apache.kylin.rest.response.SQLResponse; import org.apache.kylin.rest.util.QueryIdGenerator; import org.apache.kylin.rest.util.QueryUtil; import org.apache.kylin.rest.util.Serializer; +import org.apache.kylin.rest.util.TableauInterceptor; import org.apache.kylin.storage.exception.ScanOutOfLimitException; import org.apache.kylin.storage.hbase.HBaseConnection; import org.apache.kylin.storage.hybrid.HybridInstance; @@ -432,7 +433,7 @@ public class QueryService extends BasicService { userInfo += grantedAuthority.getAuthority(); } - SQLResponse fakeResponse = QueryUtil.tableauIntercept(sqlRequest.getSql()); + SQLResponse fakeResponse = TableauInterceptor.tableauIntercept(sqlRequest.getSql()); if (null != fakeResponse) { logger.debug("Return fake response, is exception? " + fakeResponse.getIsException()); return fakeResponse; http://git-wip-us.apache.org/repos/asf/kylin/blob/6366215a/server-base/src/main/java/org/apache/kylin/rest/util/KeywordDefaultDirtyHack.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/util/KeywordDefaultDirtyHack.java b/server-base/src/main/java/org/apache/kylin/rest/util/KeywordDefaultDirtyHack.java new file mode 100644 index 0000000..8d8d971 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/util/KeywordDefaultDirtyHack.java @@ -0,0 +1,34 @@ +/* + * 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 org.apache.kylin.rest.util.QueryUtil.IQueryTransformer; + +public class KeywordDefaultDirtyHack implements IQueryTransformer { + + @Override + public String transform(String sql) { + // KYLIN-2108, DEFAULT is hive default database, but a sql keyword too, needs quote + sql = sql.replace("DEFAULT.", "\"DEFAULT\"."); + sql = sql.replace("default.", "\"default\"."); + + return sql; + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/6366215a/server-base/src/main/java/org/apache/kylin/rest/util/QueryUtil.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/util/QueryUtil.java b/server-base/src/main/java/org/apache/kylin/rest/util/QueryUtil.java index cc9d32e..66619fe 100644 --- a/server-base/src/main/java/org/apache/kylin/rest/util/QueryUtil.java +++ b/server-base/src/main/java/org/apache/kylin/rest/util/QueryUtil.java @@ -18,87 +18,28 @@ package org.apache.kylin.rest.util; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.kylin.rest.model.SelectedColumnMeta; +import org.apache.kylin.common.KylinConfig; +import org.apache.kylin.common.util.ClassUtil; import org.apache.kylin.rest.request.SQLRequest; -import org.apache.kylin.rest.response.SQLResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.collect.Lists; + /** */ public class QueryUtil { protected static final Logger logger = LoggerFactory.getLogger(QueryUtil.class); - private static final String S0 = "\\s*"; - private static final String S1 = "\\s"; - private static final String SM = "\\s+"; - private static final Pattern PTN_GROUP_BY = Pattern.compile(S1 + "GROUP" + SM + "BY" + S1, Pattern.CASE_INSENSITIVE); - private static final Pattern PTN_HAVING_COUNT_GREATER_THAN_ZERO = Pattern.compile(S1 + "HAVING" + SM + "[(]?" + S0 + "COUNT" + S0 + "[(]" + S0 + "1" + S0 + "[)]" + S0 + ">" + S0 + "0" + S0 + "[)]?", Pattern.CASE_INSENSITIVE); - private static final Pattern PTN_SUM_1 = Pattern.compile(S1 + "SUM" + S0 + "[(]" + S0 + "[1]" + S0 + "[)]" + S1, Pattern.CASE_INSENSITIVE); - private static final Pattern PTN_INTERVAL = Pattern.compile("interval" + SM + "(floor\\()([\\d\\.]+)(\\))" + SM + "(second|minute|hour|day|month|year)", Pattern.CASE_INSENSITIVE); - private static final Pattern PTN_CONCAT = Pattern.compile("concat\\(.+?\\)");//non-greedy - - // private static final Pattern PTN_HAVING_ESCAPE_FUNCTION = - // Pattern.compile("\\{fn" + "(" + S0 + ")" + "\\}", - // Pattern.CASE_INSENSITIVE); - private static final Pattern PTN_HAVING_ESCAPE_FUNCTION = Pattern.compile("\\{fn" + "(.*?)" + "\\}", Pattern.CASE_INSENSITIVE); - - private static String[] tableauTestQueries = new String[] { "SELECT 1", // - "CREATE LOCAL TEMPORARY TABLE \"XTableau_B_Connect\" ( \"COL\" INTEGER ) ON COMMIT PRESERVE ROWS", // - "DROP TABLE \"XTableau_B_Connect\"", // - "SELECT \"COL\" FROM (SELECT 1 AS \"COL\") AS \"SUBQUERY\"", // - "SELECT TOP 1 \"COL\" FROM (SELECT 1 AS \"COL\") AS \"CHECKTOP\"", "SELECT \"COL\" FROM (SELECT 1 AS \"COL\") AS \"CHECKTOP\" LIMIT 1", // - "SELECT \"SUBCOL\" AS \"COL\" FROM ( SELECT 1 AS \"SUBCOL\" ) \"SUBQUERY\" GROUP BY 1", "SELECT \"SUBCOL\" AS \"COL\" FROM ( SELECT 1 AS \"SUBCOL\" ) \"SUBQUERY\" GROUP BY 2", "INSERT INTO \"XTableau_C_Connect\" SELECT * FROM (SELECT 1 AS COL) AS CHECKTEMP LIMIT 1", "DROP TABLE \"XTableau_C_Connect\"", "INSERT INTO \"XTableau_B_Connect\" SELECT * FROM (SELECT 1 AS COL) AS CHECKTEMP LIMIT 1" }; - - private static SQLResponse temp = new SQLResponse(new LinkedList<SelectedColumnMeta>() { - private static final long serialVersionUID = -8086728462624901359L; - - { - add(new SelectedColumnMeta(false, false, true, false, 2, true, 11, "COL", "COL", "", "", "", 10, 0, 4, "int4", false, true, false)); - } - }, new LinkedList<List<String>>() { - private static final long serialVersionUID = -470083340592928073L; - - { - add(new LinkedList<String>() { - private static final long serialVersionUID = -3673192785838230054L; - - { - add("1"); - } - }); - } - }, 0, false, null); - - private static SQLResponse[] fakeResponses = new SQLResponse[] { temp, new SQLResponse(null, null, 0, false, null), // - new SQLResponse(null, null, 0, false, null), // - temp, // - new SQLResponse(null, null, 0, true, "near 1 syntax error"), // - temp, // - new SQLResponse(null, null, 0, true, "group by 1????"), // - new SQLResponse(null, null, 0, true, "group by 2????"), // - new SQLResponse(null, null, 0, true, "XTableau_C_Connect not exist"), // - new SQLResponse(null, null, 0, true, "XTableau_C_Connect not exist"), new SQLResponse(null, null, 0, true, "XTableau_B_Connect not exist"), }; - - private static ArrayList<HashSet<String>> tableauTestQueriesInToken = new ArrayList<HashSet<String>>(); - - static { - for (String q : tableauTestQueries) { - HashSet<String> temp = new HashSet<String>(); - for (String token : q.split("[\r\n\t \\(\\)]")) { - temp.add(token); - } - temp.add(""); - tableauTestQueriesInToken.add(temp); - } + private static List<IQueryTransformer> queryTransformers; + + public interface IQueryTransformer { + String transform(String sql); } public static String massageSql(SQLRequest sqlRequest) { @@ -106,10 +47,6 @@ public class QueryUtil { sql = sql.trim(); sql = sql.replace("\r", " ").replace("\n", System.getProperty("line.separator")); - // KYLIN-2108, DEFAULT is hive default database, but a Calcite keyword too, needs quote - sql = sql.replace("DEFAULT.", "\"DEFAULT\"."); - sql = sql.replace("default.", "\"default\"."); - while (sql.endsWith(";")) sql = sql.substring(0, sql.length() - 1); @@ -123,78 +60,103 @@ public class QueryUtil { sql += ("\nOFFSET " + offset); } - return healSickSql(sql); - } - - // correct sick / invalid SQL - private static String healSickSql(String sql) { - Matcher m; - - // Case fn{ EXTRACT(...) } - // Use non-greedy regrex matching to remove escape functions - while (true) { - m = PTN_HAVING_ESCAPE_FUNCTION.matcher(sql); - if (!m.find()) - break; - sql = sql.substring(0, m.start()) + m.group(1) + sql.substring(m.end()); + // customizable SQL transformation + if (queryTransformers == null) { + initQueryTransformers(); } - - // Case: HAVING COUNT(1)>0 without Group By - // Tableau generates: SELECT SUM(1) AS "COL" FROM "VAC_SW" HAVING - // COUNT(1)>0 - m = PTN_HAVING_COUNT_GREATER_THAN_ZERO.matcher(sql); - if (m.find() && PTN_GROUP_BY.matcher(sql).find() == false) { - sql = sql.substring(0, m.start()) + " " + sql.substring(m.end()); + for (IQueryTransformer t : queryTransformers) { + sql = t.transform(sql); } + return sql; + } - // Case: SUM(1) - // Replace it with COUNT(1) - while (true) { - m = PTN_SUM_1.matcher(sql); - if (!m.find()) - break; - sql = sql.substring(0, m.start()) + " COUNT(1) " + sql.substring(m.end()); + private static void initQueryTransformers() { + List<IQueryTransformer> transformers = Lists.newArrayList(); + transformers.add(new DefaultQueryTransformer()); + + String[] classes = KylinConfig.getInstanceFromEnv().getQueryTransformers(); + for (String clz : classes) { + try { + IQueryTransformer t = (IQueryTransformer) ClassUtil.newInstance(clz); + transformers.add(t); + } catch (Exception e) { + logger.error("Failed to init query transformer", e); + } } + queryTransformers = transformers; + } - // ( date '2001-09-28' + interval floor(1) day ) generated by cognos - // calcite only recognizes date '2001-09-28' + interval '1' day - while (true) { - m = PTN_INTERVAL.matcher(sql); - if (!m.find()) - break; + // correct sick / invalid SQL + private static class DefaultQueryTransformer implements IQueryTransformer { + + private static final String S0 = "\\s*"; + private static final String S1 = "\\s"; + private static final String SM = "\\s+"; + private static final Pattern PTN_GROUP_BY = Pattern.compile(S1 + "GROUP" + SM + "BY" + S1, Pattern.CASE_INSENSITIVE); + private static final Pattern PTN_HAVING_COUNT_GREATER_THAN_ZERO = Pattern.compile(S1 + "HAVING" + SM + "[(]?" + S0 + "COUNT" + S0 + "[(]" + S0 + "1" + S0 + "[)]" + S0 + ">" + S0 + "0" + S0 + "[)]?", Pattern.CASE_INSENSITIVE); + private static final Pattern PTN_SUM_1 = Pattern.compile(S1 + "SUM" + S0 + "[(]" + S0 + "[1]" + S0 + "[)]" + S1, Pattern.CASE_INSENSITIVE); + private static final Pattern PTN_INTERVAL = Pattern.compile("interval" + SM + "(floor\\()([\\d\\.]+)(\\))" + SM + "(second|minute|hour|day|month|year)", Pattern.CASE_INSENSITIVE); + private static final Pattern PTN_CONCAT = Pattern.compile("concat\\(.+?\\)");//non-greedy + private static final Pattern PTN_HAVING_ESCAPE_FUNCTION = Pattern.compile("\\{fn" + "(.*?)" + "\\}", Pattern.CASE_INSENSITIVE); + + @Override + public String transform(String sql) { + Matcher m; + + // Case fn{ EXTRACT(...) } + // Use non-greedy regrex matching to remove escape functions + while (true) { + m = PTN_HAVING_ESCAPE_FUNCTION.matcher(sql); + if (!m.find()) + break; + sql = sql.substring(0, m.start()) + m.group(1) + sql.substring(m.end()); + } - int value = (int) Math.floor(Double.valueOf(m.group(2))); - sql = sql.substring(0, m.start(1)) + "'" + value + "'" + sql.substring(m.end(3)); - } + // Case: HAVING COUNT(1)>0 without Group By + // Tableau generates: SELECT SUM(1) AS "COL" FROM "VAC_SW" HAVING + // COUNT(1)>0 + m = PTN_HAVING_COUNT_GREATER_THAN_ZERO.matcher(sql); + if (m.find() && PTN_GROUP_BY.matcher(sql).find() == false) { + sql = sql.substring(0, m.start()) + " " + sql.substring(m.end()); + } - //according to https://issues.apache.org/jira/browse/CALCITE-1375, - //{fn concat('a','b')} will succeed but concat('a','b') will fail - StringBuilder sb = new StringBuilder(); - while (true) { - m = PTN_CONCAT.matcher(sql); - if (!m.find()) - break; + // Case: SUM(1) + // Replace it with COUNT(1) + while (true) { + m = PTN_SUM_1.matcher(sql); + if (!m.find()) + break; + sql = sql.substring(0, m.start()) + " COUNT(1) " + sql.substring(m.end()); + } - sb.append(sql.substring(0, m.start()) + "{fn " + m.group(0) + " }"); - sql = sql.substring(m.end()); - } - String temp = sb.toString() + sql; - sql = "".equals(temp) ? sql : temp; + // ( date '2001-09-28' + interval floor(1) day ) generated by cognos + // calcite only recognizes date '2001-09-28' + interval '1' day + while (true) { + m = PTN_INTERVAL.matcher(sql); + if (!m.find()) + break; - return sql; - } + int value = (int) Math.floor(Double.valueOf(m.group(2))); + sql = sql.substring(0, m.start(1)) + "'" + value + "'" + sql.substring(m.end(3)); + } - public static SQLResponse tableauIntercept(String sql) { + //according to https://issues.apache.org/jira/browse/CALCITE-1375, + //{fn concat('a','b')} will succeed but concat('a','b') will fail + StringBuilder sb = new StringBuilder(); + while (true) { + m = PTN_CONCAT.matcher(sql); + if (!m.find()) + break; - String[] tokens = sql.split("[\r\n\t \\(\\)]"); - for (int i = 0; i < tableauTestQueries.length; ++i) { - if (isTokenWiseEqual(tokens, tableauTestQueriesInToken.get(i))) { - logger.info("Hit fake response " + i); - return fakeResponses[i]; + sb.append(sql.substring(0, m.start()) + "{fn " + m.group(0) + " }"); + sql = sql.substring(m.end()); } - } + String temp = sb.toString() + sql; + sql = "".equals(temp) ? sql : temp; - return null; + return sql; + } + } public static String makeErrorMsgUserFriendly(Throwable e) { @@ -230,13 +192,4 @@ public class QueryUtil { } } - private static boolean isTokenWiseEqual(String[] tokens, HashSet<String> tokenSet) { - for (String token : tokens) { - if (!tokenSet.contains(token)) { - return false; - } - } - return true; - } - } http://git-wip-us.apache.org/repos/asf/kylin/blob/6366215a/server-base/src/main/java/org/apache/kylin/rest/util/TableauInterceptor.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/util/TableauInterceptor.java b/server-base/src/main/java/org/apache/kylin/rest/util/TableauInterceptor.java new file mode 100644 index 0000000..afe91e1 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/util/TableauInterceptor.java @@ -0,0 +1,115 @@ +/* + * 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.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +import org.apache.kylin.rest.model.SelectedColumnMeta; +import org.apache.kylin.rest.response.SQLResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TableauInterceptor { + + protected static final Logger logger = LoggerFactory.getLogger(TableauInterceptor.class); + + private static String[] tableauTestQueries = new String[] { "SELECT 1", // + "CREATE LOCAL TEMPORARY TABLE \"XTableau_B_Connect\" ( \"COL\" INTEGER ) ON COMMIT PRESERVE ROWS", // + "DROP TABLE \"XTableau_B_Connect\"", // + "SELECT \"COL\" FROM (SELECT 1 AS \"COL\") AS \"SUBQUERY\"", // + "SELECT TOP 1 \"COL\" FROM (SELECT 1 AS \"COL\") AS \"CHECKTOP\"", // + "SELECT \"COL\" FROM (SELECT 1 AS \"COL\") AS \"CHECKTOP\" LIMIT 1", // + "SELECT \"SUBCOL\" AS \"COL\" FROM ( SELECT 1 AS \"SUBCOL\" ) \"SUBQUERY\" GROUP BY 1", // + "SELECT \"SUBCOL\" AS \"COL\" FROM ( SELECT 1 AS \"SUBCOL\" ) \"SUBQUERY\" GROUP BY 2", // + "INSERT INTO \"XTableau_C_Connect\" SELECT * FROM (SELECT 1 AS COL) AS CHECKTEMP LIMIT 1", // + "DROP TABLE \"XTableau_C_Connect\"", // + "INSERT INTO \"XTableau_B_Connect\" SELECT * FROM (SELECT 1 AS COL) AS CHECKTEMP LIMIT 1" }; + + private static SQLResponse temp = new SQLResponse(new LinkedList<SelectedColumnMeta>() { + private static final long serialVersionUID = -8086728462624901359L; + + { + add(new SelectedColumnMeta(false, false, true, false, 2, true, 11, "COL", "COL", "", "", "", 10, 0, 4, "int4", false, true, false)); + } + }, new LinkedList<List<String>>() { + private static final long serialVersionUID = -470083340592928073L; + + { + add(new LinkedList<String>() { + private static final long serialVersionUID = -3673192785838230054L; + + { + add("1"); + } + }); + } + }, 0, false, null); + + private static SQLResponse[] fakeResponses = new SQLResponse[] { temp, // + new SQLResponse(null, null, 0, false, null), // + new SQLResponse(null, null, 0, false, null), // + temp, // + new SQLResponse(null, null, 0, true, "near 1 syntax error"), // + temp, // + new SQLResponse(null, null, 0, true, "group by 1????"), // + new SQLResponse(null, null, 0, true, "group by 2????"), // + new SQLResponse(null, null, 0, true, "XTableau_C_Connect not exist"), // + new SQLResponse(null, null, 0, true, "XTableau_C_Connect not exist"), // + new SQLResponse(null, null, 0, true, "XTableau_B_Connect not exist"), }; + + private static ArrayList<HashSet<String>> tableauTestQueriesInToken = new ArrayList<HashSet<String>>(); + + static { + for (String q : tableauTestQueries) { + HashSet<String> temp = new HashSet<String>(); + for (String token : q.split("[\r\n\t \\(\\)]")) { + temp.add(token); + } + temp.add(""); + tableauTestQueriesInToken.add(temp); + } + } + + public static SQLResponse tableauIntercept(String sql) { + + String[] tokens = sql.split("[\r\n\t \\(\\)]"); + for (int i = 0; i < tableauTestQueries.length; ++i) { + if (isTokenWiseEqual(tokens, tableauTestQueriesInToken.get(i))) { + logger.info("Hit fake response " + i); + return fakeResponses[i]; + } + } + + return null; + } + + private static boolean isTokenWiseEqual(String[] tokens, HashSet<String> tokenSet) { + for (String token : tokens) { + if (!tokenSet.contains(token)) { + return false; + } + } + return true; + } + + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/6366215a/server-base/src/test/java/org/apache/kylin/rest/util/QueryUtilTest.java ---------------------------------------------------------------------- diff --git a/server-base/src/test/java/org/apache/kylin/rest/util/QueryUtilTest.java b/server-base/src/test/java/org/apache/kylin/rest/util/QueryUtilTest.java index 9305410..c00cd3f 100644 --- a/server-base/src/test/java/org/apache/kylin/rest/util/QueryUtilTest.java +++ b/server-base/src/test/java/org/apache/kylin/rest/util/QueryUtilTest.java @@ -18,13 +18,27 @@ package org.apache.kylin.rest.util; +import org.apache.kylin.common.util.LocalFileMetadataTestCase; import org.apache.kylin.rest.request.SQLRequest; +import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; -public class QueryUtilTest { +public class QueryUtilTest extends LocalFileMetadataTestCase { + + @Before + public void setUp() throws Exception { + this.createTestMetadata(); + } + + @After + public void after() throws Exception { + this.cleanupTestMetadata(); + } + @Test - public void testHealInterval() { + public void testMassageSql() { { SQLRequest sqlRequest = new SQLRequest(); sqlRequest.setSql("select ( date '2001-09-28' + interval floor(1.2) day) from test_kylin_fact"); @@ -37,15 +51,21 @@ public class QueryUtilTest { String s = QueryUtil.massageSql(sqlRequest); Assert.assertEquals("select ( date '2001-09-28' + interval '2' month) from test_kylin_fact group by ( date '2001-09-28' + interval '2' month)", s); } + { + SQLRequest sqlRequest = new SQLRequest(); + sqlRequest.setSql("select concat(\"TEST_KYLIN_FACT\".\"LSTG_FORMAT_NAME\",\"TEST_KYLIN_FACT\".\"LSTG_FORMAT_NAME\") concat(\"TEST_KYLIN_FACT\".\"LSTG_FORMAT_NAME\",\"TEST_KYLIN_FACT\".\"LSTG_FORMAT_NAME\") ()"); + String s = QueryUtil.massageSql(sqlRequest); + Assert.assertEquals("select {fn concat(\"TEST_KYLIN_FACT\".\"LSTG_FORMAT_NAME\",\"TEST_KYLIN_FACT\".\"LSTG_FORMAT_NAME\") } {fn concat(\"TEST_KYLIN_FACT\".\"LSTG_FORMAT_NAME\",\"TEST_KYLIN_FACT\".\"LSTG_FORMAT_NAME\") } ()", s); + } } @Test - public void testHealConcat() { + public void testKeywordDefaultDirtyHack() { { SQLRequest sqlRequest = new SQLRequest(); - sqlRequest.setSql("select concat(\"TEST_KYLIN_FACT\".\"LSTG_FORMAT_NAME\",\"TEST_KYLIN_FACT\".\"LSTG_FORMAT_NAME\") concat(\"TEST_KYLIN_FACT\".\"LSTG_FORMAT_NAME\",\"TEST_KYLIN_FACT\".\"LSTG_FORMAT_NAME\") ()"); + sqlRequest.setSql("select * from DEFAULT.TEST_KYLIN_FACT"); String s = QueryUtil.massageSql(sqlRequest); - Assert.assertEquals("select {fn concat(\"TEST_KYLIN_FACT\".\"LSTG_FORMAT_NAME\",\"TEST_KYLIN_FACT\".\"LSTG_FORMAT_NAME\") } {fn concat(\"TEST_KYLIN_FACT\".\"LSTG_FORMAT_NAME\",\"TEST_KYLIN_FACT\".\"LSTG_FORMAT_NAME\") } ()", s); + Assert.assertEquals("select * from \"DEFAULT\".TEST_KYLIN_FACT", s); } } }