Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix DataFrame::cache errors with Plan("Mismatch between schema and batches") #8510

Merged
merged 4 commits into from
Dec 13, 2023
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion datafusion/core/src/dataframe/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ use datafusion_expr::{
};

use async_trait::async_trait;
use datafusion_optimizer::analyzer::type_coercion::TypeCoercion;
use datafusion_optimizer::analyzer::AnalyzerRule;

/// Contains options that control how data is
/// written out from a DataFrame
Expand Down Expand Up @@ -1271,8 +1273,13 @@ impl DataFrame {
/// ```
pub async fn cache(self) -> Result<DataFrame> {
let context = SessionContext::new_with_state(self.session_state.clone());
// type cast
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than explcilty running (only) coercion (which is also run as part of execution) I suggest you use the schema that comes directly from the output stream

Something like (copied from Self::collect): https://docs.rs/datafusion/latest/src/datafusion/dataframe/mod.rs.html#756-760

        let task_ctx = Arc::new(self.task_ctx());
        let plan = self.create_physical_plan().await?;
        let schema = plan.schema();
        collect(plan, task_ctx).await

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your advice

// such as, DataType::Null is normally resolved (via Coercion) to an actual type of the target schema
let analyzed_plan = TypeCoercion::new()
.analyze(self.plan.clone(), self.session_state.config_options())?;
let transform = analyzed_plan.schema().clone();
let mem_table = MemTable::try_new(
SchemaRef::from(self.schema().clone()),
SchemaRef::from(transform.as_ref().clone()),
self.collect_partitioned().await?,
)?;

Expand Down Expand Up @@ -2633,6 +2640,17 @@ mod tests {
Ok(())
}

#[tokio::test]
async fn test_cache_mismatch() -> Result<()> {
let ctx = SessionContext::new();
let df = ctx
.sql("SELECT CASE WHEN true THEN NULL ELSE 1 END")
.await?;
let cache_df = df.cache().await;
assert!(cache_df.is_ok());
Ok(())
}

#[tokio::test]
async fn cache_test() -> Result<()> {
let df = test_table()
Expand Down