This is an automated email from the ASF dual-hosted git repository. yuzelin pushed a commit to branch release-1.4 in repository https://gitbox.apache.org/repos/asf/paimon.git
commit 07eac94c18e5efe833ef187e299449380a491e65 Author: Zouxxyy <[email protected]> AuthorDate: Thu Apr 2 21:52:18 2026 +0800 [spark] Fix view query failure with VARCHAR/CHAR column types (#7585) Fix querying Paimon View fails with "Cannot up cast from STRING to VARCHAR(n)" when the underlying table has VARCHAR or CHAR columns. Spark replaces CharType/VarcharType with StringType during V2 table resolution, but the view schema still preserves the original types. This causes UpCast(StringType → VarcharType(n)) to fail. The fix uses CharVarcharUtils.replaceCharVarcharWithStringInSchema to normalize the view schema before applying UpCast. --- .../catalyst/analysis/PaimonViewResolver.scala | 9 +++- .../paimon/spark/sql/PaimonViewTestBase.scala | 48 ++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/catalyst/analysis/PaimonViewResolver.scala b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/catalyst/analysis/PaimonViewResolver.scala index b45463e916..95b8ab6e18 100644 --- a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/catalyst/analysis/PaimonViewResolver.scala +++ b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/catalyst/analysis/PaimonViewResolver.scala @@ -30,6 +30,7 @@ import org.apache.spark.sql.catalyst.parser.ParseException import org.apache.spark.sql.catalyst.parser.extensions.{CurrentOrigin, Origin} import org.apache.spark.sql.catalyst.plans.logical.{LeafNode, LogicalPlan, Project, SubqueryAlias} import org.apache.spark.sql.catalyst.rules.Rule +import org.apache.spark.sql.catalyst.util.CharVarcharUtils import org.apache.spark.sql.connector.catalog.{Identifier, PaimonLookupCatalog} case class PaimonViewResolver(spark: SparkSession) @@ -66,7 +67,13 @@ case class PaimonViewResolver(spark: SparkSession) val earlyRules = Seq(CTESubstitution, SubstituteUnresolvedOrdinals) val rewritten = earlyRules.foldLeft(parsedPlan)((plan, rule) => rule.apply(plan)) - val aliases = SparkTypeUtils.fromPaimonRowType(view.rowType()).fields.zipWithIndex.map { + // Spark internally replaces CharType/VarcharType with StringType during V2 table resolution, + // so the view's schema must also use StringType to avoid UpCast failures + // (e.g., "Cannot up cast from STRING to VARCHAR(50)"). + val viewSchema = CharVarcharUtils.replaceCharVarcharWithStringInSchema( + SparkTypeUtils.fromPaimonRowType(view.rowType())) + + val aliases = viewSchema.fields.zipWithIndex.map { case (expected, pos) => val attr = GetColumnByOrdinal(pos, expected.dataType) Alias(UpCast(attr, expected.dataType), expected.name)(explicitMetadata = diff --git a/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/PaimonViewTestBase.scala b/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/PaimonViewTestBase.scala index 7c503d98a5..a0c29bfcbc 100644 --- a/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/PaimonViewTestBase.scala +++ b/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/PaimonViewTestBase.scala @@ -242,4 +242,52 @@ abstract class PaimonViewTestBase extends PaimonHiveTestBase { } } } + + test("Paimon View: create view on table with VARCHAR columns") { + Seq(sparkCatalogName, paimonHiveCatalogName).foreach { + catalogName => + sql(s"USE $catalogName") + withDatabase("test_db") { + sql("CREATE DATABASE test_db") + sql("USE test_db") + withTable("t") { + withView("v1") { + sql("CREATE TABLE t (id INT, name VARCHAR(50), country STRING) USING paimon") + sql("INSERT INTO t VALUES (1, 'alice', 'beijing'), (2, 'bob', 'shanghai')") + sql("CREATE VIEW v1 AS SELECT * FROM t") + checkAnswer( + sql("SELECT * FROM v1"), + Seq(Row(1, "alice", "beijing"), Row(2, "bob", "shanghai"))) + } + } + } + } + } + + test("Paimon View: create view on table with nested VARCHAR and CHAR columns") { + Seq(sparkCatalogName, paimonHiveCatalogName).foreach { + catalogName => + sql(s"USE $catalogName") + withDatabase("test_db") { + sql("CREATE DATABASE test_db") + sql("USE test_db") + withTable("t") { + withView("v1") { + sql("""CREATE TABLE t ( + | id INT, + | tags ARRAY<VARCHAR(100)>, + | props MAP<STRING, VARCHAR(200)> + |) USING paimon""".stripMargin) + sql("""INSERT INTO t VALUES + | (1, ARRAY('a', 'b'), MAP('k1', 'v1')), + | (2, ARRAY('c'), MAP('k2', 'v2'))""".stripMargin) + sql("CREATE VIEW v1 AS SELECT * FROM t") + checkAnswer( + sql("SELECT * FROM v1"), + Seq(Row(1, Seq("a", "b"), Map("k1" -> "v1")), Row(2, Seq("c"), Map("k2" -> "v2")))) + } + } + } + } + } }
