andygrove opened a new issue, #56: URL: https://github.com/apache/datafusion-iceberg/issues/56
### Describe the bug `IcebergCatalogTableProvider` caches the table's Arrow schema when it is built ([catalog_table_provider.rs#L81-L84](https://github.com/apache/datafusion-iceberg/blob/72526809d80787c375ebc765ecee9b1db418a232/crates/datafusion/src/table_provider/catalog_table_provider.rs#L81-L84)). `IcebergCatalogProvider` builds one provider per table when it is constructed and keeps it ([schema.rs#L64-L94](https://github.com/apache/datafusion-iceberg/blob/72526809d80787c375ebc765ecee9b1db418a232/crates/datafusion/src/catalog/schema.rs#L64-L94)). `insert_into` plans its input against the cached schema, but `IcebergWriteExec` builds the Parquet writer from the table's current schema ([write/exec.rs#L244-L250](https://github.com/apache/datafusion-iceberg/blob/72526809d80787c375ebc765ecee9b1db418a232/crates/datafusion/src/write/exec.rs#L244-L250)). Once a column has been added to the table, by another engine or another session, the input batches lack that column and the Parquet writer fails on it: ``` Execution error: DataInvalid => Field id 3 not found in struct array ``` Every INSERT through that provider fails this way, with or without a column list, including after other sessions have written with the new schema. Scans through the same provider keep working and return only the cached columns. Writing works again only once the provider is rebuilt, which for `IcebergCatalogProvider` means registering the catalog again. ### To Reproduce Tested with an unpartitioned table in a `MemoryCatalog`: 1. Create `ns.t(id INT NOT NULL, name STRING NOT NULL)`, register an `IcebergCatalogProvider` as `c` in a session, and run `INSERT INTO c.ns.t VALUES (1, 'a')`. 2. Outside that session, add an optional column, for example with `Transaction::update_schema().add_column(AddColumn::optional("email", Type::Primitive(PrimitiveType::String)))` or `ALTER TABLE t ADD COLUMN email STRING` from Spark. 3. In the same session, run `INSERT INTO c.ns.t VALUES (2, 'b')` or `INSERT INTO c.ns.t (id, name) VALUES (2, 'b')`. Both fail with the error above. In a session that registers the catalog after step 2, `INSERT INTO c.ns.t (id, name) VALUES (2, 'b')` succeeds and `email` reads back as NULL. ### Expected behavior The INSERT succeeds and writes NULL for `email`, as it does through a provider built after the change. At the least, it should fail with an error saying that the table's schema changed after the provider was built. ### Additional context - This is a concrete case of #24 §2: the provider plans with its cached schema but writes with the table's current metadata. It persists because of #24 §1: `IcebergCatalogProvider` never rebuilds its providers. - #48 adds a constructor so that Ballista can rebuild a provider on the scheduler with the schema the client planned against. Ballista runs DML through that rebuilt provider, so it fails the same way. - Possible fixes: - have `IcebergWriteExec` fill the columns the input lacks with NULL, or with their write default - have `insert_into` compare the cached schema with the table's current schema and return a clear error -- 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]
