This is an automated email from the ASF dual-hosted git repository.
cloud-fan pushed a commit to branch branch-4.x
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/branch-4.x by this push:
new 72edddb358e3 [SPARK-57295][SQL] Make database location validation
consistent for w…
72edddb358e3 is described below
commit 72edddb358e3524db787002a12fc75420f8d1943
Author: Anurag Kumar Dwivedi <[email protected]>
AuthorDate: Thu Jun 11 12:58:56 2026 -0700
[SPARK-57295][SQL] Make database location validation consistent for w…
**PR Description**
What changes were proposed in this pull request?
This PR updates database location validation to reject whitespace-only
location values during namespace/database creation and location alteration.
Currently Spark rejects only empty string locations:
`CREATE DATABASE db LOCATION ''`
with:
`INVALID_EMPTY_LOCATION`
However, whitespace-only values such as:
```
CREATE DATABASE db LOCATION ' '
CREATE DATABASE db LOCATION '\t'
CREATE DATABASE db LOCATION '\n'
```
are not rejected by Spark validation.
As a result:
- In non-HMS catalog paths, databases may be created successfully using
whitespace-only locations.
- In HMS-backed catalog paths, the request reaches Hive Metastore and fails
later with metastore-specific exceptions.
This PR trims the location value before performing the empty-location
validation so that whitespace-only values are treated consistently as empty
locations.
The validation is updated in:
```
ResolveSessionCatalog
DataSourceV2Strategy
```
Additional regression tests are added for:
```
' '
'\t'
'\n'
```
for both:
```
namespace/database creation
namespace/database location alteration
```
By treating whitespace-only values as empty locations, Spark provides:
- consistent validation behavior
- consistent error reporting
- earlier failure during analysis
- reduced dependence on catalog-specific validation
**Does this PR introduce any user-facing change?**
Yes.
The following statements will now fail consistently with:
`INVALID_EMPTY_LOCATION`
**Examples:**
```
CREATE DATABASE db LOCATION ' '
CREATE DATABASE db LOCATION '\t'
CREATE DATABASE db LOCATION '\n'
```
Jira - https://issues.apache.org/jira/browse/SPARK-57295
Closes #56356 from AnuragKDwivedi/SPARK-57295-db-location-validation.
Authored-by: Anurag Kumar Dwivedi <[email protected]>
Signed-off-by: Wenchen Fan <[email protected]>
(cherry picked from commit 9357bc9ae05e3848995df2f2f68bb2fa0759e826)
Signed-off-by: Wenchen Fan <[email protected]>
---
.../sql/catalyst/analysis/ResolveSessionCatalog.scala | 4 ++--
.../execution/datasources/v2/DataSourceV2Strategy.scala | 4 ++--
.../command/AlterNamespaceSetLocationSuiteBase.scala | 16 +++++++++-------
.../sql/execution/command/CreateNamespaceSuiteBase.scala | 16 +++++++++-------
4 files changed, 22 insertions(+), 18 deletions(-)
diff --git
a/sql/core/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveSessionCatalog.scala
b/sql/core/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveSessionCatalog.scala
index cfd52707bbc2..b7fa1f422364 100644
---
a/sql/core/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveSessionCatalog.scala
+++
b/sql/core/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveSessionCatalog.scala
@@ -187,7 +187,7 @@ class ResolveSessionCatalog(val catalogManager:
CatalogManager)
AlterDatabasePropertiesCommand(db, properties)
case SetNamespaceLocation(ResolvedV1Database(db), location) if
conf.useV1Command =>
- if (SparkStringUtils.isEmpty(location)) {
+ if (SparkStringUtils.isBlank(location)) {
throw QueryExecutionErrors.invalidEmptyLocationError(location)
}
AlterDatabaseSetLocationCommand(db, location)
@@ -359,7 +359,7 @@ class ResolveSessionCatalog(val catalogManager:
CatalogManager)
val comment = c.properties.get(SupportsNamespaces.PROP_COMMENT)
val location = c.properties.get(SupportsNamespaces.PROP_LOCATION)
val newProperties = c.properties --
CatalogV2Util.NAMESPACE_RESERVED_PROPERTIES
- if (location.isDefined && location.get.isEmpty) {
+ if (location.exists(SparkStringUtils.isBlank)) {
throw QueryExecutionErrors.invalidEmptyLocationError(location.get)
}
CreateDatabaseCommand(name, c.ifNotExists, location, comment,
newProperties)
diff --git
a/sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/v2/DataSourceV2Strategy.scala
b/sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/v2/DataSourceV2Strategy.scala
index 8e40f01601d5..c6661488cae5 100644
---
a/sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/v2/DataSourceV2Strategy.scala
+++
b/sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/v2/DataSourceV2Strategy.scala
@@ -635,7 +635,7 @@ class DataSourceV2Strategy(session: SparkSession) extends
Strategy with Predicat
AlterNamespaceSetPropertiesExec(catalog.asNamespaceCatalog, ns,
properties) :: Nil
case SetNamespaceLocation(ResolvedNamespace(catalog, ns, _), location) =>
- if (SparkStringUtils.isEmpty(location)) {
+ if (SparkStringUtils.isBlank(location)) {
throw QueryExecutionErrors.invalidEmptyLocationError(location)
}
AlterNamespaceSetPropertiesExec(
@@ -651,7 +651,7 @@ class DataSourceV2Strategy(session: SparkSession) extends
Strategy with Predicat
case CreateNamespace(ResolvedNamespace(catalog, ns, _), ifNotExists,
properties) =>
val location = properties.get(SupportsNamespaces.PROP_LOCATION)
- if (location.isDefined && location.get.isEmpty) {
+ if (location.exists(SparkStringUtils.isBlank)) {
throw QueryExecutionErrors.invalidEmptyLocationError(location.get)
}
val finalProperties =
properties.get(SupportsNamespaces.PROP_LOCATION).map { loc =>
diff --git
a/sql/core/src/test/scala/org/apache/spark/sql/execution/command/AlterNamespaceSetLocationSuiteBase.scala
b/sql/core/src/test/scala/org/apache/spark/sql/execution/command/AlterNamespaceSetLocationSuiteBase.scala
index 00484c2efc83..6fde7ea1fc86 100644
---
a/sql/core/src/test/scala/org/apache/spark/sql/execution/command/AlterNamespaceSetLocationSuiteBase.scala
+++
b/sql/core/src/test/scala/org/apache/spark/sql/execution/command/AlterNamespaceSetLocationSuiteBase.scala
@@ -46,13 +46,15 @@ trait AlterNamespaceSetLocationSuiteBase extends QueryTest
with DDLCommandTestUt
val ns = s"$catalog.$namespace"
withNamespace(ns) {
sql(s"CREATE NAMESPACE $ns")
- val sqlText = s"ALTER NAMESPACE $ns SET LOCATION ''"
- checkError(
- exception = intercept[SparkIllegalArgumentException] {
- sql(sqlText)
- },
- condition = "INVALID_EMPTY_LOCATION",
- parameters = Map("location" -> ""))
+ Seq("", " ", "\t", "\n", "\t\n", " \t ").foreach { location =>
+ val sqlText = s"ALTER NAMESPACE $ns SET LOCATION '$location'"
+ checkError(
+ exception = intercept[SparkIllegalArgumentException] {
+ sql(sqlText)
+ },
+ condition = "INVALID_EMPTY_LOCATION",
+ parameters = Map("location" -> location))
+ }
}
}
diff --git
a/sql/core/src/test/scala/org/apache/spark/sql/execution/command/CreateNamespaceSuiteBase.scala
b/sql/core/src/test/scala/org/apache/spark/sql/execution/command/CreateNamespaceSuiteBase.scala
index 9733b104beec..e3558d4bdaa5 100644
---
a/sql/core/src/test/scala/org/apache/spark/sql/execution/command/CreateNamespaceSuiteBase.scala
+++
b/sql/core/src/test/scala/org/apache/spark/sql/execution/command/CreateNamespaceSuiteBase.scala
@@ -69,13 +69,15 @@ trait CreateNamespaceSuiteBase extends QueryTest with
DDLCommandTestUtils {
// The generated temp path is not qualified.
val path = tmpDir.getCanonicalPath
assert(!path.startsWith("file:/"))
- val sqlText = s"CREATE NAMESPACE $ns LOCATION ''"
- checkError(
- exception = intercept[SparkIllegalArgumentException] {
- sql(sqlText)
- },
- condition = "INVALID_EMPTY_LOCATION",
- parameters = Map("location" -> ""))
+ Seq("", " ", "\t", "\n", "\t\n", " \t ").foreach { location =>
+ val sqlText = s"CREATE NAMESPACE $ns LOCATION '$location'"
+ checkError(
+ exception = intercept[SparkIllegalArgumentException] {
+ sql(sqlText)
+ },
+ condition = "INVALID_EMPTY_LOCATION",
+ parameters = Map("location" -> location))
+ }
val uri = new Path(path).toUri
sql(s"CREATE NAMESPACE $ns LOCATION '$uri'")
// Make sure the location is qualified.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]