Skip to content

fix: panic and incorrect results in LogFunc::output_ordering() - #11571

Merged
jonahgao merged 2 commits into
apache:mainfrom
jonahgao:log_panic
Jul 24, 2024
Merged

fix: panic and incorrect results in LogFunc::output_ordering()#11571
jonahgao merged 2 commits into
apache:mainfrom
jonahgao:log_panic

Conversation

@jonahgao

Copy link
Copy Markdown
Member

Which issue does this PR close?

Closes #11549.

Rationale for this change

  1. The current implementation assumes log has two parameters, but this does not hold for log(100), which is equivalent to log(10, 100). This will cause panic due to index out of bounds.
  2. It mistakenly treats the second parameter as base, causing incorrect ordering results.

Ref: https://datafusion.apache.org/user-guide/sql/scalar_functions.html#log

What changes are included in this PR?

Are these changes tested?

Yes

Are there any user-facing changes?

No

@github-actions github-actions Bot added the sqllogictest SQL Logic Tests (.slt) label Jul 20, 2024

query TT
EXPLAIN SELECT LOG(c11, c12) as log_c11_base_c12
EXPLAIN SELECT LOG(c12, c11) as log_c11_base_c12

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

base should be placed in the first parameter.

@2010YOUY01 2010YOUY01 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.

Thank you for this rapid fix!

It's not the problem for this PR, but I found this output_ordering calculation is extremely hard to get all cases correct, I think the correct way to implement it is to do some hard coded exact match only for a small number of obvious cases. For the rest cases we could blindly return unordered.

If the example I gave is not mistaken, we can open an issue to address it and fix later.

match (input[0].sort_properties, input[1].sort_properties) {
let (base_sort_properties, num_sort_properties) = if input.len() == 1 {
// log(x) defaults to log(10, x)
(SortProperties::Singleton, input[0].sort_properties)

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.

This change makes sense to me 👍🏼

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.

👍🏻

} else {
(input[0].sort_properties, input[1].sort_properties)
};
match (num_sort_properties, base_sort_properties) {

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.

Looks like the following logic is not completely correct 🤔
e.g.
base is DESC NULLS FIRST
num is ASC NULLS LAST
This implementation will return log(base, num) is ASC NULLS LAST
But the actual output might look like

NULL
NULL
1
2
NULL

Which is unordered.

@jonahgao jonahgao Jul 21, 2024

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I agree with you. I noticed that other logic also does not consider nulls_first, for example

pub fn add(&self, rhs: &Self) -> Self {
match (self, rhs) {
(Self::Singleton, _) => *rhs,
(_, Self::Singleton) => *self,
(Self::Ordered(lhs), Self::Ordered(rhs))
if lhs.descending == rhs.descending =>
{
Self::Ordered(SortOptions {
descending: lhs.descending,
nulls_first: lhs.nulls_first || rhs.nulls_first,
})
}
_ => Self::Unordered,
}
}

Maybe @berkaysynnada can help confirm it.

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.

You are correct. We also need to check the placement of nulls in output_ordering() (and possibly in other implementations of it for multi-input functions). As @jonahgao noticed, impl SortProperties {} needs similar handling as well, but that can be addressed in a separate PR. I can create a ticket for it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed the nulls_first problem for log by 3feeaa8. We can create separate PRs for other fixes.

cc @2010YOUY01 @berkaysynnada

@berkaysynnada berkaysynnada 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.

The fix looks good to me. Thanks, @jonahgao. Could you also address the missing cases of nulls order checks, or do we report a ticket?

match (input[0].sort_properties, input[1].sort_properties) {
let (base_sort_properties, num_sort_properties) = if input.len() == 1 {
// log(x) defaults to log(10, x)
(SortProperties::Singleton, input[0].sort_properties)

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.

👍🏻

} else {
(input[0].sort_properties, input[1].sort_properties)
};
match (num_sort_properties, base_sort_properties) {

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.

You are correct. We also need to check the placement of nulls in output_ordering() (and possibly in other implementations of it for multi-input functions). As @jonahgao noticed, impl SortProperties {} needs similar handling as well, but that can be addressed in a separate PR. I can create a ticket for it.

SortProperties::Singleton,
];
assert_eq!(results, expected);
}

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 adding these tests. output_ordering() API of scalar functions are experimental and needs unit tests (#10595). This is a good example for the rest of them.

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.

output_ordering() API of scalar functions are experimental

What do you mean by "experimental"? If that is the case perhaps we can update the documentation to explain what that means -- I don't see any mention of it

https://docs.rs/datafusion/latest/datafusion/logical_expr/trait.ScalarUDFImpl.html#method.output_ordering

STORED AS CSV
WITH ORDER(c11)
WITH ORDER(c12 DESC)
WITH ORDER(c12 DESC NULLS LAST)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

To make c11 and c12 have the same nulls_first.

@jonahgao

Copy link
Copy Markdown
Member Author

The fix looks good to me. Thanks, @jonahgao. Could you also address the missing cases of nulls order checks, or do we report a ticket?

Filed #11596

@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.

Thank you @jonahgao @berkaysynnada and @2010YOUY01 -- I think this PR is an improvement over what is on main,

As you have pointed out, filing some tickets to track the remaining work would be a good idea.

SortProperties::Singleton,
];
assert_eq!(results, expected);
}

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.

output_ordering() API of scalar functions are experimental

What do you mean by "experimental"? If that is the case perhaps we can update the documentation to explain what that means -- I don't see any mention of it

https://docs.rs/datafusion/latest/datafusion/logical_expr/trait.ScalarUDFImpl.html#method.output_ordering

@jonahgao

Copy link
Copy Markdown
Member Author

Thanks all. The remaining issues are being tracked in ticket #11596

@jonahgao
jonahgao merged commit c8ef545 into apache:main Jul 24, 2024
@jonahgao
jonahgao deleted the log_panic branch July 24, 2024 01:47
@berkaysynnada

Copy link
Copy Markdown
Contributor

What do you mean by "experimental"

@alamb I guess I used a wrong terminology. I meant that output_ordering() methods of ScalarUDF's are not well-tested by slt or unit tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

sqllogictest SQL Logic Tests (.slt)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Crash bug when log() is used in order by clause (SQLancer)

4 participants