Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions core/src/main/resources/error/error-classes.json
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,11 @@
],
"sqlState" : "22023"
},
"FIELD_NOT_FOUND" : {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm wondering if we should unify column and field (which is nested column) in the error message. cc @srielau

Copy link
Contributor

Choose a reason for hiding this comment

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

Well, that was kind of my earlier question. How is this different from UNRESOLVED_COLUMN? (Which does include fields in the message). But it seems we KNOW that we are talking about a field here (DDL?)

Copy link
Member Author

Choose a reason for hiding this comment

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

But it seems we KNOW that we are talking about a field here (DDL?)

Even more specifically, we look for a field in a struct, and the struct doesn't have such field.

"message" : [
"No such struct field <fieldName> in <fields>."
]
},
"FORBIDDEN_OPERATION" : {
"message" : [
"The operation <statement> is not allowed on the <objectType>: <objectName>"
Expand Down Expand Up @@ -2479,11 +2484,6 @@
"The duration and time inputs to window must be an integer, long or string literal."
]
},
"_LEGACY_ERROR_TEMP_1208" : {
"message" : [
"No such struct field <fieldName> in <fields>."
]
},
"_LEGACY_ERROR_TEMP_1209" : {
"message" : [
"Ambiguous reference to fields <fields>."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2080,10 +2080,10 @@ private[sql] object QueryCompilationErrors extends QueryErrorsBase {
def noSuchStructFieldInGivenFieldsError(
fieldName: String, fields: Array[StructField]): Throwable = {
new AnalysisException(
errorClass = "_LEGACY_ERROR_TEMP_1208",
errorClass = "FIELD_NOT_FOUND",
messageParameters = Map(
"fieldName" -> fieldName,
"fields" -> fields.map(_.name).mkString(", ")))
"fieldName" -> toSQLId(fieldName),
"fields" -> fields.map(f => toSQLId(f.name)).mkString(", ")))
}

def ambiguousReferenceToFieldsError(fields: String): Throwable = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,10 +366,11 @@ class AnalysisErrorSuite extends AnalysisTest {
"Ambiguous reference to fields" :: "differentCase" :: "differentcase" :: Nil,
caseSensitive = false)

errorTest(
errorClassTest(
"missing field",
nestedRelation2.select($"top.c"),
"No such struct field" :: "aField" :: "bField" :: "cField" :: Nil,
"FIELD_NOT_FOUND",
Map("fieldName" -> "`c`", "fields" -> "`aField`, `bField`, `cField`"),
caseSensitive = false)

errorTest(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,10 @@ class ResolveSubquerySuite extends AnalysisTest {
LateralJoin(t4, LateralSubquery(Project(Seq(xa, ya), t0), Seq(x, y)), Inner, None)
)
// Analyzer will try to resolve struct first before subquery alias.
assertAnalysisError(
assertAnalysisErrorClass(
lateralJoin(t1.as("x"), t4.select($"x.a", $"x.b")),
Seq("No such struct field b in a")
)
"FIELD_NOT_FOUND",
Map("fieldName" -> "`b`", "fields" -> "`a`"))
}

test("lateral join with unsupported expressions") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,10 @@ class EncoderResolutionSuite extends PlanTest {
test("the real type is not compatible with encoder schema: array element type") {
val encoder = ExpressionEncoder[ArrayClass]
val attrs = Seq($"arr".array(new StructType().add("c", "int")))
assert(intercept[AnalysisException](encoder.resolveAndBind(attrs)).message ==
"No such struct field a in c.")
checkError(
exception = intercept[AnalysisException](encoder.resolveAndBind(attrs)),
errorClass = "FIELD_NOT_FOUND",
parameters = Map("fieldName" -> "`a`", "fields" -> "`c`"))
}

test("the real type is not compatible with encoder schema: nested array element type") {
Expand All @@ -150,8 +152,8 @@ class EncoderResolutionSuite extends PlanTest {
.add("arr", ArrayType(new StructType().add("c", "int")))))
checkError(
exception = intercept[AnalysisException](encoder.resolveAndBind(attrs)),
errorClass = "_LEGACY_ERROR_TEMP_1208",
parameters = Map("fieldName" -> "a", "fields" -> "c"))
errorClass = "FIELD_NOT_FOUND",
parameters = Map("fieldName" -> "`a`", "fields" -> "`c`"))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,12 @@ class AttributeResolutionSuite extends SparkFunSuite {
case _ => fail()
}

val ex = intercept[AnalysisException] {
attrs.resolve(Seq("ns1", "t", "a", "cc"), resolver)
}
assert(ex.getMessage.contains("No such struct field cc in aa, bb"))
checkError(
exception = intercept[AnalysisException] {
attrs.resolve(Seq("ns1", "t", "a", "cc"), resolver)
},
errorClass = "FIELD_NOT_FOUND",
parameters = Map("fieldName" -> "`cc`", "fields" -> "`aa`, `bb`"))
}

test("attribute resolution with case insensitive resolver") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ org.apache.spark.sql.AnalysisException
} ]
}


