wombatu-kun opened a new issue, #19250:
URL: https://github.com/apache/hudi/issues/19250

   ### Bug Description
   
   `BasicStagedTable.newWriteBuilder` pattern-matches its `LogicalWriteInfo` 
argument against `SupportsWrite`, instead of matching the staged `Table`:
   
   ```scala
   override def newWriteBuilder(info: LogicalWriteInfo): WriteBuilder = {
     info match {
       case supportsWrite: SupportsWrite => supportsWrite.newWriteBuilder(info)
       case _ => throw new HoodieException(s"Table `${ident.name}` does not 
support writes.")
     }
   }
   ```
   
   `SupportsWrite` is a `Table` mixin (`SupportsWrite extends Table`, in 
`org.apache.spark.sql.connector.catalog`), while `LogicalWriteInfo` is an 
unrelated interface in `org.apache.spark.sql.connector.write` whose only Spark 
implementation, `LogicalWriteInfoImpl`, implements `LogicalWriteInfo, Product, 
Serializable`. No `LogicalWriteInfo` can ever satisfy the first case, so the 
delegating branch is dead and the method throws for every input, including one 
whose underlying table is perfectly writable.
   
   **What happened:** every write staged through `BasicStagedTable` is rejected 
with ``Table `x` does not support writes.``
   
   **What was expected:** the write is delegated to the underlying table when 
that table is `SupportsWrite`; the exception is raised only when it is not.
   
   `BasicStagedTable` is what `HoodieCatalog` returns from `stageCreate`, 
`stageReplace` and `stageCreateOrReplace` for non-Hudi tables, so an atomic 
CTAS or RTAS against a writable V2 table reaches this method.
   
   **Steps to reproduce:** a unit test is enough:
   
   ```scala
   val table = mock(classOf[SupportsWrite])
   val info = mock(classOf[LogicalWriteInfo])
   when(table.newWriteBuilder(info)).thenReturn(mock(classOf[WriteBuilder]))
   BasicStagedTable(Identifier.of(Array("db"), "tbl"), table, 
mock(classOf[TableCatalog])).newWriteBuilder(info)
   // throws HoodieException: Table `tbl` does not support writes.
   ```
   
   **Why it survived:** Scala compiles a match between two unrelated interfaces 
without a warning, since a class could in principle implement both, and 
`BasicStagedTable` had no test coverage at all. Present since #4611 (2022). 
Spark's own `BasicStagedTable` delegates via `table.asWritable`.
   
   **Fix:** match on `table` instead of `info`.
   


-- 
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]

Reply via email to