Skip to content

Commit 59d5ea2

Browse files
kamcheungting-dbscottsand-db
authored andcommittedMar 9, 2022
Refactor first set of 20 error messages.
This PR is going to refactor the existing exceptions in our code base, so that they are well organized and queryable. This commit refactor the first 20 error messages. To handle compilation and error class handling, there are two more classes created: 1. The DeltaThrowable: this is used as the based interface of delta code and contains all necessary implementation for error message framework. With its help, the compilation error of OSS Delta caused by outdated Apache Spark can be avoided. 2. The DeltaThrowableHelper: This is used to pick the error class template from JSON files. It handles all exceptions' message for Delta. Thus, there are 2 JSON files we read: 1. error-classes.json: it stores the error classes for exceptions inside Apache Spark code base. It will be maintained by the Apache Spark community. 2. delta-error-classes.json: it stores the error classes for exceptions inside our Delta code base. This is maintained by Delta Lake. Note: 0. The error-classes.json and delta-error-classes.json are not allowed to include same error class. 1. Test cases for all error classes are covered. 2. No duplicate error classes in Delta 4. No error classes are shared by Delta and Spark 5. Delta error classes are correctly formatted 6. Delta message format invariants GitOrigin-RevId: cff83f531faeaa97391f8debad7e8dbe7f18f632
1 parent 0d1652a commit 59d5ea2

16 files changed

+606
-81
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
{
2+
"COMPLEX_TYPE_COLUMN_CANNOT_CONTAIN_NULL_TYPE" : {
3+
"message" : [ " Found nested NullType in column %s which is of %s. Delta doesn't support writing NullType in complex types." ],
4+
"sqlState" : "22005"
5+
},
6+
"CONFLICT_SET_COLUMN" : {
7+
"message" : [ "There is a conflict from these SET columns: %s." ],
8+
"sqlState" : "42000"
9+
},
10+
"DELTA_MERGE_UNEXPECTED_ASSIGNMENT_KEY" : {
11+
"message" : [ "Unexpected assignment key: %s - %s" ],
12+
"sqlState" : "22005"
13+
},
14+
"DELTA_TABLE_ONLY_OPERATION" : {
15+
"message" : [ "%s is not a Delta table. %s is only supported for Delta tables." ],
16+
"sqlState" : "0A000"
17+
},
18+
"FAILED_CHECKPOINT" : {
19+
"message" : [ "Cannot rename %s to %s" ]
20+
},
21+
"INVALID_CHARACTERS_IN_COLUMN_NAME" : {
22+
"message" : [ "Attribute name \"%s\" contains invalid character(s) among \" ,;{}()\\\\n\\\\t=\". Please use alias to rename it." ],
23+
"sqlState" : "42000"
24+
},
25+
"INVALID_COPY_ENCRYPTION" : {
26+
"message" : [ "Invalid %s. COPY INTO source %s." ],
27+
"sqlState" : "42000"
28+
},
29+
"INVALID_GENERATED_COLUMN_REFERENCES" : {
30+
"message" : [ "A generated column cannot use a non-existent column or another generated column" ],
31+
"sqlState" : "42000"
32+
},
33+
"MERGE_INCOMPATIBLE_DECIMAL_TYPE" : {
34+
"message" : [ "Failed to merge decimal types with incompatible %s" ],
35+
"sqlState" : "22005"
36+
},
37+
"MISSING_DELTA_TABLE" : {
38+
"message" : [ "%s is not a Delta table." ],
39+
"sqlState" : "42000"
40+
},
41+
"MISSING_NOT_NULL_COLUMN_VALUE" : {
42+
"message" : [ "Column %s, which has a NOT NULL constraint, is missing from the data being written into the table." ],
43+
"sqlState" : "42000"
44+
},
45+
"MISSING_SET_COLUMN" : {
46+
"message" : [ "SET column %s not found given columns: %s." ],
47+
"sqlState" : "42000"
48+
},
49+
"UNSUPPORTED_COLUMN_MAPPING_CONVERT_TO_DELTA" : {
50+
"message" : [ "The configuration '%s' cannot be set to `%s` when using CONVERT TO DELTA." ],
51+
"sqlState" : "0A000"
52+
},
53+
"UNSUPPORTED_COLUMN_MAPPING_MODE_CHANGE" : {
54+
"message" : [ "Changing column mapping mode from '%s' to '%s' is not supported." ],
55+
"sqlState" : "0A000"
56+
},
57+
"UNSUPPORTED_COLUMN_MAPPING_PROTOCOL" : {
58+
"message" : [ "", "Your current table protocol version does not support changing column mapping modes", "using %s.", "", "Required Delta protocol version for column mapping:", "%s", "Your table's current Delta protocol version:", "%s", "", "Please upgrade your table's protocol version using ALTER TABLE SET TBLPROPERTIES and try again.", "", "" ],
59+
"sqlState" : "0A000"
60+
},
61+
"UNSUPPORTED_COLUMN_MAPPING_SCHEMA_CHANGE" : {
62+
"message" : [ "", "Schema change is detected:", "", "old schema:", "%s", "", "new schema:", "%s", "", "Schema changes are not allowed during the change of column mapping mode.", "", "" ],
63+
"sqlState" : "0A000"
64+
},
65+
"UNSUPPORTED_COLUMN_MAPPING_WRITE" : {
66+
"message" : [ "Writing data with column mapping mode is not supported." ],
67+
"sqlState" : "0A000"
68+
},
69+
"UNSUPPORTED_MANIFEST_GENERATION_WITH_COLUMN_MAPPING" : {
70+
"message" : [ "Manifest generation is not supported for tables that leverage column mapping, as external readers cannot read these Delta tables. See Databricks documentation for more details." ],
71+
"sqlState" : "0A000"
72+
},
73+
"UNSUPPORTED_NESTED_COLUMN_IN_BLOOM_FILTER" : {
74+
"message" : [ "Creating a bloom filer index on a nested column is currently unsupported: %s" ],
75+
"sqlState" : "0A000"
76+
}
77+
}

