Implement SELECT DISTINCT [ON <exp>] as a QueryClause.

This commit is contained in:
Par Winzell
2012-02-03 21:29:16 +00:00
parent 03ee53194b
commit cf47c335a0
7 changed files with 79 additions and 0 deletions
@@ -178,6 +178,25 @@ public class Query<T extends PersistentRecord>
return query; return query;
} }
/**
* Configures the query to return only distinct rows.
*/
public Query<T> distinct ()
{
return distinct(null);
}
/**
* Configures the query to return rows for which the given expression evaluates distinctly.
*/
public Query<T> distinct (SQLExpression<?> distinctOn)
{
checkState(_distinct == null, "Distinct clause is already configured.");
Query<T> query = clone();
query._distinct = new Distinct(distinctOn);
return query;
}
/** /**
* Configures a {@link GroupBy} clause on the supplied group expressions. * Configures a {@link GroupBy} clause on the supplied group expressions.
*/ */
@@ -610,6 +629,7 @@ public class Query<T extends PersistentRecord>
addIfNotNull(clauses, _where); addIfNotNull(clauses, _where);
addAll(clauses, _joins); addAll(clauses, _joins);
addIfNotNull(clauses, _orderBy); addIfNotNull(clauses, _orderBy);
addIfNotNull(clauses, _distinct);
addIfNotNull(clauses, _groupBy); addIfNotNull(clauses, _groupBy);
addIfNotNull(clauses, _limit); addIfNotNull(clauses, _limit);
addIfNotNull(clauses, _fromOverride); addIfNotNull(clauses, _fromOverride);
@@ -636,6 +656,7 @@ public class Query<T extends PersistentRecord>
checkState(_where != null, "Where clause must be specified for delete."); checkState(_where != null, "Where clause must be specified for delete.");
checkState(_joins == null, "Join clauses not supported by delete."); checkState(_joins == null, "Join clauses not supported by delete.");
checkState(_orderBy == null, "OrderBy clause not applicable for delete."); checkState(_orderBy == null, "OrderBy clause not applicable for delete.");
checkState(_distinct == null, "Distinct clause not applicable for delete.");
checkState(_groupBy == null, "GroupBy clause not applicable for delete."); checkState(_groupBy == null, "GroupBy clause not applicable for delete.");
checkState(_fromOverride == null, "FromOverride clause not applicable for delete."); checkState(_fromOverride == null, "FromOverride clause not applicable for delete.");
checkState(_fieldDefs == null, "FieldDefinition clauses not applicable for delete."); checkState(_fieldDefs == null, "FieldDefinition clauses not applicable for delete.");
@@ -678,6 +699,7 @@ public class Query<T extends PersistentRecord>
protected WhereClause _where; protected WhereClause _where;
protected OrderBy _orderBy; protected OrderBy _orderBy;
protected Distinct _distinct;
protected GroupBy _groupBy; protected GroupBy _groupBy;
protected Limit _limit; protected Limit _limit;
protected FromOverride _fromOverride; protected FromOverride _fromOverride;
@@ -64,6 +64,10 @@ public class SelectClause
} else if (clause instanceof FieldDefinition) { } else if (clause instanceof FieldDefinition) {
_disMap.put(((FieldDefinition) clause).getField(), ((FieldDefinition) clause)); _disMap.put(((FieldDefinition) clause).getField(), ((FieldDefinition) clause));
} else if (clause instanceof Distinct) {
checkArgument(_orderBy == null, "Query can't contain multiple Distinct clauses.");
_distinct = (Distinct) clause;
} else if (clause instanceof OrderBy) { } else if (clause instanceof OrderBy) {
checkArgument(_orderBy == null, "Query can't contain multiple OrderBy clauses."); checkArgument(_orderBy == null, "Query can't contain multiple OrderBy clauses.");
_orderBy = (OrderBy) clause; _orderBy = (OrderBy) clause;
@@ -128,6 +132,11 @@ public class SelectClause
return _orderBy; return _orderBy;
} }
public Distinct getDistinct ()
{
return _distinct;
}
public GroupBy getGroupBy () public GroupBy getGroupBy ()
{ {
return _groupBy; return _groupBy;
@@ -182,6 +191,9 @@ public class SelectClause
if (_orderBy != null) { if (_orderBy != null) {
builder.append(", orderBy=").append(_orderBy); builder.append(", orderBy=").append(_orderBy);
} }
if (_distinct != null) {
builder.append(", distinct=").append(_distinct);
}
if (_groupBy != null) { if (_groupBy != null) {
builder.append(", groupBy=").append(_groupBy); builder.append(", groupBy=").append(_groupBy);
} }
@@ -215,6 +227,9 @@ public class SelectClause
/** The order by clause, if any. */ /** The order by clause, if any. */
protected OrderBy _orderBy; protected OrderBy _orderBy;
/** The distinct clause, if any. */
protected Distinct _distinct;
/** The group by clause, if any. */ /** The group by clause, if any. */
protected GroupBy _groupBy; protected GroupBy _groupBy;
@@ -14,6 +14,7 @@ import java.util.Set;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.samskivert.depot.clause.Distinct;
import com.samskivert.util.ByteEnum; import com.samskivert.util.ByteEnum;
import com.samskivert.util.Tuple; import com.samskivert.util.Tuple;
@@ -278,6 +279,17 @@ public abstract class BuildVisitor implements FragmentVisitor<Void>
return null; return null;
} }
public Void visit (Distinct distinct)
{
_builder.append("distinct ");
if (distinct.getDistinctOn() != null) {
_builder.append("on ");
distinct.getDistinctOn().accept(this);
_builder.append(" ");
}
return null;
}
public Void visit (Join join) public Void visit (Join join)
{ {
switch (join.getType()) { switch (join.getType()) {
@@ -345,6 +357,10 @@ public abstract class BuildVisitor implements FragmentVisitor<Void>
checkArgument(!_definitions.containsKey(pClass), checkArgument(!_definitions.containsKey(pClass),
"Can not yet nest SELECTs on the same persistent record."); "Can not yet nest SELECTs on the same persistent record.");
if (selectClause.getDistinct() != null) {
selectClause.getDistinct().accept(this);
}
Map<String, FieldDefinition> definitionMap = Maps.newHashMap(); Map<String, FieldDefinition> definitionMap = Maps.newHashMap();
for (FieldDefinition definition : selectClause.getFieldDefinitions()) { for (FieldDefinition definition : selectClause.getFieldDefinitions()) {
definitionMap.put(definition.getField(), definition); definitionMap.put(definition.getField(), definition);
@@ -9,6 +9,7 @@ import java.lang.reflect.Field;
import com.samskivert.depot.Key; import com.samskivert.depot.Key;
import com.samskivert.depot.PersistentRecord; import com.samskivert.depot.PersistentRecord;
import com.samskivert.depot.clause.Distinct;
import com.samskivert.depot.clause.FieldDefinition; import com.samskivert.depot.clause.FieldDefinition;
import com.samskivert.depot.clause.ForUpdate; import com.samskivert.depot.clause.ForUpdate;
import com.samskivert.depot.clause.FromOverride; import com.samskivert.depot.clause.FromOverride;
@@ -252,6 +253,11 @@ public class ExpressionEvaluator
throw new IllegalArgumentException("Can't evaluate expression: " + exists); throw new IllegalArgumentException("Can't evaluate expression: " + exists);
} }
public Object visit (Distinct distinct)
{
throw new IllegalArgumentException("Can't evaluate expression: " + distinct);
}
public Object visit (GroupBy groupBy) public Object visit (GroupBy groupBy)
{ {
throw new IllegalArgumentException("Can't evaluate expression: " + groupBy); throw new IllegalArgumentException("Can't evaluate expression: " + groupBy);
@@ -6,6 +6,7 @@ package com.samskivert.depot.impl;
import com.samskivert.depot.Key; import com.samskivert.depot.Key;
import com.samskivert.depot.clause.Distinct;
import com.samskivert.depot.clause.FieldDefinition; import com.samskivert.depot.clause.FieldDefinition;
import com.samskivert.depot.clause.ForUpdate; import com.samskivert.depot.clause.ForUpdate;
import com.samskivert.depot.clause.FromOverride; import com.samskivert.depot.clause.FromOverride;
@@ -81,6 +82,7 @@ public interface FragmentVisitor<T>
public T visit (FullText.Rank match); public T visit (FullText.Rank match);
public T visit (ColumnExp<?> columnExp); public T visit (ColumnExp<?> columnExp);
public T visit (Not not); public T visit (Not not);
public T visit (Distinct distinct);
public T visit (GroupBy groupBy); public T visit (GroupBy groupBy);
public T visit (ForUpdate forUpdate); public T visit (ForUpdate forUpdate);
public T visit (OrderBy orderBy); public T visit (OrderBy orderBy);
@@ -18,6 +18,7 @@ import com.samskivert.depot.Ops;
import com.samskivert.depot.PersistentRecord; import com.samskivert.depot.PersistentRecord;
import com.samskivert.depot.annotation.FullTextIndex; import com.samskivert.depot.annotation.FullTextIndex;
import com.samskivert.depot.annotation.GeneratedValue; import com.samskivert.depot.annotation.GeneratedValue;
import com.samskivert.depot.clause.Distinct;
import com.samskivert.depot.clause.OrderBy; import com.samskivert.depot.clause.OrderBy;
import com.samskivert.depot.expression.ColumnExp; import com.samskivert.depot.expression.ColumnExp;
import com.samskivert.depot.expression.SQLExpression; import com.samskivert.depot.expression.SQLExpression;
@@ -132,6 +133,14 @@ public class HSQLBuilder
return super.visit(operator); return super.visit(operator);
} }
public Void visit (Distinct distinct)
{
if (distinct.getDistinctOn() != null) {
throw new IllegalArgumentException("MySQL does not support DISTINCT ON");
}
return super.visit(distinct);
}
@Override public Void visit (DatePart exp) { @Override public Void visit (DatePart exp) {
if (exp.getPart() == Part.EPOCH) { if (exp.getPart() == Part.EPOCH) {
@@ -10,6 +10,7 @@ import java.sql.SQLException;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import com.samskivert.depot.clause.Distinct;
import com.samskivert.util.StringUtil; import com.samskivert.util.StringUtil;
import com.samskivert.depot.PersistentRecord; import com.samskivert.depot.PersistentRecord;
@@ -69,6 +70,14 @@ public class MySQLBuilder
return appendFunctionCall("truncate", exp.getArg()); return appendFunctionCall("truncate", exp.getArg());
} }
public Void visit (Distinct distinct)
{
if (distinct.getDistinctOn() != null) {
throw new IllegalArgumentException("MySQL does not support DISTINCT ON");
}
return super.visit(distinct);
}
@Override public Void visit (DatePart exp) { @Override public Void visit (DatePart exp) {
return appendFunctionCall(getDateFunction(exp.getPart()), exp.getArg()); return appendFunctionCall(getDateFunction(exp.getPart()), exp.getArg());
} }