#408 Add stubs for WindoFunctions

This commit is contained in:
Timo Westkämper 2013-05-03 21:41:06 +03:00
parent b4f1e9bb1a
commit f2d6a9f3ed
5 changed files with 331 additions and 0 deletions

View File

@ -333,6 +333,113 @@ public final class SQLExpressions {
return DateOperation.create((Class)date.getType(), Ops.DateTimeOps.ADD_DAYS, date, ConstantImpl.create(days));
}
/**
* @param expr
* @return
*/
public static <T extends Number> WindowOver<T> sum(Expression<T> expr) {
return new WindowOver<T>((Class<T>)expr.getType(), Ops.AggOps.SUM_AGG, expr);
}
/**
* @param expr
* @return
*/
public static WindowOver<Long> count(Expression<?> expr) {
return new WindowOver<Long>(Long.class, Ops.AggOps.COUNT_AGG, expr);
}
/**
* @param expr
* @return
*/
public static <T extends Number> WindowOver<T> avg(Expression<T> expr) {
return new WindowOver<T>((Class<T>)expr.getType(), Ops.AggOps.AVG_AGG, expr);
}
/**
* @param expr
* @return
*/
public static <T extends Comparable> WindowOver<T> min(Expression<T> expr) {
return new WindowOver<T>((Class<T>)expr.getType(), Ops.AggOps.MIN_AGG, expr);
}
/**
* @param expr
* @return
*/
public static <T extends Comparable> WindowOver<T> max(Expression<T> expr) {
return new WindowOver<T>((Class<T>)expr.getType(), Ops.AggOps.MAX_AGG, expr);
}
/**
* expr evaluated at the row that is one row after the current row within the partition;
*
* @param expr
* @return
*/
public static <T> WindowOver<T> lead(Expression<T> expr) {
return new WindowOver<T>((Class<T>)expr.getType(), SQLTemplates.LEAD, expr);
}
/**
* expr evaluated at the row that is one row before the current row within the partition
*
* @param expr
* @return
*/
public static <T> WindowOver<T> lag(Expression<T> expr) {
return new WindowOver<T>((Class<T>)expr.getType(), SQLTemplates.LAG, expr);
}
/**
* rank of the current row with gaps; same as row_number of its first peer
*
* @return
*/
public static WindowOver<Long> rank() {
return new WindowOver<Long>(Long.class, SQLTemplates.RANK);
}
/**
* rank of the current row without gaps; this function counts peer groups
*
* @return
*/
public static WindowOver<Long> denseRank() {
return new WindowOver<Long>(Long.class, SQLTemplates.DENSERANK);
}
/**
* number of the current row within its partition, counting from 1
*
* @return
*/
public static WindowOver<Long> rowNumber() {
return new WindowOver<Long>(Long.class, SQLTemplates.ROWNUMBER);
}
/**
* returns value evaluated at the row that is the first row of the window frame
*
* @param expr
* @return
*/
public static <T> WindowOver<T> first(Expression<T> expr) {
return new WindowOver<T>((Class<T>)expr.getType(), SQLTemplates.FIRST, expr);
}
/**
* returns value evaluated at the row that is the last row of the window frame
*
* @param expr
* @return
*/
public static <T> WindowOver<T> last(Expression<T> expr) {
return new WindowOver<T>((Class<T>)expr.getType(), SQLTemplates.LAST, expr);
}
private SQLExpressions() {}
}

View File