‎core/src/main/scala/org/apache/spark/sql/catalyst/plans/logical/deltaMerge.scala

+4-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package org.apache.spark.sql.catalyst.plans.logical
1818

1919
import java.util.Locale
2020

21+
import org.apache.spark.sql.delta.DeltaAnalysisException
2122
import org.apache.spark.sql.delta.schema.SchemaMergingUtils
2223
import org.apache.spark.sql.delta.sources.DeltaSQLConf
2324

@@ -150,7 +151,9 @@ object DeltaMergeIntoClause {
150151
case Assignment(key: UnresolvedAttribute, expr) => DeltaMergeAction(key.nameParts, expr)
151152
case Assignment(key: Attribute, expr) => DeltaMergeAction(Seq(key.name), expr)
152153
case other =>
153-
throw new AnalysisException(s"Unexpected assignment key: ${other.getClass} - $other")
154+
throw new DeltaAnalysisException(
155+
errorClass = "DELTA_MERGE_UNEXPECTED_ASSIGNMENT_KEY",
156+
messageParameters = Array(s"${other.getClass}", s"$other"))
154157
}
155158
}
156159
}

‎core/src/main/scala/org/apache/spark/sql/delta/Checkpoints.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ object Checkpoints extends DeltaLogging {
376376
} else {
377377
// There should be only one writer writing the checkpoint file, so there must be
378378
// something wrong here.
379-
throw new IllegalStateException(s"Cannot rename $src to $dest")
379+
throw DeltaErrors.failOnCheckpoint(src, dest)
380380
}
381381
} finally {
382382
if (!renameDone) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (2021) The Delta Lake Project Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.apache.spark.sql.delta
18+
19+
import org.apache.spark.sql.AnalysisException
20+
21+
class DeltaAnalysisException(
22+
errorClass: String, messageParameters: Array[String], cause: Option[Throwable] = None)
23+
extends AnalysisException(
24+
DeltaThrowableHelper.getMessage(errorClass, messageParameters),
25+
errorClass = Some(errorClass),
26+
messageParameters = messageParameters,
27+
cause = cause)
28+
with DeltaThrowable

‎core/src/main/scala/org/apache/spark/sql/delta/DeltaErrors.scala

+100-56
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,16 @@ object DeltaErrors
157157
new AnalysisException(s"Can't resolve column ${name} in ${schema.treeString}")
158158
}
159159

