Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
bb12708
[SPARK-58945][SQL] Fix `messageParameters` keys that do not match the…
subhramit Aug 23, 2026
6cc8eb3
[SPARK-58945][SQL] Supply searchPath for TABLE_OR_VIEW_NOT_FOUND in H…
subhramit Aug 23, 2026
a1ec54a
[SPARK-58945][SQL] Restore the correct message template for `_LEGACY_…
subhramit Aug 23, 2026
327192b
[SPARK-58945][SQL] Inline the SparkThrowable cast to match existing t…
subhramit Aug 23, 2026
970f868
[SPARK-58945][SQL] Test the H2 dialect directly since loadTable is in…
subhramit Aug 23, 2026
ec20e15
SPARK-58945][SQL] Put imports in one line in JDBCTableCatalogSuite
subhramit Aug 23, 2026
7e78270
[SPARK-58945][SQL] Fix CURSOR_OUTSIDE_SCRIPT message parameters
subhramit Aug 31, 2026
6bd5bec
[SPARK-58945][SQL] Add cursor outside script test
subhramit Aug 31, 2026
e37e909
[SPARK-58945][SQL] Reuse empty search path rendering in H2Dialect
subhramit Aug 31, 2026
9a47641
[SPARK-58945][SQL] Use csv in invalid extension errors
subhramit Aug 31, 2026
bdec4c9
[SPARK-58945][SQL] Move error parameter tests to owning suite
subhramit Aug 31, 2026
4faca26
[SPARK-58945][SQL] Fix import order in QueryCompilationErrorsSuite
subhramit Aug 31, 2026
4886995
[SPARK-58945][SQL] Lint: Group imports in QueryCompilationErrorsSuite
subhramit Aug 31, 2026
7e4262a
[SPARK-58945][SQL] Fix H2 rename table-not-found error
subhramit Sep 4, 2026
b524b6e
[SPARK-58945][SQL] Fix semi-structured parse syntax error params
subhramit Sep 4, 2026
2c7626a
[SPARK-58945][SQL] Remove obsolete imports in JDBCTableCatalogSuite
subhramit Sep 4, 2026
856cc11
[SPARK-58945][SQL] Fix semi-structured extract parser test
subhramit Sep 5, 2026
fecb36e
[SPARK-58945][SQL] Assert semi-structured extract parse context
subhramit Sep 5, 2026
c4237cf
[SPARK-58945][SQL] Fix non-foldable interval input error params
subhramit Sep 5, 2026
9059389
[SPARK-58945][SQL] Update interval input error test expectations
subhramit Sep 5, 2026
d2809f8
Retrigger CI
subhramit Sep 5, 2026
a2f3be1
Retrigger CI
subhramit Sep 5, 2026
fe4db40
Retrigger CI again
subhramit Sep 5, 2026
09a8f0d
Merge branch 'master' into SPARK-58945
subhramit Sep 5, 2026
54d369c
Retrigger CI
subhramit Sep 5, 2026
a4bb0ad
[SPARK-58945][SQL] Fix error condition parameters
subhramit Sep 7, 2026
2bde57a
Retrigger CI
subhramit Sep 7, 2026
a66d0f4
Retrigger CI again
subhramit Sep 7, 2026
a63d2a2
Merge branch 'master' into SPARK-58945
subhramit Sep 7, 2026
c175dda
Retrigger CI
subhramit Sep 7, 2026
e09887a
Adjust H2 regression coverage
subhramit Sep 8, 2026
9dae75e
[SPARK-58945][SQL] Fix test
subhramit Sep 8, 2026
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
4 changes: 2 additions & 2 deletions common/utils/src/main/resources/error/error-conditions.json
Original file line number Diff line number Diff line change
Expand Up @@ -1838,7 +1838,7 @@
},
"CURSOR_OUTSIDE_SCRIPT" : {
"message" : [
"Cursor operations can only be used within SQL scripts."
"Cursor <cursorName> can only be used within SQL scripts."
],
"sqlState" : "0A000"
},
Expand Down Expand Up @@ -11818,7 +11818,7 @@
},
"_LEGACY_ERROR_TEMP_3070" : {
"message" : [
"<internalName> is a reserved column name that cannot be read in combination with <colName> column."
"Unrecognized file metadata field: <field>"
Comment thread
subhramit marked this conversation as resolved.

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.

Dropping the template 3070 had copied from 3069 is right, and 3069 keeps the wording. What is left is the condition itself: error/README.md says You should not introduce new uncategorized errors. Instead, convert them to proper errors whenever encountering them in new code., and _LEGACY_ERROR_TEMP_3070 has no sqlState.

Reaching this throw site requires a third-party FileFormat that declares a metadata field without marking it constant or generated, since every built-in format marks them. That makes it an implementation error rather than a user error, so converting it to INTERNAL_ERROR fits better than minting a user-visible condition. Converting means touching the error-conditions.json entry and the new FileSourceCustomMetadataStructSuite case as well; if that widens this PR too far, naming the follow-up JIRA in the description would do, and the other comment about the <field> rendering then follows the same route.

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.

IMO it's better if we keep this PR scoped to fixing the broken existing path and leave reclassification of _LEGACY_ERROR_TEMP_3070 to a follow-up
I can raise a ticket

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.

Reason being, it will be semantic reclassification, not just a broken-rendering fix which I'm dealing with here

]
},
"_LEGACY_ERROR_TEMP_3071" : {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import org.apache.spark.sql.catalyst.util.QuotingUtils.{quoted, quoteIdentifier,
import org.apache.spark.sql.connector.catalog.Identifier
import org.apache.spark.util.ArrayImplicits._

private[analysis] object NoSuchItemExceptionHelper {
private[sql] object NoSuchItemExceptionHelper {

/** Format a search path for TABLE_OR_VIEW_NOT_FOUND (e.g. [`cat`.`ns`]). */
def formatSearchPath(searchPath: Seq[String]): String =
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.{CursorReference, UnresolvedCur
import org.apache.spark.sql.catalyst.plans.logical.LogicalPlan
import org.apache.spark.sql.catalyst.rules.Rule
import org.apache.spark.sql.catalyst.trees.TreePattern.UNRESOLVED_CURSOR
import org.apache.spark.sql.errors.DataTypeErrorsBase
import org.apache.spark.sql.internal.SQLConf

/**
Expand All @@ -35,7 +36,7 @@ import org.apache.spark.sql.internal.SQLConf
* 3. Looks up the cursor definition from the scripting context
* 4. Fails early if cursor is not found
*/
class ResolveCursors extends Rule[LogicalPlan] {
class ResolveCursors extends Rule[LogicalPlan] with DataTypeErrorsBase {

override def apply(plan: LogicalPlan): LogicalPlan = plan.resolveExpressionsWithPruning(
_.containsPattern(UNRESOLVED_CURSOR)) {
Expand Down Expand Up @@ -82,7 +83,7 @@ class ResolveCursors extends Rule[LogicalPlan] {
// Cursors are only allowed within SQL scripts
throw new AnalysisException(
errorClass = "CURSOR_OUTSIDE_SCRIPT",
messageParameters = Map("cursorName" -> nameParts.mkString(".")))
messageParameters = Map("cursorName" -> toSQLId(nameParts)))
Comment thread
subhramit marked this conversation as resolved.
}

// Use the SqlScriptingExecutionContextExtension API for cursor lookup
Expand All @@ -103,7 +104,7 @@ class ResolveCursors extends Rule[LogicalPlan] {
case None =>
throw new AnalysisException(
errorClass = "CURSOR_NOT_FOUND",
messageParameters = Map("cursorName" -> nameParts.mkString(".")))
messageParameters = Map("cursorName" -> toSQLId(nameParts)))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ class CSVOptions(
val extension = {
val ext = parameters.getOrElse(EXTENSION, "csv")
if (ext.size != 3 && !ext.forall(_.isLetter)) {
throw QueryExecutionErrors.invalidFileExtensionError(EXTENSION, ext)
throw QueryExecutionErrors.invalidFileExtensionError("csv", ext)
}

ext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ case class ApproxCountDistinctForIntervals(
errorSubClass = "NON_FOLDABLE_INPUT",
messageParameters = Map(
"inputName" -> toSQLId("endpointsExpression"),
"inputType" -> toSQLType(endpointsExpression.dataType)))
"inputType" -> toSQLType(endpointsExpression.dataType),
"inputExpr" -> toSQLExpr(endpointsExpression)))
} else {
endpointsExpression.dataType match {
case ArrayType(_: NumericType | DateType | TimestampType | TimestampNTZType |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4593,7 +4593,10 @@ class AstBuilder extends DataTypeAstBuilder
val path = if (field.startsWith("[")) "$" + field else s"$$.$field"
val parsedPath = JsonPathParser.parse(path)
if (parsedPath.isEmpty) {
throw new ParseException(errorClass = "PARSE_SYNTAX_ERROR", ctx = ctx)
throw new ParseException(
errorClass = "PARSE_SYNTAX_ERROR",
messageParameters = Map("error" -> s"'$field'", "hint" -> ""),
ctx = ctx)
}
val potentialAlias = parsedPath.get.collect { case Named(name) => name }.lastOption
val node = SemiStructuredExtract(expression(ctx.col), path)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4749,7 +4749,7 @@ private[sql] object QueryCompilationErrors extends QueryErrorsBase with Compilat
def invalidUDFClassError(invalidClass: String): Throwable = {
new InvalidUDFClassException(
errorClass = "_LEGACY_ERROR_TEMP_2450",
messageParameters = Map("invalidClass" -> invalidClass))
messageParameters = Map("clazz" -> invalidClass))
}

def cannotInstantiateHiveFunctionError(clazz: String, e: Throwable): Throwable = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3234,8 +3234,7 @@ private[sql] object QueryExecutionErrors extends QueryErrorsBase with ExecutionE
messageParameters = Map(
"functionName" -> toSQLId(functionName),
"parameter" -> toSQLId("extension"),
"fileExtension" -> toSQLId(extension),
"acceptable" -> "Extension is limited to exactly 3 letters (e.g. csv, tsv, etc...)"))
"invalidValue" -> toSQLId(extension)))
Comment thread
subhramit marked this conversation as resolved.
}

def invalidCharsetError(functionName: String, charset: String): RuntimeException = {
Expand All @@ -3259,7 +3258,7 @@ private[sql] object QueryExecutionErrors extends QueryErrorsBase with ExecutionE
def invalidWriterCommitMessageError(details: String): Throwable = {
new SparkRuntimeException(
errorClass = "INVALID_WRITER_COMMIT_MESSAGE",
messageParameters = Map("details" -> details))
messageParameters = Map("detail" -> details))
}

def codecNotAvailableError(codecName: String, availableCodecs: String): Throwable = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ class ApproxCountDistinctForIntervalsSuite extends SparkFunSuite {
errorSubClass = "NON_FOLDABLE_INPUT",
messageParameters = Map(
"inputName" -> "`endpointsExpression`",
"inputType" -> "\"ARRAY<DOUBLE>\"")))
"inputType" -> "\"ARRAY<DOUBLE>\"",
"inputExpr" -> "\"array(b)\"")))

wrongEndpoints = ApproxCountDistinctForIntervals(
AttributeReference("a", DoubleType)(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,14 @@ class ExpressionParserSuite extends AnalysisTest {
assertEqual("`c`.a[b]", UnresolvedAttribute("c.a").getItem($"b"))
}

test("invalid semi-structured extract path") {
checkError(
exception = parseException("c:['']"),
condition = "PARSE_SYNTAX_ERROR",
parameters = Map("error" -> "'['']'", "hint" -> ""),
queryContext = Array(ExpectedContext("c:['']", 0, 5)))
}

test("parenthesis") {
assertEqual("(a)", $"a")
assertEqual("r * (a + b)", $"r" * ($"a" + $"b"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -398,9 +398,9 @@ class StateStoreColumnFamilyMismatch(
extends SparkUnsupportedOperationException(
errorClass = "STATE_STORE_COLUMN_FAMILY_SCHEMA_INCOMPATIBLE",
messageParameters = Map(
"columnFamilyName" -> columnFamilyName,
"oldColumnFamilySchema" -> oldColumnFamilySchema,
"newColumnFamilySchema" -> newColumnFamilySchema))
"colFamilyName" -> columnFamilyName,
"oldSchema" -> oldColumnFamilySchema,
"newSchema" -> newColumnFamilySchema))

class StatefulProcessorCannotPerformOperationWithInvalidTimeMode(
operationType: String,
Expand Down
21 changes: 16 additions & 5 deletions sql/core/src/main/scala/org/apache/spark/sql/jdbc/H2Dialect.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ import scala.jdk.CollectionConverters._
import scala.util.control.NonFatal

import org.apache.spark.{SparkThrowable, SparkUnsupportedOperationException}
import org.apache.spark.sql.catalyst.analysis.{IndexAlreadyExistsException, NoSuchIndexException, NoSuchNamespaceException, NoSuchTableException, TableAlreadyExistsException}
import org.apache.spark.sql.catalyst.analysis.{
IndexAlreadyExistsException,
NoSuchIndexException,
NoSuchItemExceptionHelper,
NoSuchNamespaceException,
NoSuchTableException,
TableAlreadyExistsException}
import org.apache.spark.sql.connector.catalog.Identifier
import org.apache.spark.sql.connector.catalog.functions.UnboundFunction
import org.apache.spark.sql.connector.catalog.index.TableIndex
Expand Down Expand Up @@ -223,12 +229,17 @@ private[sql] case class H2Dialect() extends JdbcDialect with NoLegacyJDBCError {
errorClass = "TABLE_OR_VIEW_ALREADY_EXISTS",
messageParameters = Map("relationName" -> quotedName),
cause = Some(e))
// TABLE_OR_VIEW_NOT_FOUND_1
case 42102 =>
val relationName = messageParameters.getOrElse("tableName", "")
// TABLE_OR_VIEW_NOT_FOUND_1 and related object-not-found variants.
case 42102 | 42103 | 42104 =>
val relationName = messageParameters
.getOrElse("tableName", messageParameters.getOrElse("oldName", ""))
throw new NoSuchTableException(
errorClass = "TABLE_OR_VIEW_NOT_FOUND",
messageParameters = Map("relationName" -> relationName),
messageParameters = Map(
"relationName" -> relationName,
Comment thread
subhramit marked this conversation as resolved.
// classifyException receives pre-rendered strings, so no resolution
// search path is threaded through this API.
"searchPath" -> NoSuchItemExceptionHelper.formatSearchPath(Seq.empty)),
Comment thread
subhramit marked this conversation as resolved.
cause = Some(e))
// SCHEMA_NOT_FOUND_1
case 90079 =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import org.apache.spark.{SPARK_DOC_ROOT, SparkIllegalArgumentException, SparkUns
import org.apache.spark.sql._
import org.apache.spark.sql.api.java.{UDF1, UDF2, UDF23Test}
import org.apache.spark.sql.catalyst.TableIdentifier
import org.apache.spark.sql.catalyst.catalog.{CatalogStorageFormat, CatalogTable, CatalogTableType}
import org.apache.spark.sql.catalyst.catalog.{CatalogStorageFormat, CatalogTable, CatalogTableType, InvalidUDFClassException}
import org.apache.spark.sql.catalyst.expressions.{Coalesce, Literal, UnsafeRow}
import org.apache.spark.sql.catalyst.parser.ParseException
import org.apache.spark.sql.execution.datasources.SaveIntoDataSourceCommand
Expand Down Expand Up @@ -283,6 +283,14 @@ class QueryCompilationErrorsSuite
sqlState = "0A000")
}

test("SPARK-58945: invalid UDF class error reports clazz") {
checkError(
exception = QueryCompilationErrors.invalidUDFClassError("example.InvalidFunction")
.asInstanceOf[InvalidUDFClassException],
condition = "_LEGACY_ERROR_TEMP_2450",
parameters = Map("clazz" -> "example.InvalidFunction"))
}

test("GROUPING_COLUMN_MISMATCH: not found the grouping column") {
val groupingColMismatchEx = intercept[AnalysisException] {
courseSales.cube("course", "year").agg(grouping("earnings")).explain()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,31 @@ class QueryExecutionErrorsSuite
"CBC", "NoPadding")
}

test("SPARK-58945: invalid file extension reports invalidValue") {
withTempDir { dir =>
val path = new File(dir, "data").getCanonicalPath
// Use a value that is unambiguously invalid so this test focuses on the
// rendered error message rather than the full extension validation matrix.
checkError(
exception = intercept[SparkIllegalArgumentException] {
spark.range(1).write.option("extension", "12").csv(path)
},
condition = "INVALID_PARAMETER_VALUE.EXTENSION",
parameters = Map(
"functionName" -> "`csv`",
"parameter" -> "`extension`",
"invalidValue" -> "`12`"))
}
}

test("SPARK-58945: invalid writer commit message reports detail") {
checkError(
exception = QueryExecutionErrors.invalidWriterCommitMessageError("zero")
.asInstanceOf[SparkRuntimeException],
condition = "INVALID_WRITER_COMMIT_MESSAGE",
parameters = Map("detail" -> "zero"))
}

test("UNSUPPORTED_FEATURE: unsupported types (map and struct) in lit()") {
def checkUnsupportedTypeInLiteral(v: Any, literal: String, dataType: String): Unit = {
checkError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import java.io.File

import org.apache.hadoop.fs.{FileStatus, Path}

import org.apache.spark.sql.Row
import org.apache.spark.sql.{AnalysisException, Row}
import org.apache.spark.sql.catalyst.InternalRow
import org.apache.spark.sql.catalyst.expressions.{Expression, FileSourceConstantMetadataStructField, FileSourceGeneratedMetadataStructField, Literal}
import org.apache.spark.sql.catalyst.util.GenericArrayData
Expand Down Expand Up @@ -413,6 +413,21 @@ class FileSourceCustomMetadataStructSuite extends SharedSparkSession {
Row(1, 112L, 1L, f1.getLen, f1.getPath.getName)))
}
}

test("SPARK-58945: invalid file metadata fields report the field") {
withTempData("parquet", FILE_SCHEMA) { (_, f0, f1) =>
val invalidField = StructField("bad", StringType)
val format = new TestFileFormat(Seq(invalidField))
val df = createDF(format, Seq(FileStatusWithMetadata(f0), FileStatusWithMetadata(f1)))

checkError(
exception = intercept[AnalysisException] {
df.select("_metadata.bad").collect()
},
condition = "_LEGACY_ERROR_TEMP_3070",
parameters = Map("field" -> invalidField.toString))
Comment thread
subhramit marked this conversation as resolved.
}
}
}

object FileSourceCustomMetadataStructSuite {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ import org.apache.logging.log4j.Level

import org.apache.spark.{SparkConf, SparkIllegalArgumentException, SparkRuntimeException}
import org.apache.spark.sql.{AnalysisException, Row}
import org.apache.spark.sql.catalyst.analysis.{NoSuchNamespaceException, TableAlreadyExistsException}
import org.apache.spark.sql.catalyst.analysis.{NoSuchNamespaceException, NoSuchTableException, TableAlreadyExistsException}
import org.apache.spark.sql.catalyst.parser.ParseException
import org.apache.spark.sql.catalyst.util.CharVarcharUtils
import org.apache.spark.sql.connector.catalog.{Identifier, TableSummary}
import org.apache.spark.sql.connector.catalog.{Identifier, TableChange, TableSummary}
import org.apache.spark.sql.errors.DataTypeErrors.{toSQLConf, toSQLStmt}
import org.apache.spark.sql.execution.columnar.InMemoryTableScanExec
import org.apache.spark.sql.internal.SQLConf
Expand Down Expand Up @@ -229,6 +229,33 @@ class JDBCTableCatalogSuite extends SharedSparkSession {
}
}

test("SPARK-58945: H2 renameTable reports source table when it is missing") {
val e = intercept[NoSuchTableException] {
tableCatalog.renameTable(
Identifier.of(Array("test"), "not_existing_table"),
Identifier.of(Array("test"), "dst_table"))
}
checkErrorTableNotFoundWithSearchPath(
e,
"`test`.`not_existing_table`",
searchPath = "not available")
}

// `test`.`people` exists, so H2 reports `test`.`PEOPLE` as not found with candidates (42103).
// Only the parser's not-found path returns 42103. `renameTable` resolves the table in
// `AlterTable.update()`, which always returns 42102, so `alterTable` is needed here.
test("SPARK-58945: H2 alterTable reports source table when it is missing") {
val e = intercept[NoSuchTableException] {
tableCatalog.alterTable(
Identifier.of(Array("test"), "PEOPLE"),
TableChange.deleteColumn(Array("id"), false))
}
checkErrorTableNotFoundWithSearchPath(
e,
"`test`.`PEOPLE`",
searchPath = "not available")
}

test("create a table") {
withTable("h2.test.new_table") {
sql("CREATE TABLE h2.test.new_table(i INT, j STRING)")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.spark.sql.execution.streaming.state

import org.apache.spark.SparkFunSuite

class StateStoreErrorsSuite extends SparkFunSuite {

test("SPARK-58945: state store mismatch reports schema details") {
checkError(
exception = StateStoreErrors.stateStoreColumnFamilyMismatch(
"state", "old_schema", "new_schema"),
condition = "STATE_STORE_COLUMN_FAMILY_SCHEMA_INCOMPATIBLE",
parameters = Map(
"colFamilyName" -> "state",
"oldSchema" -> "old_schema",
"newSchema" -> "new_schema"))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ class SqlScriptingCursorE2eSuite extends SharedSparkSession {
|""".stripMargin)
},
condition = "CURSOR_NOT_FOUND",
parameters = Map("cursorName" -> "cur")
parameters = Map("cursorName" -> "`cur`")
)
}

Expand Down Expand Up @@ -2083,5 +2083,22 @@ class SqlScriptingCursorE2eSuite extends SharedSparkSession {
}
}

test("SPARK-58945: OPEN cursor outside script reports cursor name") {
checkError(
exception = intercept[AnalysisException] {
sql("OPEN cur")
},
condition = "CURSOR_OUTSIDE_SCRIPT",
parameters = Map("cursorName" -> "`cur`"))
}

test("SPARK-58945: DECLARE CURSOR outside script reports cursor name") {
checkError(
exception = intercept[AnalysisException] {
sql("DECLARE cur CURSOR FOR SELECT 1")
},
condition = "CURSOR_OUTSIDE_SCRIPT",
parameters = Map("cursorName" -> "`cur`"))
}
}
// scalastyle:on line.size.limit