Skip to content

fix: validate wrapped negation during type coercion - #20965

Merged
alamb merged 1 commit into
apache:mainfrom
myandpr:20356-logical-planning-validation
Mar 22, 2026
Merged

alamb merged 1 commit into
apache:mainfrom
myandpr:20356-logical-planning-validation

Conversation

@myandpr

@myandpr myandpr commented Mar 16, 2026 •

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

Invalid wrapped negation expressions such as (-'a') IS NULL could still escape type validation during optimization.

This PR keeps the fix in the analyzer/type coercion path, which is where most of DataFusion's type validation already happens, rather than adding SQL-planner-specific validation.

What changes are included in this PR?

  • add type validation for Expr::Negative in TypeCoercionRewriter
  • return an error for invalid negation operands during optimization
  • add focused regression tests for wrapped negation

Are these changes tested?

Yes.

Added:

  • a unit test for (-'a') IS NULL in type_coercion
  • an integration test that verifies the same case fails during into_optimized_plan()

Are there any user-facing changes?

Yes. Invalid wrapped negation expressions now fail during optimization instead of escaping type validation until later phases.

@github-actions github-actions Bot added sql SQL Planner core Core DataFusion crate labels Mar 16, 2026
@myandpr

myandpr commented Mar 16, 2026

Copy link
Copy Markdown
Contributor Author

Hey @alamb , Could you please help trigger CI for this PR? This is my first contribution here.

I’m hoping to contribute more actively to the project going forward, so I’d really appreciate your help. Thanks!

@myandpr
myandpr force-pushed the 20356-logical-planning-validation branch 4 times, most recently from 75d220e to 0e226bd Compare March 16, 2026 17:41

@alamb alamb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the contribution @myandpr 🙏

Comment thread datafusion/sql/src/planner.rs Outdated
let predicate_type = expr.get_type(schema)?;
if !Self::is_allowed_predicate_type(&predicate_type) {
return plan_err!(
"Cannot create filter with non-boolean predicate '{expr}' returning {predicate_type}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

most DataFusion type validation happens during the Analyzer phase, not the sql planner (binding phase)

I agree with @Acfboy s original statement that failing at physical planning time is probably too late, but trying to do this in sql is too early

Among other things it means that this type checking will not apply to queries created via the DataFrame API

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks @alamb , that makes sense.

I agree that doing this in the SQL planner makes the behavior SQL-specific and misses queries built through the DataFrame API.

I’ll rework this toward the analyzer/type coercion path instead, and I’ll take a closer look at which cases are already covered there versus which ones still need to be handled. I’ll also update the regression tests to match that direction.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@alamb Thanks for the feedback. After reading through the earlier discussion in more detail, I realized this was originally part of the follow-up from #20201, where the broader question was how much of this validation should happen earlier, and in which phase.

Based on your comment, I reworked this to keep the validation in the analyzer/type coercion path instead of the SQL planner, since that is where most of DataFusion's type validation already happens and it also applies to DataFrame API queries.

After checking the current behavior more closely, it looks like the specific remaining gap here was wrapped unary negation under IS NULL, such as (-'a') IS NULL, so I narrowed this PR to that case and added focused regression tests for it.

I also opened #20988 to track this case more precisely. Could you please take another look when you have a chance?

Also, it looks like the new CI runs ended in startup_failure before any jobs were created, likely because the workflows need to be re-triggered.
@alamb Could you please help trigger CI again when convenient? Thanks.

@myandpr myandpr changed the title fix: validate clause expressions during logical planning fix: validate wrapped negation during type coercion Mar 17, 2026
@myandpr
myandpr force-pushed the 20356-logical-planning-validation branch from 0e226bd to f83e6d3 Compare March 17, 2026 12:49
@github-actions github-actions Bot added optimizer Optimizer rules and removed sql SQL Planner labels Mar 17, 2026

@alamb alamb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks @myandpr

get_casted_expr_for_bool_op(*expr, self.schema)?,
))),
Expr::Negative(expr) => {
let data_type = expr.get_type(self.schema)?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I double checked that this matches the check in physical expr:

Ok(arg)
} else if !is_signed_numeric(&data_type)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for double checking, and thanks for the careful review.🙏

@alamb
alamb added this pull request to the merge queue Mar 22, 2026
@alamb