160+
def failOnCheckpoint(src: Path, dest: Path): DeltaIllegalStateException = {
161+
failOnCheckpoint(src.toString, dest.toString)
162+
}
163+
164+
def failOnCheckpoint(src: String, dest: String): DeltaIllegalStateException = {
165+
new DeltaIllegalStateException(
166+
errorClass = "FAILED_CHECKPOINT",
167+
messageParameters = Array(s"$src", s"$dest"))
168+
}
169+
160170

161171
def formatColumn(colName: String): String = s"`$colName`"
162172

@@ -175,9 +185,9 @@ object DeltaErrors
175185
}
176186

177187
def notNullColumnMissingException(constraint: Constraints.NotNull): Throwable = {
178-
new InvariantViolationException(s"Column ${UnresolvedAttribute(constraint.column).name}" +
179-
s", which has a NOT NULL constraint, is missing from the data being " +
180-
s"written into the table.")
188+
new InvariantViolationException(
189+
errorClass = "MISSING_NOT_NULL_COLUMN_VALUE",
190+
messageParameters = Array(s"${UnresolvedAttribute(constraint.column).name}"))
181191
}
182192

183193
def nestedNotNullConstraint(
@@ -267,13 +277,16 @@ object DeltaErrors
267277
}
268278

269279
def notADeltaTableException(deltaTableIdentifier: DeltaTableIdentifier): Throwable = {
270-
new AnalysisException(s"$deltaTableIdentifier is not a Delta table.")
280+
new DeltaAnalysisException(
281+
errorClass = "MISSING_DELTA_TABLE",
282+
messageParameters = Array(s"$deltaTableIdentifier"))
271283
}
272284

273285
def notADeltaTableException(
274286
operation: String, deltaTableIdentifier: DeltaTableIdentifier): Throwable = {
275-
new AnalysisException(s"$deltaTableIdentifier is not a Delta table. " +
276-
s"$operation is only supported for Delta tables.")
287+
new DeltaAnalysisException(
288+
errorClass = "DELTA_TABLE_ONLY_OPERATION",
289+
messageParameters = Array(s"$deltaTableIdentifier", s"$operation"))
277290
}
278291

279292
def notADeltaTableException(operation: String): Throwable = {
@@ -305,10 +318,9 @@ object DeltaErrors
305318
}
306319

307320
def invalidColumnName(name: String): Throwable = {
308-
new AnalysisException(
309-
s"""Attribute name "$name" contains invalid character(s) among " ,;{}()\\n\\t=".
310-
|Please use alias to rename it.
311-
""".stripMargin.split("\n").mkString(" ").trim)
321+
new DeltaAnalysisException(
322+
errorClass = "INVALID_CHARACTERS_IN_COLUMN_NAME",
323+
messageParameters = Array(name))
312324
}
313325

314326
def invalidPartitionColumn(e: AnalysisException): Throwable = {
@@ -627,13 +639,15 @@ object DeltaErrors
627639
}
628640

629641
def updateSetColumnNotFoundException(col: String, colList: Seq[String]): Throwable = {
630-
new AnalysisException(
631-
s"SET column ${formatColumn(col)} not found given columns: ${formatColumnList(colList)}.")
642+
new DeltaAnalysisException(
643+
errorClass = "MISSING_SET_COLUMN",
644+
messageParameters = Array(formatColumn(col), formatColumnList(colList)))
632645
}
633646

634647
def updateSetConflictException(cols: Seq[String]): Throwable = {
635-
new AnalysisException(
636-
s"There is a conflict from these SET columns: ${formatColumnList(cols)}.")
648+
new DeltaAnalysisException(
649+
errorClass = "CONFLICT_SET_COLUMN",
650+
messageParameters = Array(formatColumnList(cols)))
637651
}
638652

639653
def updateNonStructTypeFieldNotSupportedException(col: String, s: DataType): Throwable = {
@@ -654,8 +668,9 @@ object DeltaErrors
654668
}
655669