-- !query
SELECT map_zip_with(decimal_map1, int_map, (k, v1, v2) -> struct(k, v1, v2)) m
FROM various_maps
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1043,13 +1043,19 @@ class ColumnExpressionSuite extends QueryTest with SharedSparkSession {
}

test("withField should throw an exception if any intermediate structs don't exist") {
intercept[AnalysisException] {
structLevel2.withColumn("a", $"a".withField("x.b", lit(2)))
}.getMessage should include("No such struct field x in a")
checkError(
exception = intercept[AnalysisException] {
structLevel2.withColumn("a", $"a".withField("x.b", lit(2)))
},
errorClass = "FIELD_NOT_FOUND",
parameters = Map("fieldName" -> "`x`", "fields" -> "`a`"))

intercept[AnalysisException] {
structLevel3.withColumn("a", $"a".withField("a.x.b", lit(2)))
}.getMessage should include("No such struct field x in a")
checkError(
exception = intercept[AnalysisException] {
structLevel3.withColumn("a", $"a".withField("a.x.b", lit(2)))
},
errorClass = "FIELD_NOT_FOUND",
parameters = Map("fieldName" -> "`x`", "fields" -> "`a`"))
}

test("withField should throw an exception if intermediate field is not a struct") {
Expand Down Expand Up @@ -1465,9 +1471,12 @@ class ColumnExpressionSuite extends QueryTest with SharedSparkSession {
nullable = false))),
nullable = false))))

intercept[AnalysisException] {
df.withColumn("a", $"a".withField("a.b.e.f", lit(2)))
}.getMessage should include("No such struct field a in a.b")
checkError(
exception = intercept[AnalysisException] {
df.withColumn("a", $"a".withField("a.b.e.f", lit(2)))
},
errorClass = "FIELD_NOT_FOUND",
parameters = Map("fieldName" -> "`a`", "fields" -> "`a`.`b`"))
}

