This is an automated email from the ASF dual-hosted git repository.

voonhous pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hudi.git


The following commit(s) were added to refs/heads/master by this push:
     new f8c6dd8f0265 chore(spark): prune dead code in the six extended SQL AST 
builders (#19455)
f8c6dd8f0265 is described below

commit f8c6dd8f02655856c7b0317615eb8a03f3ea8ade
Author: voonhous <[email protected]>
AuthorDate: Sun Aug 2 18:40:27 2026 +0800

    chore(spark): prune dead code in the six extended SQL AST builders (#19455)
    
    * chore(spark): prune dead code in the six extended SQL AST builders
    
    #19132 pruned the forked parser to Hudi-routed statements; remove the code
    that became unreachable in the six HoodieSpark*ExtendedSqlAstBuilders
    (3.3/3.4/3.5/4.0/4.1/4.2):
    
    - uncalled methods: createSchema, createStructType and both
      visitPropertyKeys overloads (scaladoc references updated)
    - visitBooleanLiteral: unreachable from SQL in both keyword modes.
      TRUE/FALSE are ansiNonReserved, so a bare true/false in
      transform-argument position always parses as a column reference, and
      OPTIONS/TBLPROPERTIES boolean values go through getText without
      visiting the constant. visitNullLiteral stays: under ANSI keyword mode
      f(null, id) parses as a null literal.
    - branches the grammar cannot produce: getSingleFieldReference's
      empty-arguments arm (the transform rule requires at least one
      argument), visitTransformArgument's getOrElse(throw ...) (a
      transformArgument is always qualifiedName or constant),
      visitCreateFileFormat's trailing case _ (STORED AS and STORED BY are
      mutually exclusive grammar alternatives)
    - unused imports, including ParserUtils.entry which is shadowed by an
      identical local def in visitRowFormatDelimited
    
    Deliberately kept, deviating from the issue: the case _ arm of
    validateRowFormatFileFormat. It is reachable, e.g. via CREATE TABLE t
    (b BLOB) ROW FORMAT SERDE 'x' STORED BY 'y': rowFormat and
    createFileFormat coexist in createTableClauses, STORED BY leaves
    createFileFormatCtx.fileFormat null, and (rowFormatCtx, null) falls past
    the three typed arms. Removing it would turn a clean ParseException into
    a scala.MatchError.
    
    Fixes #19451
    
    * fix(spark): restore visitBooleanLiteral and address review findings on 
the pruning
    
    Review of the pruning commit found #19451's premise wrong for one deletion
    and surfaced several smaller cleanups:
    
    - Restore visitBooleanLiteral in all six builders. The issue claimed
      TRUE/FALSE are both ansiNonReserved; only TRUE is. Under ANSI keyword
      mode (SQL_standard_keyword_behavior = conf.ansiEnabled, the Spark 4.x
      default) a bare false in transform-argument position parses as a
      BooleanLiteralContext, and without the visitor the generated base
      visitor returns null and LiteralValue(lit.value, ...) throws a raw NPE
      that escapes parsePlan (only ParseException/AnalysisException are
      caught). The scaladoc now records the reachability constraint, the
      wrong comment in TestBlobDataType is corrected, and an ANSI-mode
      regression test asserts false yields a BooleanType literal while true
      stays a column reference.
    - Revert visitTransformArgument's .get to getOrElse(throw ParseException)
      so a hypothetical future grammar change fails as a parse error instead
      of a NoSuchElementException.
    - Replace the stale 'should never happen' comment on
      validateRowFormatFileFormat's case _ arm with the real reachability
      explanation (ROW FORMAT ... STORED BY), and cover that arm plus the
      (_, TableFileFormatContext) arm with tests.
    - Align the visitCreateTable scaladoc drift #19132 left behind: the
      CreateTableAsSelect and AS-select_statement lines in 3.3/3.5 and the
      AS-select_statement lines in 4.1.
    - Drop the BlobType import that the types._ wildcard already supplies
      (3.3/3.4/3.5/4.0), matching 4.1/4.2.
    - Fix a 6-vs-4-space indent drift in the 3.3 copy.
    
    Verified: reactor compiles for spark3.3/3.4/3.5, CI-style scala-2.13
    builds for spark4.0/4.1/4.2, and the TestBlobDataType suite (10/10).
---
 .../sql/hudi/dml/schema/TestBlobDataType.scala     | 39 ++++++++--
 .../HoodieSpark3_3ExtendedSqlAstBuilder.scala      | 78 +++++---------------
 .../HoodieSpark3_4ExtendedSqlAstBuilder.scala      | 75 +++++---------------
 .../HoodieSpark3_5ExtendedSqlAstBuilder.scala      | 82 +++++-----------------
 .../HoodieSpark4_0ExtendedSqlAstBuilder.scala      | 77 +++++---------------
 .../HoodieSpark4_1ExtendedSqlAstBuilder.scala      | 79 +++++----------------
 .../HoodieSpark4_2ExtendedSqlAstBuilder.scala      | 76 +++++---------------
 7 files changed, 138 insertions(+), 368 deletions(-)

diff --git 
a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/dml/schema/TestBlobDataType.scala
 
b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/dml/schema/TestBlobDataType.scala
index 4eae30286ba9..42d7bfcfba68 100644
--- 
a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/dml/schema/TestBlobDataType.scala
+++ 
b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/dml/schema/TestBlobDataType.scala
@@ -454,11 +454,10 @@ class TestBlobDataType extends HoodieSparkSqlTestBase 
with ExtendedParserTestHel
     // to #19449: the extended parser enables ANSI reserved-keyword 
enforcement whenever
     // spark.sql.ansi.enabled is set (the Spark 4.x default) instead of 
following Spark's
     // spark.sql.ansi.enforceReservedKeywords, which makes a bare TIMESTAMP 
