Skip to content

Commit e7bca31

Browse files
author
Jacky Li
committed
make caseSensitive configuration in Analyzer and Catalog
2 parents a764960 + 91b1b96 commit e7bca31

File tree

14 files changed

+243
-29
lines changed

14 files changed

+243
-29
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.spark.sql.catalyst
19+
20+
import scala.collection.immutable
21+
22+
private[spark] object CatalystConf{
23+
val CASE_SENSITIVE = "spark.sql.caseSensitive"
24+
}
25+
26+
private[spark] trait CatalystConf {
27+
def setConf(key: String, value: String) : Unit
28+
def getConf(key: String) : String
29+
def getConf(key: String, defaultValue: String) : String
30+
def getAllConfs: immutable.Map[String, String]
31+
}
32+
33+
/**
34+
* A trivial conf that is empty. Used for testing when all
35+
* relations are already filled in and the analyser needs only to resolve attribute references.
36+
*/
37+
object EmptyConf extends CatalystConf {
38+
def setConf(key: String, value: String) : Unit = {
39+
throw new UnsupportedOperationException
40+
}
41+
42+
def getConf(key: String) : String = {
43+
throw new UnsupportedOperationException
44+
}
45+
46+
def getConf(key: String, defaultValue: String) : String = {
47+
throw new UnsupportedOperationException
48+
}
49+
50+
def getAllConfs: immutable.Map[String, String] = {
51+
throw new UnsupportedOperationException
52+
}
53+
}

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@ import org.apache.spark.sql.catalyst.plans.logical._
2424
import org.apache.spark.sql.catalyst.rules._
2525
import org.apache.spark.sql.catalyst.types.StructType
2626
import org.apache.spark.sql.catalyst.types.IntegerType
27+
import org.apache.spark.sql.catalyst.CatalystConf
28+
import org.apache.spark.sql.catalyst.EmptyConf
2729

2830
/**
2931
* A trivial [[Analyzer]] with an [[EmptyCatalog]] and [[EmptyFunctionRegistry]]. Used for testing
3032
* when all relations are already filled in and the analyser needs only to resolve attribute
3133
* references.
3234
*/
33-
object SimpleAnalyzer extends Analyzer(EmptyCatalog, EmptyFunctionRegistry, true)
35+
object SimpleAnalyzer extends Analyzer(EmptyCatalog, EmptyFunctionRegistry, EmptyConf)
3436

