Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ object ResolveTableValuedFunctions extends Rule[LogicalPlan] {

override def apply(plan: LogicalPlan): LogicalPlan = plan resolveOperators {
case u: UnresolvedTableValuedFunction if u.functionArgs.forall(_.resolved) =>
builtinFunctions.get(u.functionName) match {
builtinFunctions.get(u.functionName.toLowerCase) match {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the function name is case sensitive in Hive?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean? There are no table valued functions in Hive.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@samelamin you have to make this dependent on the case sensitivity setting of the analyzer. This means you will have to turn this object into case class that takes a SQLConf as its argument.

@samelamin samelamin Mar 31, 2017

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok cool thanks @hvanhovell ill amend it over the weekend. Do you know how I can run a specific set of tests on SBT?

im getting garbage collection errors when I try to run using sbt test-only form http://spark.apache.org/developer-tools.html

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example,

build/sbt "catalyst/test-only org.apache.spark.sql.catalyst.optimizer.ColumnPruningSuite"
build/sbt -Phive "hive/test-only org.apache.spark.sql.hive.execution.SQLQuerySuite"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hvanhovell instead of creating a new case class, is there a way I can reuse the UnresolvedTableValuedFunction case class and just add in the SQLConf class?

@hvanhovell hvanhovell Apr 2, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, @gatorsmile is right. We don't do case sensitive resolution for functions. So you can ignore my comment, and undo the last change you have made.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So would you like me to revert the change to the case class?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, sorry about that!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I can revert but how will we test it since the apply method isn't being called by the tests. They new up an unresolvedTableValuedFunction? Unless we hardcore it in unresolvedTableValuedFunction to always be case insensitive then I don't know how else to do it?

case Some(tvf) =>
val resolved = tvf.flatMap { case (argList, resolver) =>
argList.implicitCast(u.functionArgs) match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import org.apache.spark.sql.catalyst.expressions.codegen.{CodegenContext, Codege
import org.apache.spark.sql.catalyst.plans.logical.{LeafNode, LogicalPlan}
import org.apache.spark.sql.catalyst.trees.TreeNode
import org.apache.spark.sql.catalyst.util.quoteIdentifier
import org.apache.spark.sql.internal.SQLConf
import org.apache.spark.sql.types.{DataType, Metadata, StructType}

/**
Expand Down Expand Up @@ -69,9 +70,15 @@ case class UnresolvedInlineTable(
* select * from range(10);
* }}}
*/
case class UnresolvedTableValuedFunction(functionName: String, functionArgs: Seq[Expression])
case class UnresolvedTableValuedFunction(conf: SQLConf,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@samelamin this is a moot point now, but try to avoid mutable state as much as you can. In this case it would have been better to pass the SQLConf class as a parameter to the ResolveTableValuedFunction, and deal with case-sensitivity there.

var functionName: String,
functionArgs: Seq[Expression])
extends LeafNode {

if (!conf.caseSensitiveAnalysis) {
functionName = functionName.toLowerCase
}

override def output: Seq[Attribute] = Nil

override lazy val resolved = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import org.antlr.v4.runtime.tree.{ParseTree, RuleNode, TerminalNode}
import org.apache.spark.internal.Logging
import org.apache.spark.sql.AnalysisException
import org.apache.spark.sql.catalyst.{FunctionIdentifier, TableIdentifier}
import org.apache.spark.sql.catalyst.SimpleCatalystConf
import org.apache.spark.sql.catalyst.analysis._
import org.apache.spark.sql.catalyst.expressions._
import org.apache.spark.sql.catalyst.parser.SqlBaseParser._
Expand All @@ -44,6 +45,7 @@ import org.apache.spark.util.random.RandomSampler
*/
class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with Logging {
import ParserUtils._
val sqlConf = SimpleCatalystConf(caseSensitiveAnalysis = false)

protected def typedVisit[T](ctx: ParseTree): T = {
ctx.accept(this).asInstanceOf[T]
Expand Down Expand Up @@ -674,12 +676,15 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with Logging {

/**
* Create a table-valued function call with arguments, e.g. range(1000)
*/
*/
override def visitTableValuedFunction(ctx: TableValuedFunctionContext)
: LogicalPlan = withOrigin(ctx) {
UnresolvedTableValuedFunction(ctx.identifier.getText, ctx.expression.asScala.map(expression))
UnresolvedTableValuedFunction(sqlConf,
ctx.identifier.getText,
ctx.expression.asScala.map(expression))
}


/**
* Create an inline table (a virtual table in Hive parlance).
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@

package org.apache.spark.sql.catalyst.parser

import org.apache.spark.sql.catalyst.FunctionIdentifier
import org.apache.spark.sql.catalyst.analysis.{UnresolvedGenerator, UnresolvedInlineTable, UnresolvedTableValuedFunction}
import org.apache.spark.sql.catalyst.expressions._
import org.apache.spark.sql.catalyst.plans._
import org.apache.spark.sql.catalyst.plans.logical._
import org.apache.spark.sql.catalyst.FunctionIdentifier
import org.apache.spark.sql.catalyst.SimpleCatalystConf
import org.apache.spark.sql.types.IntegerType

/**
Expand All @@ -33,6 +34,8 @@ class PlanParserSuite extends PlanTest {
import CatalystSqlParser._
import org.apache.spark.sql.catalyst.dsl.expressions._
import org.apache.spark.sql.catalyst.dsl.plans._
override val conf = SimpleCatalystConf(caseSensitiveAnalysis = false)


private def assertEqual(sqlCommand: String, plan: LogicalPlan): Unit = {
comparePlans(parsePlan(sqlCommand), plan)
Expand All @@ -52,6 +55,14 @@ class PlanParserSuite extends PlanTest {
assertEqual("SELECT * FROM a", plan)
}


test("case insensitive range queries") {
val plan = UnresolvedTableValuedFunction(conf, "range", Literal(2) :: Nil).select(star())
assertEqual("select * from rAngE(2)", plan)
assertEqual("select * from RaNgE(2)", plan)
assertEqual("select * from RANGE(2)", plan)
}

test("explain") {
intercept("EXPLAIN logical SELECT 1", "Unsupported SQL statement")
intercept("EXPLAIN formatted SELECT 1", "Unsupported SQL statement")
Expand Down Expand Up @@ -468,9 +479,10 @@ class PlanParserSuite extends PlanTest {
test("table valued function") {
assertEqual(
"select * from range(2)",
UnresolvedTableValuedFunction("range", Literal(2) :: Nil).select(star()))
UnresolvedTableValuedFunction(conf, "range", Literal(2) :: Nil).select(star()))
}


test("inline table") {
assertEqual("values 1, 2, 3, 4",
UnresolvedInlineTable(Seq("col1"), Seq(1, 2, 3, 4).map(x => Seq(Literal(x)))))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ struct<plan:string>
-- !query 8 output
== Parsed Logical Plan ==
'Project [unresolvedalias('ifnull('id, x), None), unresolvedalias('nullif('id, x), None), unresolvedalias('nvl('id, x), None), unresolvedalias('nvl2('id, x, y), None)]
+- 'UnresolvedTableValuedFunction range, [2]
+- 'UnresolvedTableValuedFunction SimpleCatalystConf(false,true,true,100,10,20,1000,true,false,false,false,12,false,/user/hive/warehouse,America/Los_Angeles,100,true), range, [2]

== Analyzed Logical Plan ==
ifnull(`id`, 'x'): string, nullif(`id`, 'x'): bigint, nvl(`id`, 'x'): string, nvl2(`id`, 'x', 'y'): string
Expand Down