Wrong results and panics after a column is dropped and added back under the same name
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 32/100
Direzione di ricerca
Start in crates/datafusion/src/table_provider/catalog_table_provider.rs, where the Arrow schema is cached when the provider is built, and in crates/datafusion/src/read/scan.rs, where columns are selected by name. Reproduce by creating a table, inserting a row, dropping and re-adding a column with a different type, then querying through a provider built before the change. Done means the stale-provider query either matches a fresh session or fails with a clear schema-changed error, with no wrong counts or panics. The issue leaves the fix open, so agree on the approach with maintainers before coding.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Describe the bug
IcebergCatalogTableProvider caches the table's Arrow schema when it is built (catalog_table_provider.rs#L81-L84), and IcebergCatalogProvider keeps each provider for its lifetime (schema.rs#L64-L94). Scans select columns by name from the snapshot they read (read/scan.rs#L93-L97).
Dropping a column and adding one back under the same name is how a column's type is changed when the change isn't an allowed promotion. After that, a provider built before the change reads the new column but still declares the old column's type and nullability. DataFusion trusts the declared schema:
- It folds
price IS NULLtofalsebecause the cachedpriceis NOT NULL. The plan replaces the scan withEmptyRelation, so the count is 0 without reading the table. - Operators that rely on the declared
Float64panic on the decimal arrays. SELECT *fails because a column declared non-nullable contains nulls.
To Reproduce
- Create
ns.t(id INT NOT NULL, price DOUBLE NOT NULL), register anIcebergCatalogProviderascin session S, and runINSERT INTO c.ns.t VALUES (1, 1.5). - Outside S, change
priceto a decimal: drop it withupdate_schema().delete_column("price"), then add it back withupdate_schema().add_column(AddColumn::optional("price", Type::Primitive(PrimitiveType::Decimal { precision: 10, scale: 2 }))). From Spark, that isALTER TABLE t DROP COLUMN pricefollowed byALTER TABLE t ADD COLUMN price DECIMAL(10, 2). - Insert
(2, 2.25)from a session that registers the catalog after step 2. - Run these queries in S and, for comparison, in a new session:
| Query | Session S | New session |
|---|---|---|
SELECT count(*) FROM c.ns.t WHERE price IS NULL |
0 |
1 |
SELECT count(price) FROM c.ns.t |
panics: primitive array |
1 |
SELECT id, price * 2 FROM c.ns.t |
panics: primitive array |
(1, NULL), (2, 4.50) |
SELECT * FROM c.ns.t |
error: Column 'price' is declared as non-nullable but contains null values |
(1, NULL), (2, 2.25) |
The panics come from arrow-array's as_primitive (cast.rs:849) and reach the caller as a Join Error.
If price is added back as DOUBLE instead, S still counts 0 and still fails SELECT *. Only the panics need the type change.
Expected behavior
S returns the same results as the new session, or fails every query on t with an error saying that the table's schema changed after the provider was built. It never returns a wrong count or panics.
Additional context
- This is another concrete case of #24 §2, on the read path. #56 is the write-path counterpart.
- #42 is related but different: there the provider declares the table's current schema while the scan binds to an older snapshot's schema.
- #48 lets Ballista rebuild a provider with the schema the client planned against, so a rebuilt provider behaves the same way.
- Possible fixes:
- in
scan, check each declared field against the field of the same name in the scanned snapshot's schema (field id fromPARQUET:field_id, type and nullability), and return an error on a mismatch - resolve the table, and with it the schema, per query (#24)
- in
- Lingua principale
- Rust
- Stelle
- 30
- Fork
- 30
- Merge medio
- 3g 19h
- PR unite (30g)
- 15
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di apache/datafusion-iceberg
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
apache/datafusion-iceberg#100 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
apache/datafusion-iceberg#85 · 1 commento · 5 reazioni ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 35/100
apache/datafusion-iceberg#84 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 35/100
apache/datafusion-iceberg#75 ·
I maintainer di solito rispondono entro 1 giorno
-
Slow CI runnersAperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 18/100
apache/datafusion-iceberg#74 ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di apache/datafusion-iceberg
Issue simili
-
An EXE started from the shell with MAXALLOC FFFFh leaves a 0-paragraph free block after its ownApertabug low-priority
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
dividebysandwich/rust-dos#629 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
eliasstravik/herdr-projects#758 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
-
enhancement needs:sonnet ready-for-agent
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 1 giorno