private lazy val mixedCaseStructLevel1: DataFrame = spark.createDataFrame(
Expand Down Expand Up @@ -1574,13 +1583,19 @@ class ColumnExpressionSuite extends QueryTest with SharedSparkSession {

test("withField should throw an exception because casing is different") {
withSQLConf(SQLConf.CASE_SENSITIVE.key -> "true") {
intercept[AnalysisException] {
mixedCaseStructLevel2.withColumn("a", $"a".withField("A.a", lit(2)))
}.getMessage should include("No such struct field A in a, B")

intercept[AnalysisException] {
mixedCaseStructLevel2.withColumn("a", $"a".withField("b.a", lit(2)))
}.getMessage should include("No such struct field b in a, B")
checkError(
exception = intercept[AnalysisException] {
mixedCaseStructLevel2.withColumn("a", $"a".withField("A.a", lit(2)))
},
errorClass = "FIELD_NOT_FOUND",
parameters = Map("fieldName" -> "`A`", "fields" -> "`a`, `B`"))

checkError(
exception = intercept[AnalysisException] {
mixedCaseStructLevel2.withColumn("a", $"a".withField("b.a", lit(2)))
},
errorClass = "FIELD_NOT_FOUND",
parameters = Map("fieldName" -> "`b`", "fields" -> "`a`, `B`"))
}
}

Expand Down Expand Up @@ -1785,13 +1800,19 @@ class ColumnExpressionSuite extends QueryTest with SharedSparkSession {
}

test("dropFields should throw an exception if any intermediate structs don't exist") {
intercept[AnalysisException] {
structLevel2.withColumn("a", $"a".dropFields("x.b"))
}.getMessage should include("No such struct field x in a")
checkError(
exception = intercept[AnalysisException] {
structLevel2.withColumn("a", $"a".dropFields("x.b"))
},
errorClass = "FIELD_NOT_FOUND",
parameters = Map("fieldName" -> "`x`", "fields" -> "`a`"))

intercept[AnalysisException] {
structLevel3.withColumn("a", $"a".dropFields("a.x.b"))
}.getMessage should include("No such struct field x in a")
checkError(
exception = intercept[AnalysisException] {
structLevel3.withColumn("a", $"a".dropFields("a.x.b"))
},
errorClass = "FIELD_NOT_FOUND",
parameters = Map("fieldName" -> "`x`", "fields" -> "`a`"))
}

test("dropFields should throw an exception if intermediate field is not a struct") {
Expand Down Expand Up @@ -2035,13 +2056,19 @@ class ColumnExpressionSuite extends QueryTest with SharedSparkSession {

test("dropFields should throw an exception because casing is different") {
withSQLConf(SQLConf.CASE_SENSITIVE.key -> "true") {
intercept[AnalysisException] {
mixedCaseStructLevel2.withColumn("a", $"a".dropFields("A.a"))
}.getMessage should include("No such struct field A in a, B")

intercept[AnalysisException] {
mixedCaseStructLevel2.withColumn("a", $"a".dropFields("b.a"))
}.getMessage should include("No such struct field b in a, B")
checkError(
exception = intercept[AnalysisException] {
mixedCaseStructLevel2.withColumn("a", $"a".dropFields("A.a"))
},
errorClass = "FIELD_NOT_FOUND",
parameters = Map("fieldName" -> "`A`", "fields" -> "`a`, `B`"))

checkError(
exception = intercept[AnalysisException] {
mixedCaseStructLevel2.withColumn("a", $"a".dropFields("b.a"))
},
errorClass = "FIELD_NOT_FOUND",
parameters = Map("fieldName" -> "`b`", "fields" -> "`a`, `B`"))
}
}

Expand Down Expand Up @@ -2279,9 +2306,12 @@ class ColumnExpressionSuite extends QueryTest with SharedSparkSession {
}

test("should be able to refer to newly added nested column") {
intercept[AnalysisException] {
structLevel1.select($"a".withField("d", lit(4)).withField("e", $"a.d" + 1).as("a"))
}.getMessage should include("No such struct field d in a, b, c")
checkError(
exception = intercept[AnalysisException] {
structLevel1.select($"a".withField("d", lit(4)).withField("e", $"a.d" + 1).as("a"))
},
errorClass = "FIELD_NOT_FOUND",
parameters = Map("fieldName" -> "`d`", "fields" -> "`a`, `b`, `c`"))

checkAnswer(
structLevel1
Expand Down Expand Up @@ -2327,11 +2357,14 @@ class ColumnExpressionSuite extends QueryTest with SharedSparkSession {

// we can't access the nested column in subsequent select statement after dropping it in a
// previous select statement
intercept[AnalysisException]{
structLevel1
.select($"a".dropFields("c").as("a"))
.select($"a".withField("z", $"a.c")).as("a")
}.getMessage should include("No such struct field c in a, b")
checkError(
exception = intercept[AnalysisException]{
structLevel1
.select($"a".dropFields("c").as("a"))
.select($"a".withField("z", $"a.c")).as("a")
},
errorClass = "FIELD_NOT_FOUND",
parameters = Map("fieldName" -> "`c`", "fields" -> "`a`, `b`"))
}

test("nestedDf should generate nested DataFrames") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -989,15 +989,17 @@ class DataFrameSetOperationsSuite extends QueryTest with SharedSparkSession {
// nested struct, inner struct having different col name
df1 = Seq((0, UnionClass1a(0, 1L, UnionClass2(1, "2")))).toDF("id", "a")
df2 = Seq((1, UnionClass1b(1, 2L, UnionClass3(2, 3L)))).toDF("id", "a")
var errMsg = intercept[AnalysisException] {
df1.unionByName(df2)
}.getMessage
assert(errMsg.contains("No such struct field c in a, b"))
checkError(
exception = intercept[AnalysisException] {
df1.unionByName(df2)
},
errorClass = "FIELD_NOT_FOUND",
parameters = Map("fieldName" -> "`c`", "fields" -> "`a`, `b`"))

// If right side of the nested struct has extra col.
df1 = Seq((1, 2, UnionClass1d(1, 2, Struct3(1)))).toDF("a", "b", "c")
df2 = Seq((1, 2, UnionClass1e(1, 2, Struct4(1, 5)))).toDF("a", "b", "c")
errMsg = intercept[AnalysisException] {
val errMsg = intercept[AnalysisException] {
df1.unionByName(df2)
}.getMessage
assert(errMsg.contains("Union can only be performed on tables with" +
Expand Down
20 changes: 13 additions & 7 deletions sql/core/src/test/scala/org/apache/spark/sql/SQLQuerySuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2994,13 +2994,19 @@ class SQLQuerySuite extends QueryTest with SharedSparkSession with AdaptiveSpark

test("SPARK-26402: accessing nested fields with different cases in case insensitive mode") {
withSQLConf(SQLConf.CASE_SENSITIVE.key -> "true") {
val msg = intercept[AnalysisException] {
withTable("t") {
sql("create table t (s struct<i: Int>) using json")
checkAnswer(sql("select s.I from t group by s.i"), Nil)
}
}.message
assert(msg.contains("No such struct field I in i"))
checkError(
exception = intercept[AnalysisException] {
withTable("t") {
sql("create table t (s struct<i: Int>) using json")
checkAnswer(sql("select s.I from t group by s.i"), Nil)
}
},
errorClass = "FIELD_NOT_FOUND",
parameters = Map("fieldName" -> "`I`", "fields" -> "`i`"),
context = ExpectedContext(
fragment = "s.I",
start = 7,
stop = 9))
}

withSQLConf(SQLConf.CASE_SENSITIVE.key -> "false") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,16 @@ abstract class SQLViewTestSuite extends QueryTest with SQLTestUtils {
// re-create the table without nested field `i` which is referred by the view.
sql("DROP TABLE t")
sql("CREATE TABLE t(s STRUCT<j: INT>) USING json")
val e = intercept[AnalysisException](spark.table(viewName))
assert(e.message.contains("No such struct field i in j"))
checkError(
exception = intercept[AnalysisException](spark.table(viewName)),
errorClass = "FIELD_NOT_FOUND",
parameters = Map("fieldName" -> "`i`", "fields" -> "`j`"),
context = ExpectedContext(
fragment = "s.i",
objectName = fullyQualifiedViewName("v"),
objectType = "VIEW",
startIndex = 7,
stopIndex = 9))

// drop invalid view should be fine
sql(s"DROP VIEW $viewName")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,14 @@ class FileMetadataStructRowIndexSuite extends QueryTest with SharedSparkSession

test("unsupported file format - read _metadata.row_index") {
withReadDataFrame("orc") { df =>
val ex = intercept[AnalysisException] {
df.select("*", s"${FileFormat.METADATA_NAME}.${FileFormat.ROW_INDEX}")
}
assert(ex.getMessage.contains("No such struct field row_index"))
checkError(
exception = intercept[AnalysisException] {
df.select("*", s"${FileFormat.METADATA_NAME}.${FileFormat.ROW_INDEX}")
},
errorClass = "FIELD_NOT_FOUND",
parameters = Map(
"fieldName" -> "`row_index`",
"fields" -> "`file_path`, `file_name`, `file_size`, `file_modification_time`"))
}
}

Expand Down
Loading