-
Notifications
You must be signed in to change notification settings - Fork 24.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SQL: rewrite ROUND and TRUNCATE functions with a different optional p…
…arameter handling method (#40242) * Rewrite Round and Truncate functions to have a slightly different approach to handling the optional parameter in the constructor. Until now the optional parameter was considered 0 if the value was missing and the constructor was filling in this value. The current solution is to have the optional parameter as null right until the actual calculation is done. (cherry picked from commit 3e314f8)
- Loading branch information
Showing
16 changed files
with
546 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
115 changes: 115 additions & 0 deletions
115
...a/org/elasticsearch/xpack/sql/expression/function/scalar/math/BinaryOptionalMathPipe.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
package org.elasticsearch.xpack.sql.expression.function.scalar.math; | ||
|
||
import org.elasticsearch.xpack.sql.execution.search.SqlSourceBuilder; | ||
import org.elasticsearch.xpack.sql.expression.Expression; | ||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryOptionalMathProcessor.BinaryOptionalMathOperation; | ||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe; | ||
import org.elasticsearch.xpack.sql.tree.NodeInfo; | ||
import org.elasticsearch.xpack.sql.tree.Source; | ||
|
||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Objects; | ||
|
||
public class BinaryOptionalMathPipe extends Pipe { | ||
|
||
private final Pipe left, right; | ||
private final BinaryOptionalMathOperation operation; | ||
|
||
public BinaryOptionalMathPipe(Source source, Expression expression, Pipe left, Pipe right, BinaryOptionalMathOperation operation) { | ||
super(source, expression, right == null ? Arrays.asList(left) : Arrays.asList(left, right)); | ||
this.left = left; | ||
this.right = right; | ||
this.operation = operation; | ||
} | ||
|
||
@Override | ||
public final Pipe replaceChildren(List<Pipe> newChildren) { | ||
int childrenSize = newChildren.size(); | ||
if (childrenSize > 2 || childrenSize < 1) { | ||
throw new IllegalArgumentException("expected [1 or 2] children but received [" + newChildren.size() + "]"); | ||
} | ||
return replaceChildren(newChildren.get(0), childrenSize == 1 ? null : newChildren.get(1)); | ||
} | ||
|
||
@Override | ||
public final Pipe resolveAttributes(AttributeResolver resolver) { | ||
Pipe newLeft = left.resolveAttributes(resolver); | ||
Pipe newRight = right == null ? right : right.resolveAttributes(resolver); | ||
if (newLeft == left && newRight == right) { | ||
return this; | ||
} | ||
return replaceChildren(newLeft, newRight); | ||
} | ||
|
||
@Override | ||
public boolean supportedByAggsOnlyQuery() { | ||
return right == null ? left.supportedByAggsOnlyQuery() : left.supportedByAggsOnlyQuery() || right.supportedByAggsOnlyQuery(); | ||
} | ||
|
||
@Override | ||
public boolean resolved() { | ||
return left.resolved() && (right == null || right.resolved()); | ||
} | ||
|
||
protected Pipe replaceChildren(Pipe newLeft, Pipe newRight) { | ||
return new BinaryOptionalMathPipe(source(), expression(), newLeft, newRight, operation); | ||
} | ||
|
||
@Override | ||
public final void collectFields(SqlSourceBuilder sourceBuilder) { | ||
left.collectFields(sourceBuilder); | ||
if (right != null) { | ||
right.collectFields(sourceBuilder); | ||
} | ||
} | ||
|
||
@Override | ||
protected NodeInfo<BinaryOptionalMathPipe> info() { | ||
return NodeInfo.create(this, BinaryOptionalMathPipe::new, expression(), left, right, operation); | ||
} | ||
|
||
@Override | ||
public BinaryOptionalMathProcessor asProcessor() { | ||
return new BinaryOptionalMathProcessor(left.asProcessor(), right == null ? null : right.asProcessor(), operation); | ||
} | ||
|
||
public Pipe right() { | ||
return right; | ||
} | ||
|
||
public Pipe left() { | ||
return left; | ||
} | ||
|
||
public BinaryOptionalMathOperation operation() { | ||
return operation; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(left, right, operation); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
if (this == obj) { | ||
return true; | ||
} | ||
|
||
if (obj == null || getClass() != obj.getClass()) { | ||
return false; | ||
} | ||
|
||
BinaryOptionalMathPipe other = (BinaryOptionalMathPipe) obj; | ||
return Objects.equals(left, other.left) | ||
&& Objects.equals(right, other.right) | ||
&& Objects.equals(operation, other.operation); | ||
} | ||
} |
154 changes: 154 additions & 0 deletions
154
.../elasticsearch/xpack/sql/expression/function/scalar/math/BinaryOptionalMathProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
package org.elasticsearch.xpack.sql.expression.function.scalar.math; | ||
|
||
import org.elasticsearch.common.io.stream.StreamInput; | ||
import org.elasticsearch.common.io.stream.StreamOutput; | ||
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; | ||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor; | ||
|
||
import java.io.IOException; | ||
import java.util.Objects; | ||
import java.util.function.BiFunction; | ||
|
||
/** | ||
* Processor for binary mathematical operations that have a second optional parameter. | ||
*/ | ||
public class BinaryOptionalMathProcessor implements Processor { | ||
|
||
public enum BinaryOptionalMathOperation implements BiFunction<Number, Number, Number> { | ||
|
||
ROUND((l, r) -> { | ||
double tenAtScale = Math.pow(10., r.longValue()); | ||
double middleResult = l.doubleValue() * tenAtScale; | ||
int sign = middleResult > 0 ? 1 : -1; | ||
return Math.round(Math.abs(middleResult)) / tenAtScale * sign; | ||
}), | ||
TRUNCATE((l, r) -> { | ||
double tenAtScale = Math.pow(10., r.longValue()); | ||
double g = l.doubleValue() * tenAtScale; | ||
return (((l.doubleValue() < 0) ? Math.ceil(g) : Math.floor(g)) / tenAtScale); | ||
}); | ||
|
||
private final BiFunction<Number, Number, Number> process; | ||
|
||
BinaryOptionalMathOperation(BiFunction<Number, Number, Number> process) { | ||
this.process = process; | ||
} | ||
|
||
@Override | ||
public final Number apply(Number left, Number right) { | ||
if (left == null) { | ||
return null; | ||
} | ||
if (!(left instanceof Number)) { | ||
throw new SqlIllegalArgumentException("A number is required; received [{}]", left); | ||
} | ||
|
||
if (right != null) { | ||
if (!(right instanceof Number)) { | ||
throw new SqlIllegalArgumentException("A number is required; received [{}]", right); | ||
} | ||
if (right instanceof Float || right instanceof Double) { | ||
throw new SqlIllegalArgumentException("An integer number is required; received [{}] as second parameter", right); | ||
} | ||
} else { | ||
right = 0; | ||
} | ||
|
||
return process.apply(left, right); | ||
} | ||
} | ||
|
||
private final Processor left, right; | ||
private final BinaryOptionalMathOperation operation; | ||
public static final String NAME = "mob"; | ||
|
||
public BinaryOptionalMathProcessor(Processor left, Processor right, BinaryOptionalMathOperation operation) { | ||
this.left = left; | ||
this.right = right; | ||
this.operation = operation; | ||
} | ||
|
||
public BinaryOptionalMathProcessor(StreamInput in) throws IOException { | ||
left = in.readNamedWriteable(Processor.class); | ||
right = in.readOptionalNamedWriteable(Processor.class); | ||
operation = in.readEnum(BinaryOptionalMathOperation.class); | ||
} | ||
|
||
@Override | ||
public final void writeTo(StreamOutput out) throws IOException { | ||
out.writeNamedWriteable(left); | ||
out.writeOptionalNamedWriteable(right); | ||
out.writeEnum(operation); | ||
} | ||
|
||
@Override | ||
public Object process(Object input) { | ||
return doProcess(left().process(input), right() == null ? null : right().process(input)); | ||
} | ||
|
||
public Number doProcess(Object left, Object right) { | ||
if (left == null) { | ||
return null; | ||
} | ||
if (!(left instanceof Number)) { | ||
throw new SqlIllegalArgumentException("A number is required; received [{}]", left); | ||
} | ||
|
||
if (right != null) { | ||
if (!(right instanceof Number)) { | ||
throw new SqlIllegalArgumentException("A number is required; received [{}]", right); | ||
} | ||
if (right instanceof Float || right instanceof Double) { | ||
throw new SqlIllegalArgumentException("An integer number is required; received [{}] as second parameter", right); | ||
} | ||
} else { | ||
right = 0; | ||
} | ||
|
||
return operation().apply((Number) left, (Number) right); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
if (this == obj) { | ||
return true; | ||
} | ||
|
||
if (obj == null || getClass() != obj.getClass()) { | ||
return false; | ||
} | ||
|
||
BinaryOptionalMathProcessor other = (BinaryOptionalMathProcessor) obj; | ||
return Objects.equals(left(), other.left()) | ||
&& Objects.equals(right(), other.right()) | ||
&& Objects.equals(operation(), other.operation()); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(left(), right(), operation()); | ||
} | ||
|
||
public Processor left() { | ||
return left; | ||
} | ||
|
||
public Processor right() { | ||
return right; | ||
} | ||
|
||
public BinaryOptionalMathOperation operation() { | ||
return operation; | ||
} | ||
|
||
@Override | ||
public String getWriteableName() { | ||
return NAME; | ||
} | ||
} |
Oops, something went wrong.