token unparseable
-    // there (a bug, not a Spark 4 constraint). A bare true/false in this 
position parses as a
-    // column reference in BOTH keyword modes (TRUE/FALSE are ansiNonReserved, 
so qualifiedName
-    // wins), leaving visitBooleanLiteral unreachable (#19451); null is a 
column reference only
-    // under the default non-ANSI keyword mode and a null literal under ANSI, 
so visitNullLiteral
-    // is mode-dependent and both are left unasserted here.
+    // there (a bug, not a Spark 4 constraint). Boolean and null literals are 
keyword-mode
+    // dependent: only TRUE is ansiNonReserved, so a bare true always parses 
as a column
+    // reference, while false and null are column references under the default 
non-ANSI keyword
+    // mode but typed literals under ANSI mode (both cases are asserted below).
     val plan = parseCreateTable(
       s"""
          |CREATE TABLE blob_lit_tbl (
@@ -485,6 +484,21 @@ class TestBlobDataType extends HoodieSparkSqlTestBase with 
ExtendedParserTestHel
       firstLiteralArg(transformByName(plan, "mu_ivl_t")).dataType)
     assertResult(YearMonthIntervalType(YearMonthIntervalType.YEAR, 
YearMonthIntervalType.MONTH))(
       firstLiteralArg(transformByName(plan, "uu_ivl_t")).dataType)
+
+    // Under ANSI keyword mode a bare false and a bare null must survive 
visitBooleanLiteral
+    // and visitNullLiteral as typed literals, while a bare true stays a 
column reference
+    // (TRUE is ansiNonReserved; FALSE and NULL are not).
+    withSQLConf("spark.sql.ansi.enabled" -> "true") {
+      val ansiPlan = parseCreateTable(
+        "CREATE TABLE blob_bool_tbl (id BIGINT, data BLOB) USING hudi " +
+          "PARTITIONED BY (bool_t(false, id), true_t(true, id), null_t(null, 
id))")
+      assertResult(BooleanType)(firstLiteralArg(transformByName(ansiPlan, 
"bool_t")).dataType)
+      assertResult(false)(firstLiteralArg(transformByName(ansiPlan, 
"bool_t")).value)
+      assertResult(NullType)(firstLiteralArg(transformByName(ansiPlan, 
"null_t")).dataType)
+      assertResult(null)(firstLiteralArg(transformByName(ansiPlan, 
"null_t")).value)
+      assertResult(Seq(Seq("true"), Seq("id")))(
+        transformFieldRefs(transformByName(ansiPlan, "true_t")))
+    }
   }
 
   test("Test parse CREATE TABLE with BLOB column and invalid partition 
transforms") {
@@ -539,6 +553,14 @@ class TestBlobDataType extends HoodieSparkSqlTestBase with 
ExtendedParserTestHel
       "ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' STORED AS TEXTFILE")
     assertResult(Some("TEXTFILE"))(ff6.tableSpec.serde.get.storedAs)
     assertResult(",")(ff6.tableSpec.serde.get.serdeProperties("field.delim"))
+    // Any ROW FORMAT combined with STORED AS INPUTFORMAT/OUTPUTFORMAT is 
accepted (the
+    // table-file-format arm of validateRowFormatFileFormat).
+    val ff7 = parseCreateTable("CREATE TABLE blob_ff7 (id BIGINT, data BLOB) " 
+
+      "ROW FORMAT SERDE 'com.example.Serde' " +
+      "STORED AS INPUTFORMAT 'com.example.InFmt' OUTPUTFORMAT 
'com.example.OutFmt'")
+    assertResult(Some("com.example.Serde"))(ff7.tableSpec.serde.get.serde)
+    assertResult(Some(FormatClasses("com.example.InFmt", 
"com.example.OutFmt")))(
+      ff7.tableSpec.serde.get.formatClasses)
 
     // ROW FORMAT DELIMITED with a non-text file format is rejected.
     checkExceptionContain(
@@ -556,6 +578,13 @@ class TestBlobDataType extends HoodieSparkSqlTestBase with 
ExtendedParserTestHel
     checkExceptionContain(
       "CREATE TABLE blob_ferr3 (id BIGINT, data BLOB) STORED BY 
'com.example.Handler'")(
       "Operation not allowed: STORED BY")
+    // ROW FORMAT combined with STORED BY leaves no file format for the row 
format to pair with;
+    // validateRowFormatFileFormat's catch-all arm rejects the combination 
before the STORED BY
+    // error can fire.
+    checkExceptionContain(
+      "CREATE TABLE blob_ferr5 (id BIGINT, data BLOB) " +
+        "ROW FORMAT SERDE 'com.example.Serde' STORED BY 
'com.example.Handler'")(
+      "Unexpected combination of")
     // A USING provider combined with a serde clause is not allowed.
     checkExceptionContain(
       "CREATE TABLE blob_ferr4 (id BIGINT, data BLOB) USING hudi STORED AS 
PARQUET")(
diff --git 
a/hudi-spark-datasource/hudi-spark3.3.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark3_3ExtendedSqlAstBuilder.scala
 
b/hudi-spark-datasource/hudi-spark3.3.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark3_3ExtendedSqlAstBuilder.scala
index 935bb438151e..06a51d02bf24 100644
--- 
a/hudi-spark-datasource/hudi-spark3.3.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark3_3ExtendedSqlAstBuilder.scala
+++ 
b/hudi-spark-datasource/hudi-spark3.3.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark3_3ExtendedSqlAstBuilder.scala
@@ -21,26 +21,24 @@ import 
org.apache.hudi.spark.sql.parser.{HoodieSqlBaseBaseVisitor, HoodieSqlBase
 import org.apache.hudi.spark.sql.parser.HoodieSqlBaseParser._
 
 import org.antlr.v4.runtime.{ParserRuleContext, Token}
-import org.antlr.v4.runtime.tree.{ParseTree, RuleNode, TerminalNode}
+import org.antlr.v4.runtime.tree.{ParseTree, RuleNode}
 import org.apache.spark.internal.Logging
 import org.apache.spark.sql.AnalysisException
-import org.apache.spark.sql.catalyst.{FunctionIdentifier, TableIdentifier}
+import org.apache.spark.sql.catalyst.TableIdentifier
 import org.apache.spark.sql.catalyst.analysis._
-import org.apache.spark.sql.catalyst.catalog.{BucketSpec, CatalogStorageFormat}
+import org.apache.spark.sql.catalyst.catalog.BucketSpec
 import org.apache.spark.sql.catalyst.expressions._
-import org.apache.spark.sql.catalyst.expressions.aggregate.{First, Last}
 import org.apache.spark.sql.catalyst.parser.{ParseException, ParserInterface}
-import 
org.apache.spark.sql.catalyst.parser.ParserUtils.{checkDuplicateClauses, 
checkDuplicateKeys, entry, escapedIdentifier, operationNotAllowed, source, 
string, stringWithoutUnescape, validate, withOrigin, EnhancedLogicalPlan}
+import 
org.apache.spark.sql.catalyst.parser.ParserUtils.{checkDuplicateClauses, 
checkDuplicateKeys, operationNotAllowed, source, string, stringWithoutUnescape, 
validate, withOrigin}
 import org.apache.spark.sql.catalyst.plans._
 import org.apache.spark.sql.catalyst.plans.logical._
-import org.apache.spark.sql.catalyst.util.{truncatedString, CharVarcharUtils, 
DateTimeUtils, IntervalUtils}
+import org.apache.spark.sql.catalyst.util.{DateTimeUtils, IntervalUtils}
 import org.apache.spark.sql.catalyst.util.DateTimeUtils._
 import 
org.apache.spark.sql.connector.catalog.CatalogV2Implicits.BucketSpecHelper
 import org.apache.spark.sql.connector.catalog.TableCatalog
 import org.apache.spark.sql.connector.expressions.{ApplyTransform, 
BucketTransform, DaysTransform, Expression => V2Expression, FieldReference, 
HoursTransform, IdentityTransform, LiteralValue, MonthsTransform, Transform, 
YearsTransform}
 import org.apache.spark.sql.internal.SQLConf
 import org.apache.spark.sql.types._
-import org.apache.spark.sql.types.BlobType
 import org.apache.spark.unsafe.types.{CalendarInterval, UTF8String}
 import org.apache.spark.util.Utils.isTesting
 
@@ -208,14 +206,18 @@ class HoodieSpark3_3ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
   }
 
   /**
-   * Create a NULL literal expression.
+   * Create a NULL literal expression. Reachable under ANSI keyword mode, 
where NULL (like
+   * FALSE) is a reserved word, so f(null, id) in transform-argument position 
parses as a
+   * null literal instead of a column reference.
    */
   override def visitNullLiteral(ctx: NullLiteralContext): Literal = 
withOrigin(ctx) {
     Literal(null)
   }
 
   /**
-   * Create a Boolean literal expression.
+   * Create a Boolean literal expression. Reachable under ANSI keyword mode, 
where FALSE is a
+   * reserved word (only TRUE is ansiNonReserved), so a bare false in 
transform-argument
+   * position parses as a boolean literal instead of a column reference.
    */
   override def visitBooleanLiteral(ctx: BooleanLiteralContext): Literal = 
withOrigin(ctx) {
     if (ctx.getText.toBoolean) {
@@ -642,13 +644,6 @@ class HoodieSpark3_3ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     }
   }
 
-  /**
-   * Create top level table schema.
-   */
-  protected def createSchema(ctx: ColTypeListContext): StructType = {
-    StructType(Option(ctx).toSeq.flatMap(visitColTypeList))
-  }
-
   /**
    * Create a [[StructType]] from a number of column definitions.
    */
@@ -691,13 +686,6 @@ class HoodieSpark3_3ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     }
   }
 
-  /**
-   * Create a [[StructType]] from a sequence of [[StructField]]s.
-   */
-  protected def createStructType(ctx: ComplexColTypeListContext): StructType = 
{
-    StructType(Option(ctx).toSeq.flatMap(visitComplexColTypeList))
-  }
-
   /**
    * Create a [[StructType]] from a number of column definitions.
    */
@@ -776,7 +764,7 @@ class HoodieSpark3_3ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
 
   /**
    * Convert a table property list into a key-value map.
-   * This should be called through [[visitPropertyKeyValues]] or 
[[visitPropertyKeys]].
+   * This should be called through [[visitPropertyKeyValues]].
    */
   override def visitTablePropertyList(
                                        ctx: TablePropertyListContext): 
Map[String, String] = withOrigin(ctx) {
@@ -803,19 +791,6 @@ class HoodieSpark3_3ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     props
   }
 
-  /**
-   * Parse a list of keys from a [[TablePropertyListContext]], assuming no 
values are specified.
-   */
-  def visitPropertyKeys(ctx: TablePropertyListContext): Seq[String] = {
-    val props = visitTablePropertyList(ctx)
-    val badKeys = props.filter { case (_, v) => v != null }.keys
-    if (badKeys.nonEmpty) {
-      operationNotAllowed(
-        s"Values should not be specified for key(s): ${badKeys.mkString("[", 
",", "]")}", ctx)
-    }
-    props.keys.toSeq
-  }
-
   /**
    * A table property key can either be String or a collection of dot 
separated elements. This
    * function extracts the property key based on whether its a string literal 
or a table property
@@ -925,10 +900,6 @@ class HoodieSpark3_3ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
       lazy val name: String = ctx.identifier.getText
       if (arguments.size > 1) {
         throw new ParseException(s"Too many arguments for transform $name", 
ctx)
-      } else if (arguments.isEmpty) {
-        throw
-
-          new ParseException(s"Not enough arguments for transform $name", ctx)
       } else {
         getFieldReference(ctx, arguments.head)
       }
@@ -1044,8 +1015,6 @@ class HoodieSpark3_3ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
         SerdeInfo(storedAs = Some(c.identifier.getText))
       case (null, storageHandler) =>
         operationNotAllowed("STORED BY", ctx)
-      case _ =>
-        throw new ParseException("Expected either STORED AS or STORED BY, not 
both", ctx)
     }
   }
 
@@ -1144,7 +1113,8 @@ class HoodieSpark3_3ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
               s"ROW FORMAT DELIMITED is only compatible with 'textfile', not 
'$fmt'", parentCtx)
           }
         case _ =>
-          // should never happen
+          // Reachable: ROW FORMAT ... STORED BY 'handler' leaves 
createFileFormatCtx.fileFormat
+          // null, so none of the typed arms above match.
           def str(ctx: ParserRuleContext): String = {
             (0 until ctx.getChildCount).map { i => ctx.getChild(i).getText 
}.mkString(" ")
           }
@@ -1230,14 +1200,13 @@ class HoodieSpark3_3ExtendedSqlAstBuilder(conf: 
SQLConf, delegate: ParserInterfa
   }
 
   /**
-   * Create a table, returning a [[CreateTable]] or [[CreateTableAsSelect]] 
logical plan.
+   * Create a table, returning a [[CreateTable]] logical plan.
    *
    * Expected format:
    * {{{
    *   CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db_name.]table_name
    *   [USING table_provider]
-   *   create_table_clauses
-   *   [[AS] select_statement];
+   *   create_table_clauses;
    *
    *   create_table_clauses (order insensitive):
    *     [PARTITIONED BY (partition_fields)]
@@ -1360,7 +1329,7 @@ class HoodieSpark3_3ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
 
   /**
    * Convert a property list into a key-value map.
-   * This should be called through [[visitPropertyKeyValues]] or 
[[visitPropertyKeys]].
+   * This should be called through [[visitPropertyKeyValues]].
    */
   override def visitPropertyList(ctx: PropertyListContext): Map[String, 
String] = withOrigin(ctx) {
     val properties = ctx.property.asScala.map { property =>
@@ -1386,19 +1355,6 @@ class HoodieSpark3_3ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     props
   }
 
-  /**
-   * Parse a list of keys from a [[PropertyListContext]], assuming no values 
are specified.
-   */
-  def visitPropertyKeys(ctx: PropertyListContext): Seq[String] = {
-    val props = visitPropertyList(ctx)
-    val badKeys = props.filter { case (_, v) => v != null }.keys
-    if (badKeys.nonEmpty) {
-      operationNotAllowed(
-        s"Values should not be specified for key(s): ${badKeys.mkString("[", 
",", "]")}", ctx)
-    }
-    props.keys.toSeq
-  }
-
   /**
    * A property key can either be String or a collection of dot separated 
elements. This
    * function extracts the property key based on whether its a string literal 
or a property
diff --git 
a/hudi-spark-datasource/hudi-spark3.4.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark3_4ExtendedSqlAstBuilder.scala
 
b/hudi-spark-datasource/hudi-spark3.4.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark3_4ExtendedSqlAstBuilder.scala
index cc49e8983d1f..695cf3fe1462 100644
--- 
a/hudi-spark-datasource/hudi-spark3.4.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark3_4ExtendedSqlAstBuilder.scala
+++ 
b/hudi-spark-datasource/hudi-spark3.4.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark3_4ExtendedSqlAstBuilder.scala
@@ -21,26 +21,24 @@ import 
org.apache.hudi.spark.sql.parser.{HoodieSqlBaseBaseVisitor, HoodieSqlBase
 import org.apache.hudi.spark.sql.parser.HoodieSqlBaseParser._
 
 import org.antlr.v4.runtime.{ParserRuleContext, Token}
-import org.antlr.v4.runtime.tree.{ParseTree, RuleNode, TerminalNode}
+import org.antlr.v4.runtime.tree.{ParseTree, RuleNode}
 import org.apache.spark.internal.Logging
 import org.apache.spark.sql.AnalysisException
-import org.apache.spark.sql.catalyst.{FunctionIdentifier, TableIdentifier}
+import org.apache.spark.sql.catalyst.TableIdentifier
 import org.apache.spark.sql.catalyst.analysis._
-import org.apache.spark.sql.catalyst.catalog.{BucketSpec, CatalogStorageFormat}
+import org.apache.spark.sql.catalyst.catalog.BucketSpec
 import org.apache.spark.sql.catalyst.expressions._
-import org.apache.spark.sql.catalyst.expressions.aggregate.{First, Last}
 import org.apache.spark.sql.catalyst.parser.{ParseException, ParserInterface}
-import 
org.apache.spark.sql.catalyst.parser.ParserUtils.{checkDuplicateClauses, 
checkDuplicateKeys, entry, escapedIdentifier, operationNotAllowed, source, 
string, stringWithoutUnescape, validate, withOrigin, EnhancedLogicalPlan}
+import 
org.apache.spark.sql.catalyst.parser.ParserUtils.{checkDuplicateClauses, 
checkDuplicateKeys, operationNotAllowed, source, string, stringWithoutUnescape, 
validate, withOrigin}
 import org.apache.spark.sql.catalyst.plans._
 import org.apache.spark.sql.catalyst.plans.logical._
-import org.apache.spark.sql.catalyst.util.{truncatedString, CharVarcharUtils, 
DateTimeUtils, IntervalUtils}
+import org.apache.spark.sql.catalyst.util.{DateTimeUtils, IntervalUtils}
 import org.apache.spark.sql.catalyst.util.DateTimeUtils._
 import 
org.apache.spark.sql.connector.catalog.CatalogV2Implicits.BucketSpecHelper
 import org.apache.spark.sql.connector.catalog.TableCatalog
 import org.apache.spark.sql.connector.expressions.{ApplyTransform, 
BucketTransform, DaysTransform, Expression => V2Expression, FieldReference, 
HoursTransform, IdentityTransform, LiteralValue, MonthsTransform, Transform, 
YearsTransform}
 import org.apache.spark.sql.internal.SQLConf
 import org.apache.spark.sql.types._
-import org.apache.spark.sql.types.BlobType
 import org.apache.spark.unsafe.types.{CalendarInterval, UTF8String}
 import org.apache.spark.util.Utils.isTesting
 
@@ -208,14 +206,18 @@ class HoodieSpark3_4ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
   }
 
   /**
-   * Create a NULL literal expression.
+   * Create a NULL literal expression. Reachable under ANSI keyword mode, 
where NULL (like
+   * FALSE) is a reserved word, so f(null, id) in transform-argument position 
parses as a
+   * null literal instead of a column reference.
    */
   override def visitNullLiteral(ctx: NullLiteralContext): Literal = 
withOrigin(ctx) {
     Literal(null)
   }
 
   /**
-   * Create a Boolean literal expression.
+   * Create a Boolean literal expression. Reachable under ANSI keyword mode, 
where FALSE is a
+   * reserved word (only TRUE is ansiNonReserved), so a bare false in 
transform-argument
+   * position parses as a boolean literal instead of a column reference.
    */
   override def visitBooleanLiteral(ctx: BooleanLiteralContext): Literal = 
withOrigin(ctx) {
     if (ctx.getText.toBoolean) {
@@ -642,13 +644,6 @@ class HoodieSpark3_4ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     }
   }
 
-  /**
-   * Create top level table schema.
-   */
-  protected def createSchema(ctx: ColTypeListContext): StructType = {
-    StructType(Option(ctx).toSeq.flatMap(visitColTypeList))
-  }
-
   /**
    * Create a [[StructType]] from a number of column definitions.
    */
@@ -691,13 +686,6 @@ class HoodieSpark3_4ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     }
   }
 
-  /**
-   * Create a [[StructType]] from a sequence of [[StructField]]s.
-   */
-  protected def createStructType(ctx: ComplexColTypeListContext): StructType = 
{
-    StructType(Option(ctx).toSeq.flatMap(visitComplexColTypeList))
-  }
-
   /**
    * Create a [[StructType]] from a number of column definitions.
    */
@@ -776,7 +764,7 @@ class HoodieSpark3_4ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
 
   /**
    * Convert a table property list into a key-value map.
-   * This should be called through [[visitPropertyKeyValues]] or 
[[visitPropertyKeys]].
+   * This should be called through [[visitPropertyKeyValues]].
    */
   override def visitTablePropertyList(
                                        ctx: TablePropertyListContext): 
Map[String, String] = withOrigin(ctx) {
@@ -803,19 +791,6 @@ class HoodieSpark3_4ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     props
   }
 
-  /**
-   * Parse a list of keys from a [[TablePropertyListContext]], assuming no 
values are specified.
-   */
-  def visitPropertyKeys(ctx: TablePropertyListContext): Seq[String] = {
-    val props = visitTablePropertyList(ctx)
-    val badKeys = props.filter { case (_, v) => v != null }.keys
-    if (badKeys.nonEmpty) {
-      operationNotAllowed(
-        s"Values should not be specified for key(s): ${badKeys.mkString("[", 
",", "]")}", ctx)
-    }
-    props.keys.toSeq
-  }
-
   /**
    * A table property key can either be String or a collection of dot 
separated elements. This
    * function extracts the property key based on whether its a string literal 
or a table property
@@ -925,10 +900,6 @@ class HoodieSpark3_4ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
       lazy val name: String = ctx.identifier.getText
       if (arguments.size > 1) {
         throw new ParseException(s"Too many arguments for transform $name", 
ctx)
-      } else if (arguments.isEmpty) {
-        throw
-
-          new ParseException(s"Not enough arguments for transform $name", ctx)
       } else {
         getFieldReference(ctx, arguments.head)
       }
@@ -1044,8 +1015,6 @@ class HoodieSpark3_4ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
         SerdeInfo(storedAs = Some(c.identifier.getText))
       case (null, storageHandler) =>
         operationNotAllowed("STORED BY", ctx)
-      case _ =>
-        throw new ParseException("Expected either STORED AS or STORED BY, not 
both", ctx)
     }
   }
 
@@ -1144,7 +1113,8 @@ class HoodieSpark3_4ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
               s"ROW FORMAT DELIMITED is only compatible with 'textfile', not 
'$fmt'", parentCtx)
           }
         case _ =>
-          // should never happen
+          // Reachable: ROW FORMAT ... STORED BY 'handler' leaves 
createFileFormatCtx.fileFormat
+          // null, so none of the typed arms above match.
           def str(ctx: ParserRuleContext): String = {
             (0 until ctx.getChildCount).map { i => ctx.getChild(i).getText 
}.mkString(" ")
           }
@@ -1276,7 +1246,7 @@ class HoodieSpark3_4ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     // partition transforms for BucketSpec was moved inside parser
     // https://issues.apache.org/jira/browse/SPARK-37923
     val partitioning =
-    partitionExpressions(partTransforms, partCols, ctx) ++ 
bucketSpec.map(_.asTransform)
+      partitionExpressions(partTransforms, partCols, ctx) ++ 
bucketSpec.map(_.asTransform)
     val tableSpec = TableSpec(properties, provider, options, location, comment,
       serdeInfo, external)
 
@@ -1359,7 +1329,7 @@ class HoodieSpark3_4ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
 
   /**
    * Convert a property list into a key-value map.
-   * This should be called through [[visitPropertyKeyValues]] or 
[[visitPropertyKeys]].
+   * This should be called through [[visitPropertyKeyValues]].
    */
   override def visitPropertyList(ctx: PropertyListContext): Map[String, 
String] = withOrigin(ctx) {
     val properties = ctx.property.asScala.map { property =>
@@ -1385,19 +1355,6 @@ class HoodieSpark3_4ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     props
   }
 
-  /**
-   * Parse a list of keys from a [[PropertyListContext]], assuming no values 
are specified.
-   */
-  def visitPropertyKeys(ctx: PropertyListContext): Seq[String] = {
-    val props = visitPropertyList(ctx)
-    val badKeys = props.filter { case (_, v) => v != null }.keys
-    if (badKeys.nonEmpty) {
-      operationNotAllowed(
-        s"Values should not be specified for key(s): ${badKeys.mkString("[", 
",", "]")}", ctx)
-    }
-    props.keys.toSeq
-  }
-
   /**
    * A property key can either be String or a collection of dot separated 
elements. This
    * function extracts the property key based on whether its a string literal 
or a property
diff --git 
a/hudi-spark-datasource/hudi-spark3.5.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark3_5ExtendedSqlAstBuilder.scala
 
b/hudi-spark-datasource/hudi-spark3.5.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark3_5ExtendedSqlAstBuilder.scala
index b837d548240a..1cfa82c02136 100644
--- 
a/hudi-spark-datasource/hudi-spark3.5.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark3_5ExtendedSqlAstBuilder.scala
+++ 
b/hudi-spark-datasource/hudi-spark3.5.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark3_5ExtendedSqlAstBuilder.scala
@@ -21,26 +21,24 @@ import 
org.apache.hudi.spark.sql.parser.{HoodieSqlBaseBaseVisitor, HoodieSqlBase
 import org.apache.hudi.spark.sql.parser.HoodieSqlBaseParser._
 
 import org.antlr.v4.runtime.{ParserRuleContext, Token}
-import org.antlr.v4.runtime.tree.{ParseTree, RuleNode, TerminalNode}
+import org.antlr.v4.runtime.tree.{ParseTree, RuleNode}
 import org.apache.spark.internal.Logging
 import org.apache.spark.sql.AnalysisException
-import org.apache.spark.sql.catalyst.{FunctionIdentifier, TableIdentifier}
+import org.apache.spark.sql.catalyst.TableIdentifier
 import org.apache.spark.sql.catalyst.analysis._
-import org.apache.spark.sql.catalyst.catalog.{BucketSpec, CatalogStorageFormat}
+import org.apache.spark.sql.catalyst.catalog.BucketSpec
 import org.apache.spark.sql.catalyst.expressions._
-import org.apache.spark.sql.catalyst.expressions.aggregate.{First, Last}
-import org.apache.spark.sql.catalyst.parser.{EnhancedLogicalPlan, 
ParseException, ParserInterface}
-import 
org.apache.spark.sql.catalyst.parser.ParserUtils.{checkDuplicateClauses, 
checkDuplicateKeys, entry, escapedIdentifier, operationNotAllowed, source, 
string, stringWithoutUnescape, validate, withOrigin}
+import org.apache.spark.sql.catalyst.parser.{ParseException, ParserInterface}
+import 
org.apache.spark.sql.catalyst.parser.ParserUtils.{checkDuplicateClauses, 
checkDuplicateKeys, operationNotAllowed, source, string, stringWithoutUnescape, 
validate, withOrigin}
 import org.apache.spark.sql.catalyst.plans._
 import org.apache.spark.sql.catalyst.plans.logical._
-import org.apache.spark.sql.catalyst.util.{truncatedString, CharVarcharUtils, 
DateTimeUtils, IntervalUtils}
+import org.apache.spark.sql.catalyst.util.{DateTimeUtils, IntervalUtils}
 import org.apache.spark.sql.catalyst.util.DateTimeUtils._
 import 
org.apache.spark.sql.connector.catalog.CatalogV2Implicits.BucketSpecHelper
 import org.apache.spark.sql.connector.catalog.TableCatalog
 import org.apache.spark.sql.connector.expressions.{ApplyTransform, 
BucketTransform, DaysTransform, Expression => V2Expression, FieldReference, 
HoursTransform, IdentityTransform, LiteralValue, MonthsTransform, Transform, 
YearsTransform}
 import org.apache.spark.sql.internal.SQLConf
 import org.apache.spark.sql.types._
-import org.apache.spark.sql.types.BlobType
 import org.apache.spark.unsafe.types.{CalendarInterval, UTF8String}
 import org.apache.spark.util.Utils.isTesting
 
@@ -208,14 +206,18 @@ class HoodieSpark3_5ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
   }
 
   /**
-   * Create a NULL literal expression.
+   * Create a NULL literal expression. Reachable under ANSI keyword mode, 
where NULL (like
+   * FALSE) is a reserved word, so f(null, id) in transform-argument position 
parses as a
+   * null literal instead of a column reference.
    */
   override def visitNullLiteral(ctx: NullLiteralContext): Literal = 
withOrigin(ctx) {
     Literal(null)
   }
 
   /**
-   * Create a Boolean literal expression.
+   * Create a Boolean literal expression. Reachable under ANSI keyword mode, 
where FALSE is a
+   * reserved word (only TRUE is ansiNonReserved), so a bare false in 
transform-argument
+   * position parses as a boolean literal instead of a column reference.
    */
   override def visitBooleanLiteral(ctx: BooleanLiteralContext): Literal = 
withOrigin(ctx) {
     if (ctx.getText.toBoolean) {
@@ -642,13 +644,6 @@ class HoodieSpark3_5ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     }
   }
 
-  /**
-   * Create top level table schema.
-   */
-  protected def createSchema(ctx: ColTypeListContext): StructType = {
-    StructType(Option(ctx).toSeq.flatMap(visitColTypeList))
-  }
-
   /**
    * Create a [[StructType]] from a number of column definitions.
    */
@@ -691,13 +686,6 @@ class HoodieSpark3_5ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     }
   }
 
-  /**
-   * Create a [[StructType]] from a sequence of [[StructField]]s.
-   */
-  protected def createStructType(ctx: ComplexColTypeListContext): StructType = 
{
-    StructType(Option(ctx).toSeq.flatMap(visitComplexColTypeList))
-  }
-
   /**
    * Create a [[StructType]] from a number of column definitions.
    */
@@ -776,7 +764,7 @@ class HoodieSpark3_5ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
 
   /**
    * Convert a table property list into a key-value map.
-   * This should be called through [[visitPropertyKeyValues]] or 
[[visitPropertyKeys]].
+   * This should be called through [[visitPropertyKeyValues]].
    */
   override def visitTablePropertyList(
                                        ctx: TablePropertyListContext): 
Map[String, String] = withOrigin(ctx) {
@@ -803,19 +791,6 @@ class HoodieSpark3_5ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     props
   }
 
-  /**
-   * Parse a list of keys from a [[TablePropertyListContext]], assuming no 
values are specified.
-   */
-  def visitPropertyKeys(ctx: TablePropertyListContext): Seq[String] = {
-    val props = visitTablePropertyList(ctx)
-    val badKeys = props.filter { case (_, v) => v != null }.keys
-    if (badKeys.nonEmpty) {
-      operationNotAllowed(
-        s"Values should not be specified for key(s): ${badKeys.mkString("[", 
",", "]")}", ctx)
-    }
-    props.keys.toSeq
-  }
-
   /**
    * A table property key can either be String or a collection of dot 
separated elements. This
    * function extracts the property key based on whether its a string literal 
or a table property
@@ -925,10 +900,6 @@ class HoodieSpark3_5ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
       lazy val name: String = ctx.identifier.getText
       if (arguments.size > 1) {
         throw new ParseException(s"Too many arguments for transform $name", 
ctx)
-      } else if (arguments.isEmpty) {
-        throw
-
-          new ParseException(s"Not enough arguments for transform $name", ctx)
       } else {
         getFieldReference(ctx, arguments.head)
       }
@@ -1044,8 +1015,6 @@ class HoodieSpark3_5ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
         SerdeInfo(storedAs = Some(c.identifier.getText))
       case (null, storageHandler) =>
         operationNotAllowed("STORED BY", ctx)
-      case _ =>
-        throw new ParseException("Expected either STORED AS or STORED BY, not 
both", ctx)
     }
   }
 
@@ -1144,7 +1113,8 @@ class HoodieSpark3_5ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
               s"ROW FORMAT DELIMITED is only compatible with 'textfile', not 
'$fmt'", parentCtx)
           }
         case _ =>
-          // should never happen
+          // Reachable: ROW FORMAT ... STORED BY 'handler' leaves 
createFileFormatCtx.fileFormat
+          // null, so none of the typed arms above match.
           def str(ctx: ParserRuleContext): String = {
             (0 until ctx.getChildCount).map { i => ctx.getChild(i).getText 
}.mkString(" ")
           }
@@ -1230,14 +1200,13 @@ class HoodieSpark3_5ExtendedSqlAstBuilder(conf: 
SQLConf, delegate: ParserInterfa
   }
 
   /**
-   * Create a table, returning a [[CreateTable]] or [[CreateTableAsSelect]] 
logical plan.
+   * Create a table, returning a [[CreateTable]] logical plan.
    *
    * Expected format:
    * {{{
    *   CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db_name.]table_name
    *   [USING table_provider]
-   *   create_table_clauses
-   *   [[AS] select_statement];
+   *   create_table_clauses;
    *
    *   create_table_clauses (order insensitive):
    *     [PARTITIONED BY (partition_fields)]
@@ -1277,7 +1246,7 @@ class HoodieSpark3_5ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     // partition transforms for BucketSpec was moved inside parser
     // https://issues.apache.org/jira/browse/SPARK-37923
     val partitioning =
-    partitionExpressions(partTransforms, partCols, ctx) ++ 
bucketSpec.map(_.asTransform)
+      partitionExpressions(partTransforms, partCols, ctx) ++ 
bucketSpec.map(_.asTransform)
     val tableSpec = TableSpec(properties, provider, options, location, comment,
       serdeInfo, external)
 
@@ -1360,7 +1329,7 @@ class HoodieSpark3_5ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
 
   /**
    * Convert a property list into a key-value map.
-   * This should be called through [[visitPropertyKeyValues]] or 
[[visitPropertyKeys]].
+   * This should be called through [[visitPropertyKeyValues]].
    */
   override def visitPropertyList(ctx: PropertyListContext): Map[String, 
String] = withOrigin(ctx) {
     val properties = ctx.property.asScala.map { property =>
@@ -1386,19 +1355,6 @@ class HoodieSpark3_5ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     props
   }
 
-  /**
-   * Parse a list of keys from a [[PropertyListContext]], assuming no values 
are specified.
-   */
-  def visitPropertyKeys(ctx: PropertyListContext): Seq[String] = {
-    val props = visitPropertyList(ctx)
-    val badKeys = props.filter { case (_, v) => v != null }.keys
-    if (badKeys.nonEmpty) {
-      operationNotAllowed(
-        s"Values should not be specified for key(s): ${badKeys.mkString("[", 
",", "]")}", ctx)
-    }
-    props.keys.toSeq
-  }
-
   /**
    * A property key can either be String or a collection of dot separated 
elements. This
    * function extracts the property key based on whether its a string literal 
or a property
diff --git 
a/hudi-spark-datasource/hudi-spark4.0.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark4_0ExtendedSqlAstBuilder.scala
 
b/hudi-spark-datasource/hudi-spark4.0.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark4_0ExtendedSqlAstBuilder.scala
index 4fb0356b4f7b..08863dc81a33 100644
--- 
a/hudi-spark-datasource/hudi-spark4.0.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark4_0ExtendedSqlAstBuilder.scala
+++ 
b/hudi-spark-datasource/hudi-spark4.0.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark4_0ExtendedSqlAstBuilder.scala
@@ -21,26 +21,24 @@ import 
org.apache.hudi.spark.sql.parser.{HoodieSqlBaseBaseVisitor, HoodieSqlBase
 import org.apache.hudi.spark.sql.parser.HoodieSqlBaseParser._
 
 import org.antlr.v4.runtime.{ParserRuleContext, Token}
-import org.antlr.v4.runtime.tree.{ParseTree, RuleNode, TerminalNode}
+import org.antlr.v4.runtime.tree.{ParseTree, RuleNode}
 import org.apache.spark.internal.Logging
 import org.apache.spark.sql.AnalysisException
-import org.apache.spark.sql.catalyst.{FunctionIdentifier, TableIdentifier}
+import org.apache.spark.sql.catalyst.TableIdentifier
 import org.apache.spark.sql.catalyst.analysis._
-import org.apache.spark.sql.catalyst.catalog.{BucketSpec, CatalogStorageFormat}
+import org.apache.spark.sql.catalyst.catalog.BucketSpec
 import org.apache.spark.sql.catalyst.expressions._
-import org.apache.spark.sql.catalyst.expressions.aggregate.{First, Last}
-import org.apache.spark.sql.catalyst.parser.{EnhancedLogicalPlan, 
ParseException, ParserInterface}
-import 
org.apache.spark.sql.catalyst.parser.ParserUtils.{checkDuplicateClauses, 
checkDuplicateKeys, entry, escapedIdentifier, operationNotAllowed, source, 
string, stringWithoutUnescape, validate, withOrigin}
+import org.apache.spark.sql.catalyst.parser.{ParseException, ParserInterface}
+import 
org.apache.spark.sql.catalyst.parser.ParserUtils.{checkDuplicateClauses, 
checkDuplicateKeys, operationNotAllowed, source, string, stringWithoutUnescape, 
validate, withOrigin}
 import org.apache.spark.sql.catalyst.plans._
 import org.apache.spark.sql.catalyst.plans.logical._
-import org.apache.spark.sql.catalyst.util.{truncatedString, CharVarcharUtils, 
DateTimeUtils,  IntervalUtils}
+import org.apache.spark.sql.catalyst.util.{DateTimeUtils, IntervalUtils}
 import org.apache.spark.sql.catalyst.util.DateTimeUtils._
 import 
org.apache.spark.sql.connector.catalog.CatalogV2Implicits.BucketSpecHelper
 import org.apache.spark.sql.connector.catalog.TableCatalog
 import org.apache.spark.sql.connector.expressions.{ApplyTransform, 
BucketTransform, DaysTransform, Expression => V2Expression, FieldReference, 
HoursTransform, IdentityTransform, LiteralValue, MonthsTransform, Transform, 
YearsTransform}
 import org.apache.spark.sql.internal.SQLConf
 import org.apache.spark.sql.types._
-import org.apache.spark.sql.types.BlobType
 import org.apache.spark.unsafe.types.{CalendarInterval, UTF8String}
 import org.apache.spark.util.Utils.isTesting
 
@@ -208,14 +206,18 @@ class HoodieSpark4_0ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
   }
 
   /**
-   * Create a NULL literal expression.
+   * Create a NULL literal expression. Reachable under ANSI keyword mode, 
where NULL (like
+   * FALSE) is a reserved word, so f(null, id) in transform-argument position 
parses as a
+   * null literal instead of a column reference.
    */
   override def visitNullLiteral(ctx: NullLiteralContext): Literal = 
withOrigin(ctx) {
     Literal(null)
   }
 
   /**
-   * Create a Boolean literal expression.
+   * Create a Boolean literal expression. Reachable under ANSI keyword mode, 
where FALSE is a
+   * reserved word (only TRUE is ansiNonReserved), so a bare false in 
transform-argument
+   * position parses as a boolean literal instead of a column reference.
    */
   override def visitBooleanLiteral(ctx: BooleanLiteralContext): Literal = 
withOrigin(ctx) {
     if (ctx.getText.toBoolean) {
@@ -642,13 +644,6 @@ class HoodieSpark4_0ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     }
   }
 
-  /**
-   * Create top level table schema.
-   */
-  protected def createSchema(ctx: ColTypeListContext): StructType = {
-    StructType(Option(ctx).toSeq.flatMap(visitColTypeList))
-  }
-
   /**
    * Create a [[StructType]] from a number of column definitions.
    */
@@ -691,13 +686,6 @@ class HoodieSpark4_0ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     }
   }
 
-  /**
-   * Create a [[StructType]] from a sequence of [[StructField]]s.
-   */
-  protected def createStructType(ctx: ComplexColTypeListContext): StructType = 
{
-    StructType(Option(ctx).toSeq.flatMap(visitComplexColTypeList))
-  }
-
   /**
    * Create a [[StructType]] from a number of column definitions.
    */
@@ -776,7 +764,7 @@ class HoodieSpark4_0ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
 
   /**
    * Convert a table property list into a key-value map.
-   * This should be called through [[visitPropertyKeyValues]] or 
[[visitPropertyKeys]].
+   * This should be called through [[visitPropertyKeyValues]].
    */
   override def visitTablePropertyList(
                                        ctx: TablePropertyListContext): 
Map[String, String] = withOrigin(ctx) {
@@ -803,19 +791,6 @@ class HoodieSpark4_0ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     props
   }
 
-  /**
-   * Parse a list of keys from a [[TablePropertyListContext]], assuming no 
values are specified.
-   */
-  def visitPropertyKeys(ctx: TablePropertyListContext): Seq[String] = {
-    val props = visitTablePropertyList(ctx)
-    val badKeys = props.filter { case (_, v) => v != null }.keys
-    if (badKeys.nonEmpty) {
-      operationNotAllowed(
-        s"Values should not be specified for key(s): ${badKeys.mkString("[", 
",", "]")}", ctx)
-    }
-    props.keys.toSeq
-  }
-
   /**
    * A table property key can either be String or a collection of dot 
separated elements. This
    * function extracts the property key based on whether its a string literal 
or a table property
@@ -925,10 +900,6 @@ class HoodieSpark4_0ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
       lazy val name: String = ctx.identifier.getText
       if (arguments.size > 1) {
         throw new ParseException(s"Too many arguments for transform $name", 
ctx)
-      } else if (arguments.isEmpty) {
-        throw
-
-          new ParseException(s"Not enough arguments for transform $name", ctx)
       } else {
         getFieldReference(ctx, arguments.head)
       }
@@ -1044,8 +1015,6 @@ class HoodieSpark4_0ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
         SerdeInfo(storedAs = Some(c.identifier.getText))
       case (null, storageHandler) =>
         operationNotAllowed("STORED BY", ctx)
-      case _ =>
-        throw new ParseException("Expected either STORED AS or STORED BY, not 
both", ctx)
     }
   }
 
@@ -1144,7 +1113,8 @@ class HoodieSpark4_0ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
               s"ROW FORMAT DELIMITED is only compatible with 'textfile', not 
'$fmt'", parentCtx)
           }
         case _ =>
-          // should never happen
+          // Reachable: ROW FORMAT ... STORED BY 'handler' leaves 
createFileFormatCtx.fileFormat
+          // null, so none of the typed arms above match.
           def str(ctx: ParserRuleContext): String = {
             (0 until ctx.getChildCount).map { i => ctx.getChild(i).getText 
}.mkString(" ")
           }
@@ -1276,7 +1246,7 @@ class HoodieSpark4_0ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     // partition transforms for BucketSpec was moved inside parser
     // https://issues.apache.org/jira/browse/SPARK-37923
     val partitioning =
-    partitionExpressions(partTransforms, partCols, ctx) ++ 
bucketSpec.map(_.asTransform)
+      partitionExpressions(partTransforms, partCols, ctx) ++ 
bucketSpec.map(_.asTransform)
     val tableSpec = TableSpec(properties, provider, options, location, comment,
       Option.empty, serdeInfo, external)
 
@@ -1359,7 +1329,7 @@ class HoodieSpark4_0ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
 
   /**
    * Convert a property list into a key-value map.
-   * This should be called through [[visitPropertyKeyValues]] or 
[[visitPropertyKeys]].
+   * This should be called through [[visitPropertyKeyValues]].
    */
   override def visitPropertyList(ctx: PropertyListContext): Map[String, 
String] = withOrigin(ctx) {
     val properties = ctx.property.asScala.map { property =>
@@ -1385,19 +1355,6 @@ class HoodieSpark4_0ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     props
   }
 
-  /**
-   * Parse a list of keys from a [[PropertyListContext]], assuming no values 
are specified.
-   */
-  def visitPropertyKeys(ctx: PropertyListContext): Seq[String] = {
-    val props = visitPropertyList(ctx)
-    val badKeys = props.filter { case (_, v) => v != null }.keys
-    if (badKeys.nonEmpty) {
-      operationNotAllowed(
-        s"Values should not be specified for key(s): ${badKeys.mkString("[", 
",", "]")}", ctx)
-    }
-    props.keys.toSeq
-  }
-
   /**
    * A property key can either be String or a collection of dot separated 
elements. This
    * function extracts the property key based on whether its a string literal 
or a property
diff --git 
a/hudi-spark-datasource/hudi-spark4.1.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark4_1ExtendedSqlAstBuilder.scala
 
b/hudi-spark-datasource/hudi-spark4.1.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark4_1ExtendedSqlAstBuilder.scala
index 421c6b4fe802..74b1192bef6c 100644
--- 
a/hudi-spark-datasource/hudi-spark4.1.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark4_1ExtendedSqlAstBuilder.scala
+++ 
b/hudi-spark-datasource/hudi-spark4.1.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark4_1ExtendedSqlAstBuilder.scala
@@ -21,19 +21,18 @@ import 
org.apache.hudi.spark.sql.parser.{HoodieSqlBaseBaseVisitor, HoodieSqlBase
 import org.apache.hudi.spark.sql.parser.HoodieSqlBaseParser._
 
 import org.antlr.v4.runtime.{ParserRuleContext, Token}
-import org.antlr.v4.runtime.tree.{ParseTree, RuleNode, TerminalNode}
+import org.antlr.v4.runtime.tree.{ParseTree, RuleNode}
 import org.apache.spark.internal.Logging
 import org.apache.spark.sql.AnalysisException
-import org.apache.spark.sql.catalyst.{FunctionIdentifier, TableIdentifier}
+import org.apache.spark.sql.catalyst.TableIdentifier
 import org.apache.spark.sql.catalyst.analysis._
-import org.apache.spark.sql.catalyst.catalog.{BucketSpec, CatalogStorageFormat}
+import org.apache.spark.sql.catalyst.catalog.BucketSpec
 import org.apache.spark.sql.catalyst.expressions._
-import org.apache.spark.sql.catalyst.expressions.aggregate.{First, Last}
-import org.apache.spark.sql.catalyst.parser.{EnhancedLogicalPlan, 
ParseException, ParserInterface}
-import 
org.apache.spark.sql.catalyst.parser.ParserUtils.{checkDuplicateClauses, 
checkDuplicateKeys, entry, escapedIdentifier, operationNotAllowed, source, 
string, stringWithoutUnescape, validate, withOrigin}
+import org.apache.spark.sql.catalyst.parser.{ParseException, ParserInterface}
+import 
org.apache.spark.sql.catalyst.parser.ParserUtils.{checkDuplicateClauses, 
checkDuplicateKeys, operationNotAllowed, source, string, stringWithoutUnescape, 
validate, withOrigin}
 import org.apache.spark.sql.catalyst.plans._
 import org.apache.spark.sql.catalyst.plans.logical._
-import org.apache.spark.sql.catalyst.util.{truncatedString, CharVarcharUtils, 
DateTimeUtils, IntervalUtils}
+import org.apache.spark.sql.catalyst.util.{DateTimeUtils, IntervalUtils}
 import org.apache.spark.sql.catalyst.util.DateTimeUtils._
 import 
org.apache.spark.sql.connector.catalog.CatalogV2Implicits.BucketSpecHelper
 import org.apache.spark.sql.connector.catalog.TableCatalog
@@ -207,14 +206,18 @@ class HoodieSpark4_1ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
   }
 
   /**
-   * Create a NULL literal expression.
+   * Create a NULL literal expression. Reachable under ANSI keyword mode, 
where NULL (like
+   * FALSE) is a reserved word, so f(null, id) in transform-argument position 
parses as a
+   * null literal instead of a column reference.
    */
   override def visitNullLiteral(ctx: NullLiteralContext): Literal = 
withOrigin(ctx) {
     Literal(null)
   }
 
   /**
-   * Create a Boolean literal expression.
+   * Create a Boolean literal expression. Reachable under ANSI keyword mode, 
where FALSE is a
+   * reserved word (only TRUE is ansiNonReserved), so a bare false in 
transform-argument
+   * position parses as a boolean literal instead of a column reference.
    */
   override def visitBooleanLiteral(ctx: BooleanLiteralContext): Literal = 
withOrigin(ctx) {
     if (ctx.getText.toBoolean) {
@@ -641,13 +644,6 @@ class HoodieSpark4_1ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     }
   }
 
-  /**
-   * Create top level table schema.
-   */
-  protected def createSchema(ctx: ColTypeListContext): StructType = {
-    StructType(Option(ctx).toSeq.flatMap(visitColTypeList))
-  }
-
   /**
    * Create a [[StructType]] from a number of column definitions.
    */
@@ -690,13 +686,6 @@ class HoodieSpark4_1ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     }
   }
 
-  /**
-   * Create a [[StructType]] from a sequence of [[StructField]]s.
-   */
-  protected def createStructType(ctx: ComplexColTypeListContext): StructType = 
{
-    StructType(Option(ctx).toSeq.flatMap(visitComplexColTypeList))
-  }
-
   /**
    * Create a [[StructType]] from a number of column definitions.
    */
@@ -775,7 +764,7 @@ class HoodieSpark4_1ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
 
   /**
    * Convert a table property list into a key-value map.
-   * This should be called through [[visitPropertyKeyValues]] or 
[[visitPropertyKeys]].
+   * This should be called through [[visitPropertyKeyValues]].
    */
   override def visitTablePropertyList(
                                        ctx: TablePropertyListContext): 
Map[String, String] = withOrigin(ctx) {
@@ -802,19 +791,6 @@ class HoodieSpark4_1ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     props
   }
 
-  /**
-   * Parse a list of keys from a [[TablePropertyListContext]], assuming no 
values are specified.
-   */
-  def visitPropertyKeys(ctx: TablePropertyListContext): Seq[String] = {
-    val props = visitTablePropertyList(ctx)
-    val badKeys = props.filter { case (_, v) => v != null }.keys
-    if (badKeys.nonEmpty) {
-      operationNotAllowed(
-        s"Values should not be specified for key(s): ${badKeys.mkString("[", 
",", "]")}", ctx)
-    }
-    props.keys.toSeq
-  }
-
   /**
    * A table property key can either be String or a collection of dot 
separated elements. This
    * function extracts the property key based on whether its a string literal 
or a table property
@@ -924,10 +900,6 @@ class HoodieSpark4_1ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
       lazy val name: String = ctx.identifier.getText
       if (arguments.size > 1) {
         throw new ParseException(s"Too many arguments for transform $name", 
ctx)
-      } else if (arguments.isEmpty) {
-        throw
-
-          new ParseException(s"Not enough arguments for transform $name", ctx)
       } else {
         getFieldReference(ctx, arguments.head)
       }
@@ -1043,8 +1015,6 @@ class HoodieSpark4_1ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
         SerdeInfo(storedAs = Some(c.identifier.getText))
       case (null, storageHandler) =>
         operationNotAllowed("STORED BY", ctx)
-      case _ =>
-        throw new ParseException("Expected either STORED AS or STORED BY, not 
both", ctx)
     }
   }
 
@@ -1143,7 +1113,8 @@ class HoodieSpark4_1ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
               s"ROW FORMAT DELIMITED is only compatible with 'textfile', not 
'$fmt'", parentCtx)
           }
         case _ =>
-          // should never happen
+          // Reachable: ROW FORMAT ... STORED BY 'handler' leaves 
createFileFormatCtx.fileFormat
+          // null, so none of the typed arms above match.
           def str(ctx: ParserRuleContext): String = {
             (0 until ctx.getChildCount).map { i => ctx.getChild(i).getText 
}.mkString(" ")
           }
@@ -1235,8 +1206,7 @@ class HoodieSpark4_1ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
    * {{{
    *   CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db_name.]table_name
    *   [USING table_provider]
-   *   create_table_clauses
-   *   [[AS] select_statement];
+   *   create_table_clauses;
    *
    *   create_table_clauses (order insensitive):
    *     [PARTITIONED BY (partition_fields)]
@@ -1276,7 +1246,7 @@ class HoodieSpark4_1ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     // partition transforms for BucketSpec was moved inside parser
     // https://issues.apache.org/jira/browse/SPARK-37923
     val partitioning =
-    partitionExpressions(partTransforms, partCols, ctx) ++ 
bucketSpec.map(_.asTransform)
+      partitionExpressions(partTransforms, partCols, ctx) ++ 
bucketSpec.map(_.asTransform)
     val tableSpec = TableSpec(properties, provider, options, location, comment,
       Option.empty, serdeInfo, external)
 
@@ -1359,7 +1329,7 @@ class HoodieSpark4_1ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
 
   /**
    * Convert a property list into a key-value map.
-   * This should be called through [[visitPropertyKeyValues]] or 
[[visitPropertyKeys]].
+   * This should be called through [[visitPropertyKeyValues]].
    */
   override def visitPropertyList(ctx: PropertyListContext): Map[String, 
String] = withOrigin(ctx) {
     val properties = ctx.property.asScala.map { property =>
@@ -1385,19 +1355,6 @@ class HoodieSpark4_1ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     props
   }
 
-  /**
-   * Parse a list of keys from a [[PropertyListContext]], assuming no values 
are specified.
-   */
-  def visitPropertyKeys(ctx: PropertyListContext): Seq[String] = {
-    val props = visitPropertyList(ctx)
-    val badKeys = props.filter { case (_, v) => v != null }.keys
-    if (badKeys.nonEmpty) {
-      operationNotAllowed(
-        s"Values should not be specified for key(s): ${badKeys.mkString("[", 
",", "]")}", ctx)
-    }
-    props.keys.toSeq
-  }
-
   /**
    * A property key can either be String or a collection of dot separated 
elements. This
    * function extracts the property key based on whether its a string literal 
or a property
diff --git 
a/hudi-spark-datasource/hudi-spark4.2.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark4_2ExtendedSqlAstBuilder.scala
 
b/hudi-spark-datasource/hudi-spark4.2.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark4_2ExtendedSqlAstBuilder.scala
index 198723f0e709..94066110b720 100644
--- 
a/hudi-spark-datasource/hudi-spark4.2.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark4_2ExtendedSqlAstBuilder.scala
+++ 
b/hudi-spark-datasource/hudi-spark4.2.x/src/main/scala/org/apache/spark/sql/parser/HoodieSpark4_2ExtendedSqlAstBuilder.scala
@@ -21,19 +21,18 @@ import 
org.apache.hudi.spark.sql.parser.{HoodieSqlBaseBaseVisitor, HoodieSqlBase
 import org.apache.hudi.spark.sql.parser.HoodieSqlBaseParser._
 
 import org.antlr.v4.runtime.{ParserRuleContext, Token}
-import org.antlr.v4.runtime.tree.{ParseTree, RuleNode, TerminalNode}
+import org.antlr.v4.runtime.tree.{ParseTree, RuleNode}
 import org.apache.spark.internal.Logging
 import org.apache.spark.sql.AnalysisException
-import org.apache.spark.sql.catalyst.{FunctionIdentifier, TableIdentifier}
+import org.apache.spark.sql.catalyst.TableIdentifier
 import org.apache.spark.sql.catalyst.analysis._
-import org.apache.spark.sql.catalyst.catalog.{BucketSpec, CatalogStorageFormat}
+import org.apache.spark.sql.catalyst.catalog.BucketSpec
 import org.apache.spark.sql.catalyst.expressions._
-import org.apache.spark.sql.catalyst.expressions.aggregate.{First, Last}
-import org.apache.spark.sql.catalyst.parser.{EnhancedLogicalPlan, 
ParseException, ParserInterface}
-import 
org.apache.spark.sql.catalyst.parser.ParserUtils.{checkDuplicateClauses, 
checkDuplicateKeys, entry, escapedIdentifier, operationNotAllowed, source, 
string, stringWithoutUnescape, validate, withOrigin}
+import org.apache.spark.sql.catalyst.parser.{ParseException, ParserInterface}
+import 
org.apache.spark.sql.catalyst.parser.ParserUtils.{checkDuplicateClauses, 
checkDuplicateKeys, operationNotAllowed, source, string, stringWithoutUnescape, 
validate, withOrigin}
 import org.apache.spark.sql.catalyst.plans._
 import org.apache.spark.sql.catalyst.plans.logical._
-import org.apache.spark.sql.catalyst.util.{truncatedString, CharVarcharUtils, 
DateTimeUtils, IntervalUtils}
+import org.apache.spark.sql.catalyst.util.{DateTimeUtils, IntervalUtils}
 import org.apache.spark.sql.catalyst.util.DateTimeUtils._
 import 
org.apache.spark.sql.connector.catalog.CatalogV2Implicits.BucketSpecHelper
 import org.apache.spark.sql.connector.catalog.TableCatalog
@@ -207,14 +206,18 @@ class HoodieSpark4_2ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
   }
 
   /**
-   * Create a NULL literal expression.
+   * Create a NULL literal expression. Reachable under ANSI keyword mode, 
where NULL (like
+   * FALSE) is a reserved word, so f(null, id) in transform-argument position 
parses as a
+   * null literal instead of a column reference.
    */
   override def visitNullLiteral(ctx: NullLiteralContext): Literal = 
withOrigin(ctx) {
     Literal(null)
   }
 
   /**
-   * Create a Boolean literal expression.
+   * Create a Boolean literal expression. Reachable under ANSI keyword mode, 
where FALSE is a
+   * reserved word (only TRUE is ansiNonReserved), so a bare false in 
transform-argument
+   * position parses as a boolean literal instead of a column reference.
    */
   override def visitBooleanLiteral(ctx: BooleanLiteralContext): Literal = 
withOrigin(ctx) {
     if (ctx.getText.toBoolean) {
@@ -641,13 +644,6 @@ class HoodieSpark4_2ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     }
   }
 
-  /**
-   * Create top level table schema.
-   */
-  protected def createSchema(ctx: ColTypeListContext): StructType = {
-    StructType(Option(ctx).toSeq.flatMap(visitColTypeList))
-  }
-
   /**
    * Create a [[StructType]] from a number of column definitions.
    */
@@ -690,13 +686,6 @@ class HoodieSpark4_2ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     }
   }
 
-  /**
-   * Create a [[StructType]] from a sequence of [[StructField]]s.
-   */
-  protected def createStructType(ctx: ComplexColTypeListContext): StructType = 
{
-    StructType(Option(ctx).toSeq.flatMap(visitComplexColTypeList))
-  }
-
   /**
    * Create a [[StructType]] from a number of column definitions.
    */
@@ -775,7 +764,7 @@ class HoodieSpark4_2ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
 
   /**
    * Convert a table property list into a key-value map.
-   * This should be called through [[visitPropertyKeyValues]] or 
[[visitPropertyKeys]].
+   * This should be called through [[visitPropertyKeyValues]].
    */
   override def visitTablePropertyList(
                                        ctx: TablePropertyListContext): 
Map[String, String] = withOrigin(ctx) {
@@ -802,19 +791,6 @@ class HoodieSpark4_2ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     props
   }
 
-  /**
-   * Parse a list of keys from a [[TablePropertyListContext]], assuming no 
values are specified.
-   */
-  def visitPropertyKeys(ctx: TablePropertyListContext): Seq[String] = {
-    val props = visitTablePropertyList(ctx)
-    val badKeys = props.filter { case (_, v) => v != null }.keys
-    if (badKeys.nonEmpty) {
-      operationNotAllowed(
-        s"Values should not be specified for key(s): ${badKeys.mkString("[", 
",", "]")}", ctx)
-    }
-    props.keys.toSeq
-  }
-
   /**
    * A table property key can either be String or a collection of dot 
separated elements. This
    * function extracts the property key based on whether its a string literal 
or a table property
@@ -924,10 +900,6 @@ class HoodieSpark4_2ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
       lazy val name: String = ctx.identifier.getText
       if (arguments.size > 1) {
         throw new ParseException(s"Too many arguments for transform $name", 
ctx)
-      } else if (arguments.isEmpty) {
-        throw
-
-          new ParseException(s"Not enough arguments for transform $name", ctx)
       } else {
         getFieldReference(ctx, arguments.head)
       }
@@ -1043,8 +1015,6 @@ class HoodieSpark4_2ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
         SerdeInfo(storedAs = Some(c.identifier.getText))
       case (null, storageHandler) =>
         operationNotAllowed("STORED BY", ctx)
-      case _ =>
-        throw new ParseException("Expected either STORED AS or STORED BY, not 
both", ctx)
     }
   }
 
@@ -1143,7 +1113,8 @@ class HoodieSpark4_2ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
               s"ROW FORMAT DELIMITED is only compatible with 'textfile', not 
'$fmt'", parentCtx)
           }
         case _ =>
-          // should never happen
+          // Reachable: ROW FORMAT ... STORED BY 'handler' leaves 
createFileFormatCtx.fileFormat
+          // null, so none of the typed arms above match.
           def str(ctx: ParserRuleContext): String = {
             (0 until ctx.getChildCount).map { i => ctx.getChild(i).getText 
}.mkString(" ")
           }
@@ -1275,7 +1246,7 @@ class HoodieSpark4_2ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     // partition transforms for BucketSpec was moved inside parser
     // https://issues.apache.org/jira/browse/SPARK-37923
     val partitioning =
-    partitionExpressions(partTransforms, partCols, ctx) ++ 
bucketSpec.map(_.asTransform)
+      partitionExpressions(partTransforms, partCols, ctx) ++ 
bucketSpec.map(_.asTransform)
     val tableSpec = TableSpec(properties, provider, options, location, comment,
       Option.empty, serdeInfo, external)
 
@@ -1358,7 +1329,7 @@ class HoodieSpark4_2ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
 
   /**
    * Convert a property list into a key-value map.
-   * This should be called through [[visitPropertyKeyValues]] or 
[[visitPropertyKeys]].
+   * This should be called through [[visitPropertyKeyValues]].
    */
   override def visitPropertyList(ctx: PropertyListContext): Map[String, 
String] = withOrigin(ctx) {
     val properties = ctx.property.asScala.map { property =>
@@ -1384,19 +1355,6 @@ class HoodieSpark4_2ExtendedSqlAstBuilder(conf: SQLConf, 
delegate: ParserInterfa
     props
   }
 
-  /**
-   * Parse a list of keys from a [[PropertyListContext]], assuming no values 
are specified.
-   */
-  def visitPropertyKeys(ctx: PropertyListContext): Seq[String] = {
-    val props = visitPropertyList(ctx)
-    val badKeys = props.filter { case (_, v) => v != null }.keys
-    if (badKeys.nonEmpty) {
-      operationNotAllowed(
-        s"Values should not be specified for key(s): ${badKeys.mkString("[", 
",", "]")}", ctx)
-    }
-    props.keys.toSeq
-  }
-
   /**
    * A property key can either be String or a collection of dot separated 
elements. This
    * function extracts the property key based on whether its a string literal 
or a property

Reply via email to