alamb commented Mar 22, 2026 •

Copy link
Copy Markdown
Contributor

Thanks again

Merged via the queue into apache:main with commit 93724b0 Mar 22, 2026
7 checks passed
@alamb

alamb commented Mar 22, 2026

Copy link
Copy Markdown
Contributor

@myandpr

myandpr commented Mar 22, 2026

Copy link
Copy Markdown
Contributor Author

🤔 this one seems to have broken CI for some reason:

https://github.com/apache/datafusion/actions/runs/23406116823/job/68085237860

Thanks for catching that. I checked the failing job, and this looks like an sqllogictest expectation mismatch.

It looks like this particular sqllogictest case had not been exercised in the earlier CI runs I saw on the PR. The analyzer change now surfaces this negation error through type_coercion, while the existing expectation in scalar.slt still assumes the older error form.

I’ll open a follow-up PR to update that.

github-merge-queue Bot pushed a commit that referenced this pull request Mar 23, 2026
)

## Which issue does this PR close?

- Fixes CI breakage on main introduced by #20965.

link:
https://github.com/apache/datafusion/actions/runs/23406116823/job/68085237860

## Rationale for this change

#20965 moved invalid negation validation into the analyzer/type coercion
path.

After that change, the `SELECT -'100'` sqllogictest case now reports the
error through `type_coercion`, but `scalar.slt` was still expecting the
older error form. This follow-up updates the sqllogictest expectation to
match the current behavior.

## What changes are included in this PR?

- update the `scalar.slt` expectation for `SELECT -'100'`
- keep the expected error aligned with the analyzer/type coercion error
shape introduced by #20965

## Are these changes tested?

Yes.

I reproduced the failure locally and verified the updated expectation
with:
- `cargo test -p datafusion-sqllogictest --test sqllogictests -- scalar`

## Are there any user-facing changes?

No.

Signed-off-by: yaommen <[email protected]>
@alamb

alamb commented Mar 23, 2026

Copy link
Copy Markdown
Contributor

It looks like this particular sqllogictest case had not been exercised in the earlier CI runs I saw on the PR. The analyzer change now surfaces this negation error through type_coercion, while the existing expectation in scalar.slt still assumes the older error form.

Perhaps it was anewly added test

Thank you for the follow up

de-bgunter pushed a commit to de-bgunter/datafusion that referenced this pull request Mar 24, 2026
## Which issue does this PR close?

- Related to apache#20988 .

## Rationale for this change

Invalid wrapped negation expressions such as `(-'a') IS NULL` could
still escape type validation during optimization.

This PR keeps the fix in the analyzer/type coercion path, which is where
most of DataFusion's type validation already happens, rather than adding
SQL-planner-specific validation.

## What changes are included in this PR?

- add type validation for `Expr::Negative` in `TypeCoercionRewriter`
- return an error for invalid negation operands during optimization
- add focused regression tests for wrapped negation

## Are these changes tested?

Yes.

Added:
- a unit test for `(-'a') IS NULL` in `type_coercion`
- an integration test that verifies the same case fails during
`into_optimized_plan()`

## Are there any user-facing changes?

Yes. Invalid wrapped negation expressions now fail during optimization
instead of escaping type validation until later phases.
de-bgunter pushed a commit to de-bgunter/datafusion that referenced this pull request Mar 24, 2026
…che#21102)

## Which issue does this PR close?

- Fixes CI breakage on main introduced by apache#20965.

link:
https://github.com/apache/datafusion/actions/runs/23406116823/job/68085237860

## Rationale for this change

apache#20965 moved invalid negation validation into the analyzer/type coercion
path.

After that change, the `SELECT -'100'` sqllogictest case now reports the
error through `type_coercion`, but `scalar.slt` was still expecting the
older error form. This follow-up updates the sqllogictest expectation to
match the current behavior.

## What changes are included in this PR?

- update the `scalar.slt` expectation for `SELECT -'100'`
- keep the expected error aligned with the analyzer/type coercion error
shape introduced by apache#20965

## Are these changes tested?

Yes.

I reproduced the failure locally and verified the updated expectation
with:
- `cargo test -p datafusion-sqllogictest --test sqllogictests -- scalar`

## Are there any user-facing changes?

No.

Signed-off-by: yaommen <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Core DataFusion crate optimizer Optimizer rules

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants