This is an automated email from the ASF dual-hosted git repository.
akashrn5 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/carbondata.git
The following commit(s) were added to refs/heads/master by this push:
new cf47039 [CARBONDATA-3636]Timeseries query is not hitting datamap if
granularity in query is given case insensitive
cf47039 is described below
commit cf4703942d95c2c30b6ab370dd5976fc0aaabb86
Author: Indhumathi27 <[email protected]>
AuthorDate: Fri Dec 27 18:10:06 2019 +0530
[CARBONDATA-3636]Timeseries query is not hitting datamap if granularity in
query is given case insensitive
Problem:
TimeSeriesUDF function has two parameters:
1.Column -> AttributeReference
2.Granularity -> Literal
For timeseries function having granularity with different case letters in
Create datamap and actual query,
select query is not hitting the datamap. This is because, since we store
granularity as Literal,
semanticEquals returns false, if granularity is of different case.
Solution:
For TimeSeriesFunction, check Literal value case insensitive
This closes #3541
---
.../carbondata/mv/datamap/MVAnalyzerRule.scala | 2 +-
.../carbondata/mv/rewrite/DefaultMatchMaker.scala | 94 ++++++++++++++++------
.../org/apache/carbondata/mv/rewrite/Utils.scala | 92 ++++++++++++++++++++-
.../TestMVTimeSeriesCreateDataMapCommand.scala | 11 +++
.../timeseries/TestMVTimeSeriesLoadAndQuery.scala | 6 +-
5 files changed, 173 insertions(+), 32 deletions(-)
diff --git
a/datamap/mv/core/src/main/scala/org/apache/carbondata/mv/datamap/MVAnalyzerRule.scala
b/datamap/mv/core/src/main/scala/org/apache/carbondata/mv/datamap/MVAnalyzerRule.scala
index cf76e48..30f795d 100644
---
a/datamap/mv/core/src/main/scala/org/apache/carbondata/mv/datamap/MVAnalyzerRule.scala
+++
b/datamap/mv/core/src/main/scala/org/apache/carbondata/mv/datamap/MVAnalyzerRule.scala
@@ -149,7 +149,7 @@ class MVAnalyzerRule(sparkSession: SparkSession) extends
Rule[LogicalPlan] {
if (!outPutUDFColumn.equalsIgnoreCase("") &&
compactSQL.contains("WHERE")) {
queryArray = compactSQL.split("\n")
queryArray(queryArray.indexOf("WHERE") + 1) = queryArray(
- queryArray.indexOf("WHERE") + 1).replace(outPutUDFColumn,
+ queryArray.indexOf("WHERE") +
1).toLowerCase.replace(outPutUDFColumn.toLowerCase,
s"gen_subsumer_0.`$outPutUDFColumn`")
reWrittenQuery = queryArray.mkString("\n")
}
diff --git
a/datamap/mv/core/src/main/scala/org/apache/carbondata/mv/rewrite/DefaultMatchMaker.scala
b/datamap/mv/core/src/main/scala/org/apache/carbondata/mv/rewrite/DefaultMatchMaker.scala
index 6fbc87f..06dd7a1 100644
---
a/datamap/mv/core/src/main/scala/org/apache/carbondata/mv/rewrite/DefaultMatchMaker.scala
+++
b/datamap/mv/core/src/main/scala/org/apache/carbondata/mv/rewrite/DefaultMatchMaker.scala
@@ -23,6 +23,7 @@ import org.apache.spark.sql.catalyst.expressions.{Alias,
Attribute, AttributeMap
import org.apache.spark.sql.catalyst.expressions.aggregate.AggregateExpression
import org.apache.spark.sql.catalyst.expressions.codegen.{CodegenContext,
ExprCode}
import org.apache.spark.sql.catalyst.plans.{FullOuter, Inner, LeftOuter}
+import org.apache.spark.sql.execution.command.timeseries.TimeSeriesFunction
import org.apache.spark.sql.types.{DataType, Metadata}
import org.apache.carbondata.mv.datamap.MVHelper
@@ -59,7 +60,12 @@ abstract class DefaultMatchPattern extends
MatchPattern[ModularPlan] {
subsumer.outputList.collect {
case a: Alias if a.child.isInstanceOf[Expression] &&
!a.child.isInstanceOf[AggregateExpression] =>
- a.child -> a.toAttribute
+ a.child match {
+ case s: ScalaUDF if s.function.isInstanceOf[TimeSeriesFunction] =>
+ Utils.getTransformedTimeSeriesUDF(s) -> a.toAttribute
+ case _ =>
+ a.child -> a.toAttribute
+ }
}.toMap
// Check and replace all alias references with subsumer alias map
references.
@@ -76,8 +82,18 @@ abstract class DefaultMatchPattern extends
MatchPattern[ModularPlan] {
qualifier = a.qualifier)
}.getOrElse(a)
case a: Expression =>
- aliasMapExp
+ var attribute = aliasMapExp
.get(a)
+ // attribute will be empty, if attribute name is of different
case. If empty, change
+ // case of scalaUDF present in expression and get updated
expression from aliasMap
+ if (attribute.isEmpty) {
+ val newExp = a transform {
+ case s: ScalaUDF if
s.function.isInstanceOf[TimeSeriesFunction] =>
+ Utils.getTransformedTimeSeriesUDF(s)
+ }
+ attribute = aliasMapExp.get(newExp)
+ }
+ attribute
.map { ref =>
AttributeReference(
ref.name, ref.dataType)(
@@ -137,17 +153,20 @@ object SelectSelectNoChildDelta extends
DefaultMatchPattern with PredicateHelper
compensation: Option[ModularPlan]): Boolean = {
if (subsumee.asInstanceOf[Select].predicateList.contains(exprE)) {
subsumer.asInstanceOf[Select].predicateList.exists(_.semanticEquals(exprE)) ||
- canEvaluate(exprE, subsumer)
+ canEvaluate(exprE, subsumer) ||
+ Utils.isExpressionMatchesUDF(exprE,
subsumer.asInstanceOf[Select].predicateList)
} else if (subsumee.asInstanceOf[Select].outputList.contains(exprE)) {
exprE match {
case a@Alias(_, _) =>
exprListR.exists(a1 => a1.isInstanceOf[Alias] &&
a1.asInstanceOf[Alias].child.semanticEquals(a.child)) ||
- exprListR.exists(_.semanticEquals(exprE) || canEvaluate(exprE,
subsumer))
+ exprListR.exists(_.semanticEquals(exprE) || canEvaluate(exprE,
subsumer)) ||
+ Utils.isExpressionMatchesUDF(exprE, exprListR)
case exp =>
exprListR.exists(a1 => a1.isInstanceOf[Alias] &&
a1.asInstanceOf[Alias].child.semanticEquals(exp)) ||
- exprListR.exists(_.semanticEquals(exprE) || canEvaluate(exprE,
subsumer))
+ exprListR.exists(_.semanticEquals(exprE) || canEvaluate(exprE,
subsumer)) ||
+ Utils.isExpressionMatchesUDF(exprE, exprListR)
}
} else {
false
@@ -199,7 +218,8 @@ object SelectSelectNoChildDelta extends DefaultMatchPattern
with PredicateHelper
val rejoinOutputList = rejoin.flatMap(_.output)
val isPredicateRmE = sel_1a.predicateList.forall(expr =>
- sel_1q.predicateList.exists(_.semanticEquals(expr)))
+ sel_1q.predicateList.exists(_.semanticEquals(expr)) ||
+ Utils.isExpressionMatchesUDF(expr, sel_1q.predicateList))
val isPredicateEmdR = sel_1q.predicateList.forall(expr =>
isDerivable(expr, sel_1a.outputList ++ rejoinOutputList, sel_1q,
sel_1a, None))
// Check if sel_1q.outputList is non empty and then check whether
@@ -244,19 +264,24 @@ object SelectSelectNoChildDelta extends
DefaultMatchPattern with PredicateHelper
val sel_1q_join = sel_1q.extractJoinConditions(
sel_1q.children(mappedEdge.left),
sel_1q.children(mappedEdge.right))
- sel_1a_join.forall(e =>
sel_1q_join.exists(e.semanticEquals(_))) &&
- sel_1q_join.forall(e =>
sel_1a_join.exists(e.semanticEquals(_)))
+ sel_1a_join.forall(e =>
sel_1q_join.exists(e.semanticEquals(_)) ||
+ Utils.isExpressionMatchesUDF(e, sel_1q_join)) &&
+ sel_1q_join.forall(e =>
sel_1a_join.exists(e.semanticEquals(_)) ||
+ Utils.isExpressionMatchesUDF(e, sel_1a_join))
} else false
case _ => false
}
}
val isPredicateEmR = sel_1q.predicateList.forall(expr =>
- sel_1a.predicateList.exists(_.semanticEquals(expr)))
+ sel_1a.predicateList.exists(_.semanticEquals(expr)) ||
+ Utils.isExpressionMatchesUDF(expr, sel_1a.predicateList))
val isOutputEmR = sel_1q.outputList.forall(expr =>
- sel_1a.outputList.exists(_.semanticEquals(expr)))
+ sel_1a.outputList.exists(_.semanticEquals(expr)) ||
+ Utils.isExpressionMatchesUDF(expr, sel_1a.outputList))
val isOutputRmE = sel_1a.outputList.forall(expr =>
- sel_1q.outputList.exists(_.semanticEquals(expr)))
+ sel_1q.outputList.exists(_.semanticEquals(expr)) ||
+ Utils.isExpressionMatchesUDF(expr, sel_1q.outputList))
val isLOEmLOR = !(isLeftJoinView(sel_1a) &&
sel_1q.joinEdges.head.joinType == Inner)
if (r2eJoinsMatch) {
@@ -329,10 +354,13 @@ object SelectSelectNoChildDelta extends
DefaultMatchPattern with PredicateHelper
sel_3q.children.forall(_.isInstanceOf[GroupBy]) =>
val isPredicateRmE = sel_3a.predicateList.isEmpty ||
sel_3a.predicateList.forall(expr =>
-
sel_3q.predicateList.exists(_.semanticEquals(expr)))
+
sel_3q.predicateList.exists(_.semanticEquals(expr)) ||
+ Utils.isExpressionMatchesUDF(expr,
sel_3q.predicateList))
val isPredicateEmdR = sel_3q.predicateList.isEmpty ||
sel_3q.predicateList.forall(expr =>
-
sel_3a.predicateList.exists(_.semanticEquals(expr)) ||
+
sel_3a.predicateList.exists(_.semanticEquals(expr) ||
+
Utils.isExpressionMatchesUDF(expr,
+
sel_3a.predicateList)) ||
isDerivable(expr, sel_3a.outputList, sel_3q,
sel_3a, None))
val isOutputEdR = sel_3q.outputList.forall(expr =>
isDerivable(expr, sel_3a.outputList, sel_3q, sel_3a, None))
@@ -341,7 +369,8 @@ object SelectSelectNoChildDelta extends DefaultMatchPattern
with PredicateHelper
if (isPredicateRmE && isPredicateEmdR && isOutputEdR && isSingleChild)
{
val isPredicateEmR = sel_3q.predicateList.isEmpty ||
sel_3q.predicateList.forall(expr =>
-
sel_3a.predicateList.exists(_.semanticEquals(expr)))
+
sel_3a.predicateList.exists(_.semanticEquals(expr)) ||
+ Utils.isExpressionMatchesUDF(expr,
sel_3a.predicateList))
val isOutputRmE = sel_3a.outputList.forall(expr =>
isDerivable(expr, sel_3q.outputList, sel_3a, sel_3q, None))
val isOutputEmR = sel_3q.outputList.forall(expr =>
@@ -355,7 +384,8 @@ object SelectSelectNoChildDelta extends DefaultMatchPattern
with PredicateHelper
case a: Alias => sel_3a.outputList
.find { a1 =>
a1.isInstanceOf[Alias] &&
- a1.asInstanceOf[Alias].child.semanticEquals(a.child)
+ (a1.asInstanceOf[Alias].child.semanticEquals(a.child) ||
+ Utils.isExpressionMatchesUDF(a1.asInstanceOf[Alias].child,
a.child))
}.map(_.toAttribute).get
})
val wip = sel_3q_exp.copy(
@@ -386,9 +416,11 @@ object GroupbyGroupbyNoChildDelta extends
DefaultMatchPattern {
gb_2q @ modular.GroupBy(_, _, _, _, _, _, _, _),
None) =>
val isGroupingEmR = gb_2q.predicateList.forall(expr =>
- gb_2a.predicateList.exists(_.semanticEquals(expr)))
+ gb_2a.predicateList.exists(_.semanticEquals(expr)) ||
+ Utils.isExpressionMatchesUDF(expr, gb_2a.predicateList))
val isGroupingRmE = gb_2a.predicateList.forall(expr =>
- gb_2q.predicateList.exists(_.semanticEquals(expr)))
+ gb_2q.predicateList.exists(_.semanticEquals(expr)) ||
+ Utils.isExpressionMatchesUDF(expr, gb_2q.predicateList))
val isOutputEmR = gb_2q.outputList.forall {
case a @ Alias(_, _) =>
gb_2a.outputList.exists{
@@ -403,7 +435,8 @@ object GroupbyGroupbyNoChildDelta extends
DefaultMatchPattern {
val mappings = gb_2a.outputList.zipWithIndex.map { case(exp,
index) =>
(exp, gb_2q.outputList.find {
case a: Alias if exp.isInstanceOf[Alias] =>
- a.child.semanticEquals(exp.children.head)
+ a.child.semanticEquals(exp.children.head) ||
+ Utils.isExpressionMatchesUDF(a.child, exp.children.head)
case a: Alias => a.child.semanticEquals(exp)
case other => other.semanticEquals(exp)
}.getOrElse(gb_2a.outputList(index)))
@@ -476,7 +509,7 @@ object GroupbyGroupbySelectOnlyChildDelta extends
DefaultMatchPattern with Predi
compensation: Option[ModularPlan]) = {
if (subsumee.asInstanceOf[GroupBy].predicateList.contains(exprE)) {
if (exprListR.exists(_.semanticEquals(exprE)) || canEvaluate(exprE,
exprListR) ||
- isDerivableForUDF(exprE, exprListR)) {
+ isDerivableForUDF(exprE, exprListR) ||
Utils.isExpressionMatchesUDF(exprE, exprListR)) {
true
} else {
false
@@ -484,7 +517,7 @@ object GroupbyGroupbySelectOnlyChildDelta extends
DefaultMatchPattern with Predi
} else if (compensation.getOrElse(throw new RuntimeException("compensation
cannot be None"))
.asInstanceOf[Select].predicateList.contains(exprE)) {
if (canEvaluate(exprE, exprListR) ||
exprListR.exists(_.semanticEquals(exprE)) ||
- isDerivableForUDF(exprE, exprListR)) {
+ isDerivableForUDF(exprE, exprListR) ||
Utils.isExpressionMatchesUDF(exprE, exprListR)) {
true
} else {
false
@@ -546,7 +579,9 @@ object GroupbyGroupbySelectOnlyChildDelta extends
DefaultMatchPattern with Predi
val rejoinOutputList = sel_1c1.children.tail.flatMap(_.output)
val isGroupingEdR = gb_2q.predicateList.forall(expr =>
isDerivable(expr, gb_2a.predicateList ++ rejoinOutputList, gb_2q,
gb_2a, compensation))
- val needRegrouping =
!gb_2a.predicateList.forall(gb_2q.predicateList.contains)
+ val needRegrouping = !gb_2a.predicateList
+ .forall(f => gb_2q.predicateList.contains(f) ||
+ Utils.isExpressionMatchesUDF(f, gb_2q.predicateList))
val canPullup = sel_1c1.predicateList.forall(expr =>
isDerivable(expr, gb_2a.predicateList ++ rejoinOutputList, gb_2q,
gb_2a, compensation))
val isAggEmR = gb_2q.outputList.collect {
@@ -558,9 +593,12 @@ object GroupbyGroupbySelectOnlyChildDelta extends
DefaultMatchPattern with Predi
// pull up
val pullupOutputList = gb_2a.outputList.map(_.toAttribute) ++
rejoinOutputList
val myOutputList = gb_2a.outputList.filter {
- case alias: Alias => gb_2q.outputList.filter(_.isInstanceOf[Alias])
- .exists(_.asInstanceOf[Alias].child.semanticEquals(alias.child))
- case attr: Attribute =>
gb_2q.outputList.exists(_.semanticEquals(attr))
+ case alias: Alias =>
+ val aliasList = gb_2q.outputList.filter(_.isInstanceOf[Alias])
+
aliasList.exists(_.asInstanceOf[Alias].child.semanticEquals(alias.child)) ||
+ Utils.isExpressionMatchesUDF(alias.child, aliasList)
+ case attr: Attribute =>
+ gb_2q.outputList.exists(_.semanticEquals(attr))
}.map(_.toAttribute) ++ rejoinOutputList
// TODO: find out if we really need to check needRegrouping or just
use myOutputList
val sel_2c1 = if (needRegrouping) {
@@ -707,10 +745,13 @@ object SelectSelectGroupbyChildDelta extends
DefaultMatchPattern with PredicateH
val isPredicateRmE = sel_3a.predicateList.forall(expr =>
sel_3q.predicateList.exists(_.semanticEquals(expr)) ||
- gb_2c.predicateList.exists(_.semanticEquals(expr)))
+ Utils.isExpressionMatchesUDF(expr, sel_3q.predicateList) ||
+ gb_2c.predicateList.exists(_.semanticEquals(expr)) ||
+ Utils.isExpressionMatchesUDF(expr, gb_2c.predicateList))
val isPredicateEmdR = sel_3q.predicateList
.forall(expr =>
sel_3a.predicateList.exists(_.semanticEquals(expr)) ||
+ Utils.isExpressionMatchesUDF(expr, sel_3a.predicateList) ||
isDerivable(
expr,
sel_3a.outputList ++ rejoinOutputList,
@@ -769,7 +810,8 @@ object SelectSelectGroupbyChildDelta extends
DefaultMatchPattern with PredicateH
val mappings = sel_3q_exp.outputList.zipWithIndex.map {
case(exp, index) =>
(exp, gb_2c.outputList.find {
case a: Alias if exp.isInstanceOf[Alias] =>
- a.child.semanticEquals(exp.children.head)
+ a.child.semanticEquals(exp.children.head) ||
+ Utils.isExpressionMatchesUDF(a.child, exp.children.head)
case a: Alias => a.child.semanticEquals(exp)
case other => other.semanticEquals(exp)
}.getOrElse(gb_2c.outputList(index)))
diff --git
a/datamap/mv/core/src/main/scala/org/apache/carbondata/mv/rewrite/Utils.scala
b/datamap/mv/core/src/main/scala/org/apache/carbondata/mv/rewrite/Utils.scala
index 802be83..2733a5a 100644
---
a/datamap/mv/core/src/main/scala/org/apache/carbondata/mv/rewrite/Utils.scala
+++
b/datamap/mv/core/src/main/scala/org/apache/carbondata/mv/rewrite/Utils.scala
@@ -17,8 +17,9 @@
package org.apache.carbondata.mv.rewrite
-import org.apache.spark.sql.catalyst.expressions.{Alias, Attribute,
AttributeMap, Cast, Divide, Expression, Multiply, PredicateHelper, ScalaUDF}
+import org.apache.spark.sql.catalyst.expressions.{Alias, Attribute,
AttributeMap, Cast, Divide, Expression, Literal, Multiply, PredicateHelper,
ScalaUDF}
import org.apache.spark.sql.catalyst.expressions.aggregate._
+import org.apache.spark.sql.execution.command.timeseries.TimeSeriesFunction
import org.apache.carbondata.mv.plans.modular
import org.apache.carbondata.mv.plans.modular.{ModularPlan, Select}
@@ -321,7 +322,8 @@ object Utils extends PredicateHelper {
case expr: Expression if !expr.isInstanceOf[AggregateFunction] =>
operator_a.outputList.find {
case alias: Alias if alias_m.contains(alias.toAttribute) &&
-
alias_m(alias.toAttribute).child.semanticEquals(expr) &&
+
(alias_m(alias.toAttribute).child.semanticEquals(expr) ||
+
Utils.isExpressionMatchesUDF(alias_m(alias.toAttribute), expr)) &&
!alias_m(alias.toAttribute).child
.isInstanceOf[AggregateExpression] => true
case attr: Attribute if alias_m.contains(attr) &&
@@ -459,14 +461,14 @@ object Utils extends PredicateHelper {
subsumer.asInstanceOf[Select].outputList.forall {
case Alias(s: ScalaUDF, _) =>
expE.children.foreach { expr =>
- if (s.semanticEquals(expr)) {
+ if (s.semanticEquals(expr)|| isExpressionMatchesUDF(s, expr)) {
canBeDerived = true
}
// It is because when expression is like between filter, the expr
will be as Cast
// expression and its child will be ScalaUDF(timeseries), So compare
the child also.
if (!canBeDerived && null != expr.children) {
expr.children.foreach { expC =>
- if (s.semanticEquals(expC)) {
+ if (s.semanticEquals(expC) || isExpressionMatchesUDF(s, expC)) {
canBeDerived = true
}
}
@@ -519,4 +521,86 @@ object Utils extends PredicateHelper {
}
}
+ /**
+ * Check's if timeseries udf function exists. If exists, compare literal
with case insensitive
+ * value
+ */
+ def isExpressionMatchesUDF(subsumeExp: Expression, subsumerExprList:
Seq[Expression]): Boolean = {
+ // Check if expression has a ScalaUDF of timeSeries function and verify
it's children
+ // irrespective of case. The structure of scalaUDF function will look like,
+ // ScalaUDF
+ // |
+ // TimeSeriesFunction
+ // / \
+ // / \
+ // AttributeReference Literal
+ subsumeExp match {
+ case Alias(udf: ScalaUDF, _) if
udf.function.isInstanceOf[TimeSeriesFunction] =>
+ val children = udf.children
+ val subsumerTimeSeriesExp = subsumerExprList.filter(a1 =>
+ a1.isInstanceOf[Alias] &&
a1.asInstanceOf[Alias].child.isInstanceOf[ScalaUDF] &&
+ a1.asInstanceOf[Alias].child.asInstanceOf[ScalaUDF].function.
+ isInstanceOf[TimeSeriesFunction])
+ subsumerTimeSeriesExp.exists(f => {
+ val childExprsOfTimeSeriesUDF = f.asInstanceOf[Alias].child
+ .asInstanceOf[ScalaUDF].children
+ childExprsOfTimeSeriesUDF.head.semanticEquals(children.head) &&
+
childExprsOfTimeSeriesUDF.last.asInstanceOf[Literal].toString().equalsIgnoreCase(
+ children.last.asInstanceOf[Literal].toString())
+ })
+ case udf: ScalaUDF if udf.function.isInstanceOf[TimeSeriesFunction] =>
+ val children = udf.children
+ var subsumerTimeSeriesExprList: Seq[Expression] = Seq.empty
+ subsumerExprList foreach {
+ case s: ScalaUDF if s.function.isInstanceOf[TimeSeriesFunction] =>
+ subsumerTimeSeriesExprList = subsumerTimeSeriesExprList.+:(s)
+ case Alias(s: ScalaUDF, _) if
s.function.isInstanceOf[TimeSeriesFunction] =>
+ subsumerTimeSeriesExprList =
subsumerTimeSeriesExprList.+:(s.asInstanceOf[Expression])
+ case _ =>
+ }
+ subsumerTimeSeriesExprList.exists(f => {
+ val childExprsOfTimeSeriesUDF = f.asInstanceOf[ScalaUDF].children
+ childExprsOfTimeSeriesUDF.head.semanticEquals(children.head) &&
+
childExprsOfTimeSeriesUDF.last.asInstanceOf[Literal].toString().equalsIgnoreCase(
+ children.last.asInstanceOf[Literal].toString())
+ })
+ case exp: Expression =>
+ val transformedExpwithLowerCase = exp.transform {
+ case s: ScalaUDF if s.function.isInstanceOf[TimeSeriesFunction] =>
+ getTransformedTimeSeriesUDF(s)
+ case other => other
+ }
+ val transformedExprListWithLowerCase = subsumerExprList map { expr =>
+ expr.transform {
+ case s: ScalaUDF if s.function.isInstanceOf[TimeSeriesFunction] =>
+ getTransformedTimeSeriesUDF(s)
+ case other => other
+ }
+ }
+
transformedExprListWithLowerCase.exists(_.semanticEquals(transformedExpwithLowerCase))
+ case _ => false
+ }
+ }
+
+ def getTransformedTimeSeriesUDF(s: ScalaUDF): Expression = {
+ s.transform {
+ case l: Literal =>
+ Literal(l.toString().toLowerCase, l.dataType)
+ }
+ }
+
+ /**
+ * Check if expr1 and expr2 matches TimeSeriesUDF function. If both
expressions are
+ * timeseries udf functions, then check it's childrens are same irrespective
of case.
+ */
+ def isExpressionMatchesUDF(expr1: Expression, expr2: Expression): Boolean = {
+ (expr1, expr2) match {
+ case (s1: ScalaUDF, s2: ScalaUDF) if
s1.function.isInstanceOf[TimeSeriesFunction] &&
+
s2.function.isInstanceOf[TimeSeriesFunction] =>
+ s1.children.head.semanticEquals(s2.children.head) &&
+ s1.children.last.asInstanceOf[Literal].toString()
+ .equalsIgnoreCase(s2.children.last.asInstanceOf[Literal].toString())
+ case _ => false
+ }
+ }
}
diff --git
a/datamap/mv/core/src/test/scala/org/apache/carbondata/mv/timeseries/TestMVTimeSeriesCreateDataMapCommand.scala
b/datamap/mv/core/src/test/scala/org/apache/carbondata/mv/timeseries/TestMVTimeSeriesCreateDataMapCommand.scala
index 375eb64..160332f 100644
---
a/datamap/mv/core/src/test/scala/org/apache/carbondata/mv/timeseries/TestMVTimeSeriesCreateDataMapCommand.scala
+++
b/datamap/mv/core/src/test/scala/org/apache/carbondata/mv/timeseries/TestMVTimeSeriesCreateDataMapCommand.scala
@@ -210,6 +210,17 @@ class TestMVTimeSeriesCreateDataMapCommand extends
QueryTest with BeforeAndAfter
}.getMessage.contains("MV Timeseries is only supported on Timestamp/Date
column")
}
+ test("test timeseries with case sensitive granularity") {
+ sql("drop datamap if exists datamap1")
+ sql("create datamap datamap1 on table maintable using 'mv'" +
+ " as select timeseries(projectjoindate,'Second'), sum(projectcode) from
maintable group by timeseries(projectjoindate,'Second')")
+ val df1 = sql("select timeseries(projectjoindate,'SECOND'),
sum(projectcode) from maintable group by timeseries(projectjoindate,'SECOND')")
+ val df2 = sql("select timeseries(projectjoinDATE,'SECOnd'),
sum(projectcode) from maintable where projectcode=8 group by
timeseries(projectjoinDATE,'SECOnd')")
+ TestUtil.verifyMVDataMap(df1.queryExecution.optimizedPlan, "datamap1")
+ TestUtil.verifyMVDataMap(df2.queryExecution.optimizedPlan, "datamap1")
+ sql("drop datamap if exists datamap1")
+ }
+
class QueryTask(query: String) extends Callable[String] {
override def call(): String = {
var result = "PASS"
diff --git
a/datamap/mv/core/src/test/scala/org/apache/carbondata/mv/timeseries/TestMVTimeSeriesLoadAndQuery.scala
b/datamap/mv/core/src/test/scala/org/apache/carbondata/mv/timeseries/TestMVTimeSeriesLoadAndQuery.scala
index 3916ea3..fdc3190 100644
---
a/datamap/mv/core/src/test/scala/org/apache/carbondata/mv/timeseries/TestMVTimeSeriesLoadAndQuery.scala
+++
b/datamap/mv/core/src/test/scala/org/apache/carbondata/mv/timeseries/TestMVTimeSeriesLoadAndQuery.scala
@@ -48,6 +48,8 @@ class TestMVTimeSeriesLoadAndQuery extends QueryTest with
BeforeAndAfterAll {
val df1 = sql("select
timeseries(projectjoindate,'minute'),sum(projectcode) from maintable where
timeseries(projectjoindate,'minute') = '2016-02-23 09:17:00'" +
"group by timeseries(projectjoindate,'minute')")
assert(TestUtil.verifyMVDataMap(df1.queryExecution.optimizedPlan,
"datamap1"))
+ val df2 = sql("select timeseries(projectjoindate,'MINUTE'),
sum(projectcode) from maintable where timeseries(projectjoindate,'MINute') =
'2016-02-23 09:17:00' group by timeseries(projectjoindate,'MINUTE')")
+ TestUtil.verifyMVDataMap(df2.queryExecution.optimizedPlan, "datamap1")
dropDataMap("datamap1")
}
@@ -181,8 +183,10 @@ class TestMVTimeSeriesLoadAndQuery extends QueryTest with
BeforeAndAfterAll {
"create datamap datamap1 on table maintable using 'mv' as " +
"select timeseries(projectjoindate,'month'), max(salary) from maintable
where timeseries(projectjoindate,'month') = '2016-03-01 00:00:00' or
timeseries(projectjoindate,'month') = '2016-02-01 00:00:00' group by
timeseries(projectjoindate,'month')")
loadData("maintable")
- val df1 = sql("select timeseries(projectjoindate,'month'), max(salary)
from maintable where timeseries(projectjoindate,'month') = '2016-03-01
00:00:00' or timeseries(projectjoindate,'month') = '2016-02-01 00:00:00' group
by timeseries(projectjoindate,'month')")
+ var df1 = sql("select timeseries(projectjoindate,'month'), max(salary)
from maintable where timeseries(projectjoindate,'month') = '2016-03-01
00:00:00' or timeseries(projectjoindate,'month') = '2016-02-01 00:00:00' group
by timeseries(projectjoindate,'month')")
checkPlan("datamap1", df1)
+ df1 = sql("select timeseries(projectjoindate,'MONth'), max(salary) from
maintable where timeseries(projectjoindate,'MoNtH') = '2016-03-01 00:00:00' or
timeseries(projectjoinDATE,'MONth') = '2016-02-01 00:00:00' group by
timeseries(projectjoindate,'MONth')")
+ TestUtil.verifyMVDataMap(df1.queryExecution.optimizedPlan, "datamap1")
sql(
"create datamap datamap2 on table maintable using 'mv' as " +
"select timeseries(projectjoindate,'month'), max(salary) from maintable
where timeseries(projectjoindate,'month') = '2016-03-01 00:00:00' and
timeseries(projectjoindate,'month') = '2016-02-01 00:00:00' group by
timeseries(projectjoindate,'month')")