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

SQL: Implement GREATEST and LEAST functions #35879

Merged
merged 6 commits into from
Nov 26, 2018
Merged

Conversation

matriv
Copy link
Contributor

@matriv matriv commented Nov 25, 2018

Add GREATEST(expr1, expr2, ... exprN) and LEAST(expr1, expr2, exprN)
functions which are in the family of CONDITIONAL functions.

Implementation follows PostgreSQL behaviour, so the functions return
NULL when all of their arguments evaluate to NULL.

Renamed CoalescePipe and CoalesceProcessor to ConditionalPipe and
ConditionalProcessor respectively, to be able to reuse them for
Greatest and Least evaluations. To achieve that ConditionalOperation
has been added to differentiate between the functionalities at execution
time.

Closes: #35878

Add GREATEST(expr1, expr2, ... exprN) and LEAST(expr1, expr2, exprN)
functions which are in the family of CONDITIONAL functions.

Implementation follows PostgreSQL behaviour, so the functions return
`NULL` when all of their arguments evaluate to `NULL`.

Renamed `CoalescePipe` and `CoalesceProcessor` to `ConditionalPipe` and
`ConditionalProcessor` respectively, to be able to reuse them for
`Greatest` and `Least` evaluations. To achieve that `ConditionalOperation`
has been added to differentiate between the functionalities at execution
time.

Closes: elastic#35878
@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-search

@@ -140,9 +139,6 @@ protected PhysicalPlan rule(ProjectExec project) {
if (pj instanceof ScalarFunction) {
ScalarFunction f = (ScalarFunction) pj;
processors.put(f.toAttribute(), Expressions.pipe(f));
} else if (pj instanceof In) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Leftover: In is already extending ScalarFunction.

Copy link
Member

@costin costin left a comment

Choose a reason for hiding this comment

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

Looks good however the optimizer rule could be improved as well as the class hierarchy.

return least(values);
}

private static Object getMinOrMax(Collection<Object> values, BiFunction<Object, Object, Boolean> comparison) {
Copy link
Member

Choose a reason for hiding this comment

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

Since Min and Max are the actual functions, extremum seems like a better name.

Copy link
Member

Choose a reason for hiding this comment

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

Also can a type (Number) be inferred to the function instead of Object`?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But the arguments can also be Strings.


@Override
public Object fold() {
LinkedHashSet<Object> values = new LinkedHashSet<>(children().size());
Copy link
Member

Choose a reason for hiding this comment

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

Looks like a candidate for Foldables.

}

@Override
protected String scriptMethodName() {
Copy link
Member

Choose a reason for hiding this comment

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

Isn't it possible to move this and the method below to the parent?
Make the function accept a ConditionalOperation and invoke its scriptMethodName and create a ConditionalPipe for it.

@@ -1229,6 +1232,27 @@ protected Expression rule(Expression e) {
return new Coalesce(c.location(), newChildren);
}
}

if (e instanceof Greatest || e instanceof Least) {
Copy link
Member

Choose a reason for hiding this comment

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

The logic is exactly that of Coalesce so simply do a check for Greatest, Least and Coalesce - maybe they have a common parent class so use that then after filtering the children simply call expression.replaceChildren(newChildren).

@@ -68,6 +68,11 @@ public Object fold() {
return NullIfProcessor.apply(children().get(0).fold(), children().get(1).fold());
}

@Override
protected String scriptMethodName() {
Copy link
Member

Choose a reason for hiding this comment

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

?
This is typically an indication that the parent is not generic enough.


public enum ConditionalOperation implements Function<Collection<Object>, Object> {

COALESCE(Conditionals::coalesce, Conditionals::coalesceOnInput),
Copy link
Member

Choose a reason for hiding this comment

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

Looks like coalesce/greatest/least should have a common parent more specialized then the others conditionals.

@matriv
Copy link
Contributor Author

matriv commented Nov 26, 2018

@costin Thanks for the review. Pushed commits to address comments.

Copy link
Member

@costin costin left a comment

Choose a reason for hiding this comment

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

Left some minor polishing comments.
LGTM!

return foldAndAdd(list, to, new LinkedHashSet<>(list.size()));
}

private static <T, C extends Collection<T>> C foldAndAdd(Collection<Expression> expressions, DataType to, C values) {
Copy link
Member

Choose a reason for hiding this comment

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

Please rename it to foldTo - I was thinking the values are folded and added together based on the current name.

*/
public abstract class ArbitraryConditionalFunction extends ConditionalFunction {

private ConditionalOperation operation;
Copy link
Member

Choose a reason for hiding this comment

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

final

return null;
}

static Object coalesceOnInput(List<Processor> processors, Object input) {
Copy link
Member

Choose a reason for hiding this comment

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

The On is superfluous. - coalesceInput, greatestInput, etc...

Copy link
Contributor

@astefan astefan left a comment

Choose a reason for hiding this comment

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

LGTM. Left one question only.

if ((i == 0) || (result == null) || (comparison.apply(value, result) == Boolean.TRUE)) {
result = value;
}
i++;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think here the i logic can be replaced by a boolean? Seems to be a true/false scenario.

@matriv matriv merged commit 3f7cae3 into elastic:master Nov 26, 2018
@matriv matriv deleted the mt/impl-35878 branch November 26, 2018 17:21
matriv added a commit that referenced this pull request Nov 26, 2018
Add GREATEST(expr1, expr2, ... exprN) and LEAST(expr1, expr2, exprN)
functions which are in the family of CONDITIONAL functions.

Implementation follows PostgreSQL behaviour, so the functions return
`NULL` when all of their arguments evaluate to `NULL`.

Renamed `CoalescePipe` and `CoalesceProcessor` to `ConditionalPipe` and
`ConditionalProcessor` respectively, to be able to reuse them for
`Greatest` and `Least` evaluations. To achieve that `ConditionalOperation`
has been added to differentiate between the functionalities at execution
time.

Closes: #35878
@matriv
Copy link
Contributor Author

matriv commented Nov 26, 2018

Backported to 6.x with 44d6c74

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

Successfully merging this pull request may close these issues.

5 participants