uros-b commented on code in PR #58496:
URL: https://github.com/apache/spark/pull/58496#discussion_r3955899159


##########
sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/parquet/ParquetIOSuite.scala:
##########
@@ -2009,6 +2009,37 @@ class ParquetIOSuite extends ParquetTest with 
SharedSparkSession {
     }
   }
 
+  test("SPARK-59251: Parquet readers reject incompatible primitive type 
conversions consistently") {
+    val cases = Seq(
+      ("required int32 c (DATE);", DecimalType(10, 0),
+        (record: SimpleGroup) => record.add(0, 1)),
+      ("required fixed_len_byte_array(4) c;", StringType,
+        (record: SimpleGroup) =>
+          record.add(0, Binary.fromConstantByteArray(Array[Byte](1, 2, 3, 
4)))))
+
+    cases.foreach { case (column, readType, writeValue) =>
+      val parquetSchema = MessageTypeParser.parseMessageType(
+        s"message root {\n  $column\n}")
+      val readSchema = new StructType().add("c", readType)
+
+      withTempDir { dir =>
+        val path = new Path(s"${dir.getCanonicalPath}/incompatible.parquet")
+        val writer = createParquetWriter(parquetSchema, path)
+        val record = new SimpleGroup(parquetSchema)
+        writeValue(record)
+        writer.write(record)
+        writer.close()
+
+        withAllParquetReaders {
+          val error = intercept[SparkException] {
+            spark.read.schema(readSchema).parquet(path.toString).collect()
+          }
+          assert(error.getCondition === 
"FAILED_READ_FILE.PARQUET_COLUMN_DATA_TYPE_MISMATCH")

Review Comment:
   Tests do not actually prove reader consistency.
   
   The new ParquetIOSuite test only asserts:
   ```
   assert(error.getCondition === 
"FAILED_READ_FILE.PARQUET_COLUMN_DATA_TYPE_MISMATCH")
   ```
   
   That hides a real payload mismatch. Vectorized uses 
Arrays.toString(descriptor.getPath()) ([c]); the row converter uses 
parquetType.getName (c). Nearby tests (SPARK-35640, the vectorized half of 
ParquetSchemaSuite) use checkErrorMatchPVals with "column" -> "\\[c\\]". If 
this test did the same, the row reader would fail.
   
   The updated ParquetSchemaSuite row-reader case still only checks the cause 
type and "Encountered error while reading file", while the vectorized sibling 
already checks the full error class and [a]. Now that both throw the same 
exception, those two tests should be aligned.
   
   Please also assert expectedType / actualType (decimal(10,0) vs INT32, string 
vs FIXED_LEN_BYTE_ARRAY).



##########
sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/parquet/ParquetRowConverter.scala:
##########
@@ -421,12 +432,26 @@ private[parquet] class ParquetRowConverter(
         }
 
       case t: DecimalType =>
-        throw 
QueryExecutionErrors.cannotCreateParquetConverterForDecimalTypeError(
-          t, parquetType.toString)
+        parquetType.asPrimitiveType().getPrimitiveTypeName match {
+          case INT32 | INT64 =>
+            throw new SchemaColumnConvertNotSupportedException(
+              parquetType.getName,
+              parquetType.asPrimitiveType().getPrimitiveTypeName.toString,
+              t.catalogString)
+          case _ =>
+            throw 
QueryExecutionErrors.cannotCreateParquetConverterForDecimalTypeError(
+              t, parquetType.toString)
+        }
 
-      case _: StringType =>
+      case _: StringType if parquetType.asPrimitiveType().getPrimitiveTypeName 
== BINARY =>
         new ParquetStringConverter(updater)
 
+      case t: StringType =>
+        throw new SchemaColumnConvertNotSupportedException(
+          parquetType.getName,
+          parquetType.asPrimitiveType().getPrimitiveTypeName.toString,
+          t.catalogString)
+
       case geom: GeometryType =>
         new ParquetGeometryConverter(geom.srid, updater)
 

Review Comment:
   User-facing change is a bit wider than the description.
   
   The PR description lists only:
   - FIXED_LEN_BYTE_ARRAY → STRING
   - INT32 DATE → DECIMAL
   
   However, the code also changes:
   - INT32/INT64/BOOLEAN/FLOAT/INT96 → STRING; Before: ParquetDecodingException 
(or silent UTF-8 for INT96 / FLBA); After: PARQUET_COLUMN_DATA_TYPE_MISMATCH.
   - INT64 TIMESTAMP/TIME → DECIMAL; Before: day/time ticks as decimal; After: 
mismatch.
   - UINT32 → DECIMAL; Before: treated as signed INT32 decimal; After: mismatch 
(vectorized already rejects).
   
   The INT32 vs STRING schema-mismatch test change is this broader STRING 
guard, not the DATE/FLBA cases. Please expand the user-facing section, and add 
at least INT64 TIMESTAMP → DECIMAL and INT96 → STRING (the other 
silent-corruption path). ParquetTypeWideningSuite still does not list DateType 
→ DecimalType as unsupported.



##########
sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/parquet/ParquetRowConverter.scala:
##########
@@ -421,12 +432,26 @@ private[parquet] class ParquetRowConverter(
         }
 
       case t: DecimalType =>
-        throw 
QueryExecutionErrors.cannotCreateParquetConverterForDecimalTypeError(
-          t, parquetType.toString)
+        parquetType.asPrimitiveType().getPrimitiveTypeName match {
+          case INT32 | INT64 =>
+            throw new SchemaColumnConvertNotSupportedException(
+              parquetType.getName,
+              parquetType.asPrimitiveType().getPrimitiveTypeName.toString,
+              t.catalogString)
+          case _ =>
+            throw 
QueryExecutionErrors.cannotCreateParquetConverterForDecimalTypeError(
+              t, parquetType.toString)
+        }
 
-      case _: StringType =>
+      case _: StringType if parquetType.asPrimitiveType().getPrimitiveTypeName 
== BINARY =>

Review Comment:
   Nit regarding robustness on the asPrimitiveType() on the new StringType 
match:
   ```
   case _: StringType if parquetType.asPrimitiveType().getPrimitiveTypeName == 
BINARY =>
   ```
   If the Parquet type is a group (struct/list requested as STRING), this 
throws IllegalStateException instead of 
SchemaColumnConvertNotSupportedException. Previously this path did not call 
asPrimitiveType(). Guard with parquetType.isPrimitive && ....



##########
sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/parquet/ParquetRowConverter.scala:
##########
@@ -329,6 +329,15 @@ private[parquet] class ParquetRowConverter(
       }
     }
 
+    def canReadAsDecimal: Boolean = {

Review Comment:
   There is a possible follow up here regarding the remaining decimal parity 
gap. Please investigate more.
   
   Vectorized isDecimalTypeMatched also requires the requested decimal to be 
wide enough (integerPrecision >= IntDecimal.precision for unannotated INT32). 
The new canReadAsDecimal only looks at the logical annotation, so INT32 → 
Decimal(5,0) can still succeed on the row reader and fail on the vectorized 
reader (ParquetTypeWideningSuite already documents that). Not a regression, but 
it is the same class of “flip enableVectorizedReader and the query changes” bug 
this PR is fixing. Worth a comment that this mirrors only the annotation half 
of isDecimalTypeMatched and/or linking a Jira item to fully close the gap.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to