656670
def bloomFilterOnNestedColumnNotSupportedException(name: String): Throwable = {
657-
new AnalysisException(
658-
s"Creating a bloom filer index on a nested column is currently unsupported: $name")
671+
new DeltaAnalysisException(
672+
errorClass = "UNSUPPORTED_NESTED_COLUMN_IN_BLOOM_FILTER",
673+
messageParameters = Array(name))
659674
}
660675

661676
def bloomFilterOnColumnTypeNotSupportedException(name: String, dataType: DataType): Throwable = {
@@ -1004,6 +1019,18 @@ object DeltaErrors
10041019
s"COPY INTO source encryption currently only supports s3/s3n/s3a/abfss.")
10051020
}
10061021

1022+
def copyIntoEncryptionSseCRequired(): Throwable = {
1023+
new DeltaIllegalArgumentException(
1024+
errorClass = "INVALID_COPY_ENCRYPTION",
1025+
messageParameters = Array("encryption type", "encryption must specify 'TYPE' = 'AWS_SSE_C'"))
1026+
}
1027+
1028+
def copyIntoEncryptionMasterKeyRequired(): Throwable = {
1029+
new DeltaIllegalArgumentException(
1030+
errorClass = "INVALID_COPY_ENCRYPTION",
1031+
messageParameters = Array("encryption arguments", "encryption must specify a MASTER_KEY"))
1032+
}
1033+
10071034
def copyIntoCredentialsNotAllowedOn(scheme: String): Throwable = {
10081035
new IllegalArgumentException(
10091036
s"Invalid scheme $scheme. " +
@@ -1146,9 +1173,8 @@ object DeltaErrors
11461173
}
11471174

11481175
def generatedColumnsReferToWrongColumns(e: AnalysisException): Throwable = {
1149-
new AnalysisException(
1150-
"A generated column cannot use a non-existent column or another generated column",
1151-
cause = Some(e))
1176+
new DeltaAnalysisException(
1177+
errorClass = "INVALID_GENERATED_COLUMN_REFERENCES", Array.empty, cause = Some(e))
11521178
}
11531179

11541180
def generatedColumnsUpdateColumnType(current: StructField, update: StructField): Throwable = {
@@ -1235,39 +1261,36 @@ object DeltaErrors
12351261
}
12361262

12371263
def changeColumnMappingModeNotSupported(oldMode: String, newMode: String): Throwable = {
1238-
new ColumnMappingUnsupportedException("Changing column mapping mode from" +
1239-
s" '$oldMode' to '$newMode' is not supported.")
1264+
new DeltaColumnMappingUnsupportedException(
1265+
errorClass = "UNSUPPORTED_COLUMN_MAPPING_MODE_CHANGE",
1266+
messageParameters = Array(oldMode, newMode))
1267+
}
1268+
1269+
def writesWithColumnMappingNotSupported: Throwable = {
1270+
new DeltaColumnMappingUnsupportedException(
1271+
errorClass = "UNSUPPORTED_COLUMN_MAPPING_WRITE")
12401272
}
12411273

12421274
def generateManifestWithColumnMappingNotSupported: Throwable = {
1243-
new ColumnMappingUnsupportedException("Manifest generation is not supported for tables that " +
1244-
"leverage column mapping, as external readers cannot read these Delta tables. " +
1245-
"See Databricks documentation for more details.")
1275+
new DeltaColumnMappingUnsupportedException(
1276+
errorClass = "UNSUPPORTED_MANIFEST_GENERATION_WITH_COLUMN_MAPPING")
12461277
}
12471278

12481279
def convertToDeltaWithColumnMappingNotSupported(mode: DeltaColumnMappingMode): Throwable = {
1249-
new ColumnMappingUnsupportedException(
1250-
s"The configuration '${DeltaConfigs.COLUMN_MAPPING_MODE.defaultTablePropertyKey}' " +
1251-
s"cannot be set to `${mode.name}` when using CONVERT TO DELTA.")
1280+
new DeltaColumnMappingUnsupportedException(
1281+
errorClass = "UNSUPPORTED_COLUMN_MAPPING_CONVERT_TO_DELTA",
1282+
messageParameters = Array(
1283+
DeltaConfigs.COLUMN_MAPPING_MODE.defaultTablePropertyKey,
1284+
mode.name))
12521285
}
12531286

12541287
def changeColumnMappingModeOnOldProtocol(oldProtocol: Protocol): Throwable = {
1255-
// scalastyle:off line.size.limit
1256-
new ColumnMappingUnsupportedException(
1257-
s"""
1258-
|Your current table protocol version does not support changing column mapping modes
1259-
|using ${DeltaConfigs.COLUMN_MAPPING_MODE.key}.
1260-
|
1261-
|Required Delta protocol version for column mapping:
1262-
|${DeltaColumnMapping.MIN_PROTOCOL_VERSION}
1263-
|
1264-
|Your table's current Delta protocol version:
1265-
|$oldProtocol
1266-
|
1267-
|Please upgrade your table's protocol version using ALTER TABLE SET TBLPROPERTIES and try again.
1268-
|
1269-
|""".stripMargin)
1270-
// scalastyle:on line.size.limit
1288+
new DeltaColumnMappingUnsupportedException(
1289+
errorClass = "UNSUPPORTED_COLUMN_MAPPING_PROTOCOL",
1290+
messageParameters = Array(
1291+
s"${DeltaConfigs.COLUMN_MAPPING_MODE.key}",
1292+
s"${DeltaColumnMapping.MIN_PROTOCOL_VERSION.toString}",
1293+
s"$oldProtocol"))
12711294
}
12721295

12731296
def columnRenameNotSupported(spark: SparkSession, protocol: Protocol): Throwable = {
@@ -1301,19 +1324,11 @@ object DeltaErrors
13011324
def schemaChangeDuringMappingModeChangeNotSupported(
13021325
oldSchema: StructType,
13031326
newSchema: StructType): Throwable =
1304-
new ColumnMappingUnsupportedException(
1305-
s"""
1306-
|Schema change is detected:
1307-
|
1308-
|old schema:
1309-
|${formatSchema(oldSchema)}
1310-
|
1311-
|new schema:
1312-
|${formatSchema(newSchema)}
1313-
|
1314-
|Schema changes are not allowed during the change of column mapping mode.
1315-
|
1316-
|""".stripMargin)
1327+
new DeltaColumnMappingUnsupportedException(
1328+
errorClass = "UNSUPPORTED_COLUMN_MAPPING_SCHEMA_CHANGE",
1329+
messageParameters = Array(
1330+
formatSchema(oldSchema),
1331+
formatSchema(newSchema)))
13171332

13181333
def foundInvalidCharsInColumnNames(cause: Throwable): Throwable = {
13191334
var adviceMsg = "Please use alias to rename it."
@@ -1625,6 +1640,35 @@ class MetadataMismatchErrorBuilder {
16251640
}
16261641
}
16271642

1643+
class DeltaColumnMappingUnsupportedException(
1644+
errorClass: String,
1645+
messageParameters: Array[String] = Array.empty)
1646+
extends ColumnMappingUnsupportedException(
1647+
DeltaThrowableHelper.getMessage(errorClass, messageParameters))
1648+
with DeltaThrowable {
1649+
override def getErrorClass: String = errorClass
1650+
}
1651+
1652+
class DeltaIllegalArgumentException(
1653+
errorClass: String,
1654+
messageParameters: Array[String] = Array.empty,
1655+
cause: Throwable = null)
1656+
extends IllegalArgumentException(
1657+
DeltaThrowableHelper.getMessage(errorClass, messageParameters), cause)
1658+
with DeltaThrowable {
1659+
override def getErrorClass: String = errorClass
1660+
}
1661+
1662+
class DeltaIllegalStateException(
1663+
errorClass: String,
1664+
messageParameters: Array[String] = Array.empty,
1665+
cause: Throwable = null)
1666+
extends IllegalStateException(
1667+
DeltaThrowableHelper.getMessage(errorClass, messageParameters), cause)
1668+
with DeltaThrowable {
1669+
override def getErrorClass: String = errorClass
1670+
}
1671+
16281672
/** Errors thrown around column mapping. */
16291673
class ColumnMappingUnsupportedException(msg: String)
16301674
extends UnsupportedOperationException(msg)

0 commit comments

Comments
 (0)
Please sign in to comment.