diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/window/Lag.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/window/Lag.java index e21120ce26..d18a30b77c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/window/Lag.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/window/Lag.java @@ -21,6 +21,7 @@ import org.apache.doris.catalog.FunctionSignature; import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature; +import org.apache.doris.nereids.trees.expressions.literal.Literal; import org.apache.doris.nereids.trees.expressions.literal.NullLiteral; import org.apache.doris.nereids.trees.expressions.shape.TernaryExpression; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; @@ -89,9 +90,18 @@ public class Lag extends WindowFunction implements TernaryExpression, Explicitly return; } if (children().size() >= 2) { - DataType offsetType = getOffset().getDataType(); - if (!offsetType.isNumericType()) { - throw new AnalysisException("The offset of LEAD must be a number:" + this.toSql()); + checkValidParams(getOffset(), true); + if (getOffset() instanceof Literal) { + if (((Literal) getOffset()).getDouble() <= 0) { + throw new AnalysisException( + "The offset parameter of LAG must be a constant positive integer: " + this.toSql()); + } + } else { + throw new AnalysisException( + "The offset parameter of LAG must be a constant positive integer: " + this.toSql()); + } + if (children().size() >= 3) { + checkValidParams(getDefaultValue(), false); } } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/window/Lead.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/window/Lead.java index bc99bc4d54..04b62d7686 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/window/Lead.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/window/Lead.java @@ -21,6 +21,7 @@ import org.apache.doris.catalog.FunctionSignature; import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature; +import org.apache.doris.nereids.trees.expressions.literal.Literal; import org.apache.doris.nereids.trees.expressions.literal.NullLiteral; import org.apache.doris.nereids.trees.expressions.shape.TernaryExpression; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; @@ -90,9 +91,18 @@ public class Lead extends WindowFunction implements TernaryExpression, Explicitl return; } if (children().size() >= 2) { - DataType offsetType = getOffset().getDataType(); - if (!offsetType.isNumericType()) { - throw new AnalysisException("The offset of LEAD must be a number:" + this.toSql()); + checkValidParams(getOffset(), true); + if (getOffset() instanceof Literal) { + if (((Literal) getOffset()).getDouble() <= 0) { + throw new AnalysisException( + "The offset parameter of LEAD must be a constant positive integer: " + this.toSql()); + } + } else { + throw new AnalysisException( + "The offset parameter of LAG must be a constant positive integer: " + this.toSql()); + } + if (children().size() >= 3) { + checkValidParams(getDefaultValue(), false); } } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/window/WindowFunction.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/window/WindowFunction.java index c30372eec2..57ba66ac5a 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/window/WindowFunction.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/window/WindowFunction.java @@ -17,8 +17,10 @@ package org.apache.doris.nereids.trees.expressions.functions.window; +import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.functions.BoundFunction; +import org.apache.doris.nereids.types.DataType; import java.util.List; import java.util.Objects; @@ -46,11 +48,25 @@ public abstract class WindowFunction extends BoundFunction implements SupportWin } WindowFunction that = (WindowFunction) o; return Objects.equals(getName(), that.getName()) - && Objects.equals(children, that.children); + && Objects.equals(children, that.children); } @Override public int hashCode() { return Objects.hash(getName(), children); } + + /** + * LAG/LEAD param must be const, and offset must be number + */ + protected void checkValidParams(Expression param, boolean isOffset) { + DataType type = param.getDataType(); + if (isOffset == true && !type.isNumericType()) { + throw new AnalysisException("The offset of LAG/LEAD must be a number: " + this.toSql()); + } + if (!param.isConstant()) { + throw new AnalysisException( + "The parameter 2 or parameter 3 of LAG/LEAD must be a constant value: " + this.toSql()); + } + } } diff --git a/regression-test/data/correctness_p0/test_always_nullable_window_function.out b/regression-test/data/correctness_p0/test_always_nullable_window_function.out index b4f1f53eb0..93605da266 100644 --- a/regression-test/data/correctness_p0/test_always_nullable_window_function.out +++ b/regression-test/data/correctness_p0/test_always_nullable_window_function.out @@ -1,15 +1,15 @@ -- This file is automatically generated. You should know what you did if you want to edit this -- !select_default -- -21 04-21-11 1 1 1 2 1.0 1 1 1 1 -21 04-21-11 1 1 1 2 1.0 1 1 1 1 -22 04-22-10-21 0 0 1 1 0.5 1 0 0 0 -22 04-22-10-21 0 1 1 2 0.6666666666666666 1 0 0 0 -22 04-22-10-21 1 0 0 1 0.3333333333333333 1 0 1 1 -22 04-22-10-21 1 0 1 1 0.5 1 0 1 1 -23 04-23-10 1 1 1 2 1.0 1 1 1 1 -23 04-23-10 1 1 1 2 1.0 1 1 1 1 -24 02-24-10-21 1 1 1 2 1.0 1 1 1 1 -24 02-24-10-21 1 1 1 2 1.0 1 1 1 1 +21 04-21-11 1 1 1 2 1.0 1 1 \N 1 +21 04-21-11 1 1 1 2 1.0 1 1 1 \N +22 04-22-10-21 0 0 1 1 0.5 1 0 \N 1 +22 04-22-10-21 0 1 1 2 0.6666666666666666 1 0 1 1 +22 04-22-10-21 1 0 0 1 0.3333333333333333 1 0 0 0 +22 04-22-10-21 1 0 1 1 0.5 1 0 0 \N +23 04-23-10 1 1 1 2 1.0 1 1 \N 1 +23 04-23-10 1 1 1 2 1.0 1 1 1 \N +24 02-24-10-21 1 1 1 2 1.0 1 1 \N 1 +24 02-24-10-21 1 1 1 2 1.0 1 1 1 \N -- !select_empty_window -- 21 04-21-11 1 \N \N \N \N \N \N \N \N @@ -24,16 +24,16 @@ 24 02-24-10-21 1 1 1 1 1.0 1 1 \N \N -- !select_default_nullable -- -21 04-21-11 1 1 1 2 1.0 1 1 1 1 -21 04-21-11 1 1 1 2 1.0 1 1 1 1 -22 04-22-10-21 0 0 1 1 0.5 1 0 0 0 -22 04-22-10-21 0 1 1 2 0.6666666666666666 1 0 0 0 -22 04-22-10-21 1 0 0 1 0.3333333333333333 1 0 1 1 -22 04-22-10-21 1 0 1 1 0.5 1 0 1 1 -23 04-23-10 1 1 1 2 1.0 1 1 1 1 -23 04-23-10 1 1 1 2 1.0 1 1 1 1 -24 02-24-10-21 1 1 1 2 1.0 1 1 1 1 -24 02-24-10-21 1 1 1 2 1.0 1 1 1 1 +21 04-21-11 1 1 1 2 1.0 1 1 \N 1 +21 04-21-11 1 1 1 2 1.0 1 1 1 \N +22 04-22-10-21 0 0 1 1 0.5 1 0 \N 1 +22 04-22-10-21 0 1 1 2 0.6666666666666666 1 0 1 1 +22 04-22-10-21 1 0 0 1 0.3333333333333333 1 0 0 0 +22 04-22-10-21 1 0 1 1 0.5 1 0 0 \N +23 04-23-10 1 1 1 2 1.0 1 1 \N 1 +23 04-23-10 1 1 1 2 1.0 1 1 1 \N +24 02-24-10-21 1 1 1 2 1.0 1 1 \N 1 +24 02-24-10-21 1 1 1 2 1.0 1 1 1 \N -- !select_empty_window_nullable -- 21 04-21-11 1 \N \N \N \N \N \N \N \N diff --git a/regression-test/suites/correctness_p0/test_always_nullable_window_function.groovy b/regression-test/suites/correctness_p0/test_always_nullable_window_function.groovy index 52e2f699a2..3ac6f17d8c 100644 --- a/regression-test/suites/correctness_p0/test_always_nullable_window_function.groovy +++ b/regression-test/suites/correctness_p0/test_always_nullable_window_function.groovy @@ -79,8 +79,8 @@ suite("test_always_nullable_window_function") { avg(state) over(partition by myday order by time_col rows BETWEEN 1 preceding AND 1 following) avg_value, max(state) over(partition by myday order by time_col rows BETWEEN 1 preceding AND 1 following) max_value, min(state) over(partition by myday order by time_col rows BETWEEN 1 preceding AND 1 following) min_value, - lag(state, 0, null) over (partition by myday order by time_col) lag_value, - lead(state, 0, null) over (partition by myday order by time_col) lead_value + lag(state, 1, null) over (partition by myday order by time_col) lag_value, + lead(state, 1, null) over (partition by myday order by time_col) lead_value from ${tableName} order by myday, time_col, state; """ qt_select_empty_window """ @@ -104,8 +104,8 @@ suite("test_always_nullable_window_function") { avg(state) over(partition by myday order by time_col rows BETWEEN 1 preceding AND 1 following) avg_value, max(state) over(partition by myday order by time_col rows BETWEEN 1 preceding AND 1 following) max_value, min(state) over(partition by myday order by time_col rows BETWEEN 1 preceding AND 1 following) min_value, - lag(state, 0, null) over (partition by myday order by time_col) lag_value, - lead(state, 0, null) over (partition by myday order by time_col) lead_value + lag(state, 1, null) over (partition by myday order by time_col) lag_value, + lead(state, 1, null) over (partition by myday order by time_col) lead_value from ${nullableTableName} order by myday, time_col, state; """ qt_select_empty_window_nullable """