3537
/**
3638
* Provides a logical query plan analyzer, which translates [[UnresolvedAttribute]]s and
@@ -39,11 +41,15 @@ object SimpleAnalyzer extends Analyzer(EmptyCatalog, EmptyFunctionRegistry, true
3941
*/
4042
class Analyzer(catalog: Catalog,
4143
registry: FunctionRegistry,
42-
caseSensitive: Boolean,
44+
conf: CatalystConf,
4345
maxIterations: Int = 100)
4446
extends RuleExecutor[LogicalPlan] with HiveTypeCoercion {
4547

46-
val resolver = if (caseSensitive) caseSensitiveResolution else caseInsensitiveResolution
48+
val resolver = if (conf.getConf(CatalystConf.CASE_SENSITIVE, "true").toBoolean) {
49+
caseSensitiveResolution
50+
} else {
51+
caseInsensitiveResolution
52+
}
4753

4854
val fixedPoint = FixedPoint(maxIterations)
4955

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Catalog.scala

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ package org.apache.spark.sql.catalyst.analysis
2020
import scala.collection.mutable
2121

2222
import org.apache.spark.sql.catalyst.plans.logical.{LogicalPlan, Subquery}
23+
import org.apache.spark.sql.catalyst.CatalystConf
24+
import org.apache.spark.sql.catalyst.EmptyConf
2325

2426
/**
2527
* An interface for looking up relations by name. Used by an [[Analyzer]].
2628
*/
2729
trait Catalog {
2830

29-
def caseSensitive: Boolean
31+
val conf: CatalystConf
3032

3133
def tableExists(db: Option[String], tableName: String): Boolean
3234

@@ -44,7 +46,7 @@ trait Catalog {
4446
protected def processDatabaseAndTableName(
4547
databaseName: Option[String],
4648
tableName: String): (Option[String], String) = {
47-
if (!caseSensitive) {
49+
if (!conf.getConf(CatalystConf.CASE_SENSITIVE, "true").toBoolean) {
4850
(databaseName.map(_.toLowerCase), tableName.toLowerCase)
4951
} else {
5052
(databaseName, tableName)
@@ -54,15 +56,15 @@ trait Catalog {
5456
protected def processDatabaseAndTableName(
5557
databaseName: String,
5658
tableName: String): (String, String) = {
57-
if (!caseSensitive) {
59+
if (!conf.getConf(CatalystConf.CASE_SENSITIVE, "true").toBoolean) {
5860
(databaseName.toLowerCase, tableName.toLowerCase)
5961
} else {
6062
(databaseName, tableName)
6163
}
6264
}
6365
}
6466

65-
class SimpleCatalog(val caseSensitive: Boolean) extends Catalog {
67+
class SimpleCatalog(val conf: CatalystConf) extends Catalog {
6668
val tables = new mutable.HashMap[String, LogicalPlan]()
6769

6870
override def registerTable(
@@ -165,7 +167,7 @@ trait OverrideCatalog extends Catalog {
165167
*/
166168
object EmptyCatalog extends Catalog {
167169

168-
val caseSensitive: Boolean = true
170+
override val conf: CatalystConf = EmptyConf
169171

170172
def tableExists(db: Option[String], tableName: String): Boolean = {
171173
throw new UnsupportedOperationException
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.spark.sql.catalyst.test
19+
20+
import org.apache.spark.sql.catalyst.CatalystConf
21+
22+
import scala.collection.immutable
23+
import scala.collection.mutable
24+
25+
/** A CatalystConf that can be used for local testing. */
26+
class SimpleConf extends CatalystConf{
27+
val map = mutable.Map[String, String]()
28+
29+
def setConf(key: String, value: String) : Unit = {
30+
map.put(key, value)
31+
}
32+
def getConf(key: String) : String ={
33+
map.get(key).get
34+
}
35+
def getConf(key: String, defaultValue: String) : String = {
36+
map.getOrElse(key, defaultValue)
37+
}
38+
def getAllConfs: immutable.Map[String, String] = {
39+
map.toMap
40+
}
41+
}

sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/AnalysisSuite.scala

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,23 @@ import org.apache.spark.sql.catalyst.expressions.{Alias, AttributeReference}
2323
import org.apache.spark.sql.catalyst.errors.TreeNodeException
2424
import org.apache.spark.sql.catalyst.plans.logical._
2525
import org.apache.spark.sql.catalyst.types._
26+
import org.apache.spark.sql.catalyst.CatalystConf
27+
import org.apache.spark.sql.catalyst.test.SimpleConf
2628

2729
import org.apache.spark.sql.catalyst.dsl.expressions._
2830
import org.apache.spark.sql.catalyst.dsl.plans._
2931

3032
class AnalysisSuite extends FunSuite with BeforeAndAfter {
31-
val caseSensitiveCatalog = new SimpleCatalog(true)
32-
val caseInsensitiveCatalog = new SimpleCatalog(false)
33+
val caseSensitiveConf = new SimpleConf()
34+
caseSensitiveConf.setConf(CatalystConf.CASE_SENSITIVE, "true")
35+
val caseInsensitiveConf = new SimpleConf()
36+
caseInsensitiveConf.setConf(CatalystConf.CASE_SENSITIVE, "false")
37+
val caseSensitiveCatalog = new SimpleCatalog(caseSensitiveConf)
38+
val caseInsensitiveCatalog = new SimpleCatalog(caseInsensitiveConf)
3339
val caseSensitiveAnalyze =
34-
new Analyzer(caseSensitiveCatalog, EmptyFunctionRegistry, caseSensitive = true)
40+
new Analyzer(caseSensitiveCatalog, EmptyFunctionRegistry, caseSensitiveConf)
3541
val caseInsensitiveAnalyze =
36-
new Analyzer(caseInsensitiveCatalog, EmptyFunctionRegistry, caseSensitive = false)
42+
new Analyzer(caseInsensitiveCatalog, EmptyFunctionRegistry, caseInsensitiveConf)
3743

3844
val testRelation = LocalRelation(AttributeReference("a", IntegerType, nullable = true)())
3945
val testRelation2 = LocalRelation(

sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/DecimalPrecisionSuite.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ package org.apache.spark.sql.catalyst.analysis
2020
import org.apache.spark.sql.catalyst.expressions._
2121
import org.apache.spark.sql.catalyst.plans.logical.{Project, LocalRelation}
2222
import org.apache.spark.sql.catalyst.types._
23+
import org.apache.spark.sql.catalyst.test.SimpleConf
2324
import org.scalatest.{BeforeAndAfter, FunSuite}
2425

2526
class DecimalPrecisionSuite extends FunSuite with BeforeAndAfter {
26-
val catalog = new SimpleCatalog(false)
27-
val analyzer = new Analyzer(catalog, EmptyFunctionRegistry, caseSensitive = false)
27+
val conf = new SimpleConf
28+
val catalog = new SimpleCatalog(conf)
29+
val analyzer = new Analyzer(catalog, EmptyFunctionRegistry, conf)
2830

2931
val relation = LocalRelation(
3032
AttributeReference("i", IntegerType)(),

sql/core/src/main/scala/org/apache/spark/sql/SQLConf.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
package org.apache.spark.sql
1919

20+
import org.apache.spark.sql.catalyst.CatalystConf
21+
2022
import scala.collection.immutable
2123
import scala.collection.JavaConversions._
2224

@@ -61,7 +63,7 @@ private[spark] object SQLConf {
6163
*
6264
* SQLConf is thread-safe (internally synchronized, so safe to be used in multiple threads).
6365
*/
64-
private[sql] trait SQLConf {
66+
private[sql] trait SQLConf extends CatalystConf {
6567
import SQLConf._
6668

6769
/** Only low degree of contention is expected for conf, thus NOT using ConcurrentHashMap. */
@@ -196,4 +198,3 @@ private[sql] trait SQLConf {
196198
settings.clear()
197199
}
198200
}
199-

sql/core/src/main/scala/org/apache/spark/sql/SQLContext.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,14 @@ class SQLContext(@transient val sparkContext: SparkContext)
5858
self =>
5959

6060
@transient
61-
protected[sql] lazy val catalog: Catalog = new SimpleCatalog(true)
61+
protected[sql] lazy val catalog: Catalog = new SimpleCatalog(this)
6262

6363
@transient
6464
protected[sql] lazy val functionRegistry: FunctionRegistry = new SimpleFunctionRegistry
6565

6666
@transient
6767
protected[sql] lazy val analyzer: Analyzer =
68-
new Analyzer(catalog, functionRegistry, caseSensitive = true)
68+
new Analyzer(catalog, functionRegistry, this)
6969

7070
@transient
7171
protected[sql] lazy val optimizer: Optimizer = DefaultOptimizer
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.spark.sql.test
19+
20+
import org.apache.spark.sql.{SQLConf, SQLContext}
21+
import org.apache.spark.{SparkConf, SparkContext}
22+
import org.apache.spark.sql.catalyst.CatalystConf
23+
24+
/** A case insensitive SQLContext that can be used for local testing. */
25+
object TestCaseInsensitiveSQLContext
26+
extends SQLContext(
27+
new SparkContext(
28+
"local[2]",
29+
"CaseInsensitiveSQLContext",
30+
new SparkConf())) {
31+
32+
this.setConf(CatalystConf.CASE_SENSITIVE, "false")
33+
34+
/** Fewer partitions to speed up testing. */
35+
override private[spark] def numShufflePartitions: Int =
36+
getConf(SQLConf.SHUFFLE_PARTITIONS, "5").toInt
37+
}
38+
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.spark.sql
19+
20+
import java.util.TimeZone
21+
22+
import org.apache.spark.sql.test.TestCaseInsensitiveSQLContext
23+
import org.scalatest.BeforeAndAfterAll
24+
import org.apache.spark.sql.catalyst.CatalystConf
25+
26+
/* Implicits */
27+
28+
import org.apache.spark.sql.test.TestCaseInsensitiveSQLContext._
29+
30+
object CaseInsensitiveTestData{
31+
case class StringData(s: String)
32+
val table = TestCaseInsensitiveSQLContext.sparkContext.parallelize(StringData("test") :: Nil)
33+
table.registerTempTable("caseInsensitiveTable")
34+
}
35+
36+
class SQLQueryCaseInsensitiveSuite extends QueryTest with BeforeAndAfterAll {
37+
CaseInsensitiveTestData
38+
39+
var origZone: TimeZone = _
40+
41+
override protected def beforeAll() {
42+
origZone = TimeZone.getDefault
43+
TimeZone.setDefault(TimeZone.getTimeZone("UTC"))
44+
}
45+
46+
override protected def afterAll() {
47+
TimeZone.setDefault(origZone)
48+
}
49+
50+
test("SPARK-4699 case sensitivity SQL query") {
51+
setConf(CatalystConf.CASE_SENSITIVE, "false")
52+
checkAnswer(sql("SELECT S FROM CASEINSENSITIVETABLE"), "test")
53+
}
54+
55+
}

0 commit comments

Comments
 (0)