@ -46,6 +46,20 @@ public class SQLTemplates extends Templates {
public static final Operator<Object> NEXTVAL = new OperatorImpl<Object>("SQL_NEXTVAL");
public static final Operator<Long> ROWNUMBER = new OperatorImpl<Long>("ROWNUMBER");
public static final Operator<Long> RANK = new OperatorImpl<Long>("RANK");
public static final Operator<Long> DENSERANK = new OperatorImpl<Long>("DENSERANK");
public static final Operator<Object> FIRST = new OperatorImpl<Object>("FIRST");
public static final Operator<Object> LAST = new OperatorImpl<Object>("LAST");
public static final Operator<Object> LEAD = new OperatorImpl<Object>("LAST");
public static final Operator<Object> LAG = new OperatorImpl<Object>("LAST");
public static final SQLTemplates DEFAULT = new SQLTemplates("\"",'\\',false);
public static abstract class Builder {
@ -260,6 +274,14 @@ public class SQLTemplates extends Templates {
add(UNION_ALL, "{0}\nunion all\n{1}", 1);
add(NEXTVAL, "nextval('{0s}')");
add(ROWNUMBER, "row_number()");
add(RANK, "rank()");
add(DENSERANK, "dense_rank()");
add(FIRST, "first({0})");
add(LAST, "last({0})");
add(LEAD, "lead({0})");
add(LAG, "lag({0})");
add(Ops.AggOps.BOOLEAN_ANY, "some({0})");
add(Ops.AggOps.BOOLEAN_ALL, "every({0})");

View File

@ -0,0 +1,115 @@
/*
* Copyright 2011, Mysema Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mysema.query.sql;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nullable;
import com.mysema.query.types.Expression;
import com.mysema.query.types.MutableExpressionBase;
import com.mysema.query.types.Visitor;
import com.mysema.query.types.expr.SimpleExpression;
import com.mysema.query.types.template.SimpleTemplate;
/**
* @author tiwe
*/
public class WindowFunction<A> extends MutableExpressionBase<A> {
private static final long serialVersionUID = -4130672293308756779L;
// TODO : change this to List<OrderSpecifier<?>>
private List<Expression<?>> orderBy = new ArrayList<Expression<?>>();
@Nullable
private Expression<?> partitionBy;
private final Expression<A> target;
private volatile SimpleExpression<A> value;
public WindowFunction(Expression<A> expr) {
super(expr.getType());
this.target = expr;
}
public SimpleExpression<A> getValue() {
if (value == null) {
List<Expression<?>> args = new ArrayList<Expression<?>>();
StringBuilder builder = new StringBuilder();
builder.append("{0} over (");
args.add(target);
if (partitionBy != null) {
builder.append("partition by {1}");
args.add(partitionBy);
}
if (!orderBy.isEmpty()) {
if (partitionBy != null) {
builder.append(" ");
}
builder.append("order by ");
boolean first = true;
for (Expression<?> expr : orderBy) {
if (!first) {
builder.append(", ");
}
builder.append("{" + args.size()+"}");
args.add(expr);
first = false;
}
}
builder.append(")");
value = SimpleTemplate.<A>create(
(Class<A>)target.getType(),
builder.toString(),
args.toArray(new Expression[args.size()]));
}
return value;
}
@Override
public <R,C> R accept(Visitor<R,C> v, C context) {
return getValue().accept(v, context);
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (o instanceof WindowFunction) {
WindowFunction<?> so = (WindowFunction<?>)o;
return so.target.equals(target)
&& so.partitionBy.equals(partitionBy)
&& so.orderBy.equals(orderBy);
} else {
return false;
}
}
public WindowFunction<A> orderBy(Expression<?>... orderBy) {
value = null;
this.orderBy.addAll(Arrays.asList(orderBy));
return this;
}
public WindowFunction<A> partition(Expression<?> partitionBy) {
value = null;
this.partitionBy = partitionBy;
return this;
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2011, Mysema Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mysema.query.sql;
import com.google.common.collect.ImmutableList;
import com.mysema.query.types.Expression;
import com.mysema.query.types.Operator;
import com.mysema.query.types.expr.SimpleOperation;
/**
* WindowOver is the first part of a WindowFunction construction
*
* @author tiwe
*
* @param <T>
*/
public class WindowOver<T> extends SimpleOperation<T> {
private static final long serialVersionUID = 464583892898579544L;
public WindowOver(Class<T> type, Operator<? super T> op) {
super(type, op, ImmutableList.<Expression<?>>of());
}
public WindowOver(Class<T> type, Operator<? super T> op, Expression<?> arg) {
super(type, op, ImmutableList.<Expression<?>>of(arg));
}
public WindowFunction<T> over() {
return new WindowFunction<T>(this);
}
}

View File

@ -0,0 +1,43 @@
package com.mysema.query.sql;
import static org.junit.Assert.*;
import org.junit.Test;
import com.mysema.query.support.Expressions;
import com.mysema.query.types.Expression;
import com.mysema.query.types.path.NumberPath;
public class WindowFunctionTest {
private static String toString(Expression<?> e) {
return new SQLSerializer(SQLTemplates.DEFAULT).handle(e).toString();
}
@Test
public void Complex() {
NumberPath<Long> path = Expressions.numberPath(Long.class, "path");
NumberPath<Long> path2 = Expressions.numberPath(Long.class, "path2");
Expression<?> wf = SQLExpressions.sum(path).over().partition(path2).orderBy(path);
assertEquals("sum(path) over (partition by path2 order by path)", toString(wf));
}
@Test
public void All() {
NumberPath<Long> path = Expressions.numberPath(Long.class, "path");
assertEquals("sum(path)", toString(SQLExpressions.sum(path)));
assertEquals("count(path)", toString(SQLExpressions.count(path)));
assertEquals("avg(path)", toString(SQLExpressions.avg(path)));
assertEquals("min(path)", toString(SQLExpressions.min(path)));
assertEquals("max(path)", toString(SQLExpressions.max(path)));
assertEquals("lead(path)", toString(SQLExpressions.lead(path)));
assertEquals("lag(path)", toString(SQLExpressions.lag(path)));
assertEquals("rank()", toString(SQLExpressions.rank()));
assertEquals("dense_rank()", toString(SQLExpressions.denseRank()));
assertEquals("row_number()", toString(SQLExpressions.rowNumber()));
assertEquals("first(path)", toString(SQLExpressions.first(path)));
assertEquals("last(path)", toString(SQLExpressions.last(path)));
}
}