Give Depot the ability to evaluate SQL expressions without contacting the database. This code is not yet tied into anything, but possible applications, in increasing order of amazingness:

*) We do not currently invalidate cached keysets even when we know for a fact that the associated records have changed (and exactly how they've changed). By storing along with each cached keyset the query datastructure that was used to fetch the keys, we can use the SQLExpression evaluator to do a secondary invalidation sweep over the records when they're requested and make sure they still match the WHERE clause of the original query. If they don't, we toss'em from the cached keyset. This basically means we can trust our cached keysets much more and increase the amount of time we keep them around in the cache.

 *) Extending the first idea, we can do more than lazily invalidate parts of cached collections. We can, in fact, automatically adjust every relevant cached collection each time a record is deleted, inserted or modified. In theory, we could guarantee perpetual veracity of all cached collection queries. It's not a given that this is in every way a good idea, but it's a very interesting one to investigate.

As part of this change, ExpressionVisitor methods have to return a value. Slightly less elegant, but of greater general utility.
This commit is contained in:
Par Winzell
2009-04-13 17:19:01 +00:00
parent b8ac3750eb
commit 859b357d7b
31 changed files with 488 additions and 176 deletions
+5 -5
View File
@@ -46,7 +46,7 @@ public class Key<T extends PersistentRecord> extends WhereClause
implements SQLExpression, CacheKey, ValidatingCacheInvalidator, Serializable
{
/** Handles the matching of the key columns to its bound values. This is needed so that we can
* combine a buncy of keys into a {@link KeySet}. */
* combine a bunch of keys into a {@link KeySet}. */
public static class Expression<U extends PersistentRecord> implements SQLExpression
{
public Expression (Class<U> pClass, Comparable<?>[] values) {
@@ -59,8 +59,8 @@ public class Key<T extends PersistentRecord> extends WhereClause
public Comparable<?>[] getValues () {
return _values;
}
public void accept (ExpressionVisitor builder) {
builder.visit(this);
public Object accept (ExpressionVisitor<?> builder) {
return builder.visit(this);
}
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet) {
classSet.add(getPersistentClass());
@@ -186,9 +186,9 @@ public class Key<T extends PersistentRecord> extends WhereClause
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from CacheKey
+2 -2
View File
@@ -294,9 +294,9 @@ public abstract class KeySet<T extends PersistentRecord> extends WhereClause
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from ValidatingCacheInvalidator
@@ -81,9 +81,9 @@ public class FieldDefinition implements QueryClause
}
// from SQLExpression
public void accept (ExpressionVisitor visitor)
public Object accept (ExpressionVisitor<?> visitor)
{
visitor.visit(this);
return visitor.visit(this);
}
/** The name of the field on the persistent object to override. */
@@ -31,9 +31,9 @@ import com.samskivert.depot.impl.ExpressionVisitor;
public class ForUpdate implements QueryClause
{
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from SQLExpression
@@ -63,9 +63,9 @@ public class FromOverride implements QueryClause
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
@Override // from Object
@@ -42,15 +42,14 @@ public class GroupBy implements QueryClause
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from SQLExpression
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
{
// I can't imagine a GROUP BY clause bringing in new tables... ?
}
/** The expressions that are generated for the clause. */
@@ -62,9 +62,9 @@ public class InsertClause implements QueryClause
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
protected Class<? extends PersistentRecord> _pClass;
@@ -74,9 +74,9 @@ public class Join implements QueryClause
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from SQLExpression
@@ -47,9 +47,9 @@ public class Limit implements QueryClause
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from SQLExpression
@@ -76,9 +76,9 @@ public class OrderBy implements QueryClause
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from SQLExpression
@@ -189,9 +189,9 @@ public class SelectClause<T extends PersistentRecord> implements QueryClause
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
@Override // from Object
@@ -73,9 +73,9 @@ public class Where extends WhereClause
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from SQLExpression
@@ -52,9 +52,9 @@ public class ColumnExp
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from SQLExpression
@@ -39,9 +39,9 @@ public class EpochSeconds implements SQLExpression
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from SQLExpression
@@ -20,10 +20,10 @@
package com.samskivert.depot.expression;
import com.samskivert.util.StringUtil;
import java.util.Collection;
import com.samskivert.util.StringUtil;
import com.samskivert.depot.PersistentRecord;
import com.samskivert.depot.impl.ExpressionVisitor;
@@ -42,9 +42,9 @@ public class FunctionExp implements SQLExpression
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from SQLExpression
@@ -38,9 +38,9 @@ public class LiteralExp
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from SQLExpression
@@ -31,13 +31,27 @@ import com.samskivert.depot.impl.SQLBuilder;
*/
public interface SQLExpression
{
public static final class NoValue
{
public NoValue (String reason)
{
_reason = reason;
}
public String toString () {
return "[unknown value, reason=" + _reason + "]";
}
protected String _reason;
}
/**
* Most uses of this class have been implemented with a visitor pattern. Create your own
* {@link ExpressionVisitor} and call this method with it.
*
* @see SQLBuilder
*/
public void accept (ExpressionVisitor builder);
public Object accept (ExpressionVisitor<?> visitor);
/**
* Adds all persistent classes that are brought into the SQL context by this clause: FROM
@@ -31,15 +31,15 @@ import com.samskivert.depot.impl.ExpressionVisitor;
public class ValueExp
implements SQLExpression
{
public ValueExp (Object _value)
public ValueExp (Object value)
{
this._value = _value;
_value = value;
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from SQLExpression
@@ -71,7 +71,7 @@ import com.samskivert.depot.impl.clause.UpdateClause;
* Implements the base functionality of the SQL-building pass of {@link SQLBuilder}. Dialectal
* subclasses of this should be created and returned from {@link SQLBuilder#getBuildVisitor()}.
*/
public abstract class BuildVisitor implements ExpressionVisitor
public abstract class BuildVisitor implements ExpressionVisitor<Void>
{
public String getQuery ()
{
@@ -83,7 +83,7 @@ public abstract class BuildVisitor implements ExpressionVisitor
return _bindables;
}
public void visit (FromOverride override)
public Void visit (FromOverride override)
{
_builder.append(" from " );
List<Class<? extends PersistentRecord>> from = override.getFromClasses();
@@ -95,24 +95,27 @@ public abstract class BuildVisitor implements ExpressionVisitor
_builder.append(" as ");
appendTableAbbreviation(from.get(ii));
}
return null;
}
public void visit (FieldDefinition definition)
public Void visit (FieldDefinition definition)
{
definition.getDefinition().accept(this);
if (_enableAliasing) {
_builder.append(" as ");
appendIdentifier(definition.getField());
}
return null;
}
public void visit (WhereClause where)
public Void visit (WhereClause where)
{
_builder.append(" where ");
where.getWhereExpression().accept(this);
return null;
}
public void visit (Key.Expression<? extends PersistentRecord> key)
public Void visit (Key.Expression<? extends PersistentRecord> key)
{
Class<? extends PersistentRecord> pClass = key.getPersistentClass();
String[] keyFields = DepotUtil.getKeyFields(pClass);
@@ -134,9 +137,10 @@ public abstract class BuildVisitor implements ExpressionVisitor
bindValue(values[ii]);
}
}
return null;
}
public void visit (FunctionExp functionExp)
public Void visit (FunctionExp functionExp)
{
_builder.append(functionExp.getFunction());
_builder.append("(");
@@ -148,11 +152,12 @@ public abstract class BuildVisitor implements ExpressionVisitor
arguments[ii].accept(this);
}
_builder.append(")");
return null;
}
public void visit (MultiOperator multiOperator)
public Void visit (MultiOperator multiOperator)
{
SQLExpression[] conditions = multiOperator.getConditions();
SQLExpression[] conditions = multiOperator.getOperands();
for (int ii = 0; ii < conditions.length; ii++) {
if (ii > 0) {
_builder.append(" ").append(multiOperator.operator()).append(" ");
@@ -161,29 +166,32 @@ public abstract class BuildVisitor implements ExpressionVisitor
conditions[ii].accept(this);
_builder.append(")");
}
return null;
}
public void visit (BinaryOperator binaryOperator)
public Void visit (BinaryOperator binaryOperator)
{
_builder.append('(');
binaryOperator.getLeftHandSide().accept(this);
_builder.append(binaryOperator.operator());
binaryOperator.getRightHandSide().accept(this);
_builder.append(')');
return null;
}
public void visit (IsNull isNull)
public Void visit (IsNull isNull)
{
isNull.getColumn().accept(this);
_builder.append(" is null");
return null;
}
public void visit (In in)
public Void visit (In in)
{
// if the In() expression is empty, replace it with a 'false'
if (in.getValues().length == 0) {
new ValueExp(false).accept(this);
return;
return null;
}
in.getColumn().accept(this);
_builder.append(" in (");
@@ -195,14 +203,15 @@ public abstract class BuildVisitor implements ExpressionVisitor
bindValue(values[ii]);
}
_builder.append(")");
return null;
}
public abstract void visit (EpochSeconds seconds);
public abstract Void visit (EpochSeconds seconds);
public abstract void visit (FullText.Match match);
public abstract void visit (FullText.Rank rank);
public abstract Void visit (FullText.Match match);
public abstract Void visit (FullText.Rank rank);
public void visit (Case caseExp)
public Void visit (Case caseExp)
{
_builder.append("(case ");
for (Tuple<SQLExpression, SQLExpression> tuple : caseExp.getWhenExps()) {
@@ -217,21 +226,24 @@ public abstract class BuildVisitor implements ExpressionVisitor
elseExp.accept(this);
}
_builder.append(" end)");
return null;
}
public void visit (ColumnExp columnExp)
public Void visit (ColumnExp columnExp)
{
appendRhsColumn(columnExp.getPersistentClass(), columnExp.getField());
return null;
}
public void visit (Not not)
public Void visit (Not not)
{
_builder.append(" not (");
not.getCondition().accept(this);
_builder.append(")");
return null;
}
public void visit (GroupBy groupBy)
public Void visit (GroupBy groupBy)
{
_builder.append(" group by ");
@@ -242,14 +254,16 @@ public abstract class BuildVisitor implements ExpressionVisitor
}
values[ii].accept(this);
}
return null;
}
public void visit (ForUpdate forUpdate)
public Void visit (ForUpdate forUpdate)
{
_builder.append(" for update ");
return null;
}
public void visit (OrderBy orderBy)
public Void visit (OrderBy orderBy)
{
_builder.append(" order by ");
@@ -262,9 +276,10 @@ public abstract class BuildVisitor implements ExpressionVisitor
values[ii].accept(this);
_builder.append(" ").append(orders[ii]);
}
return null;
}
public void visit (Join join)
public Void visit (Join join)
{
switch (join.getType()) {
case INNER:
@@ -282,31 +297,36 @@ public abstract class BuildVisitor implements ExpressionVisitor
appendTableAbbreviation(join.getJoinClass());
_builder.append(" on ");
join.getJoinCondition().accept(this);
return null;
}
public void visit (Limit limit)
public Void visit (Limit limit)
{
_builder.append(" limit ").append(limit.getCount()).
append(" offset ").append(limit.getOffset());
return null;
}
public void visit (LiteralExp literalExp)
public Void visit (LiteralExp literalExp)
{
_builder.append(literalExp.getText());
return null;
}
public void visit (ValueExp valueExp)
public Void visit (ValueExp valueExp)
{
bindValue(valueExp.getValue());
return null;
}
public void visit (Exists<? extends PersistentRecord> exists)
public Void visit (Exists<? extends PersistentRecord> exists)
{
_builder.append("exists ");
exists.getSubClause().accept(this);
return null;
}
public void visit (SelectClause<? extends PersistentRecord> selectClause)
public Void visit (SelectClause<? extends PersistentRecord> selectClause)
{
Class<? extends PersistentRecord> pClass = selectClause.getPersistentClass();
boolean isInner = _innerClause;
@@ -401,9 +421,10 @@ public abstract class BuildVisitor implements ExpressionVisitor
if (isInner) {
_builder.append(")");
}
return null;
}
public void visit (UpdateClause<? extends PersistentRecord> updateClause)
public Void visit (UpdateClause<? extends PersistentRecord> updateClause)
{
if (updateClause.getWhereClause() == null) {
throw new IllegalArgumentException(
@@ -436,9 +457,10 @@ public abstract class BuildVisitor implements ExpressionVisitor
}
}
updateClause.getWhereClause().accept(this);
return null;
}
public void visit (DeleteClause deleteClause)
public Void visit (DeleteClause deleteClause)
{
_builder.append("delete from ");
appendTableName(deleteClause.getPersistentClass());
@@ -446,9 +468,10 @@ public abstract class BuildVisitor implements ExpressionVisitor
appendTableAbbreviation(deleteClause.getPersistentClass());
_builder.append(" ");
deleteClause.getWhereClause().accept(this);
return null;
}
public void visit (InsertClause insertClause)
public Void visit (InsertClause insertClause)
{
Class<? extends PersistentRecord> pClass = insertClause.getPersistentClass();
Object pojo = insertClause.getPojo();
@@ -487,9 +510,10 @@ public abstract class BuildVisitor implements ExpressionVisitor
bindField(pClass, field, pojo);
}
_builder.append(")");
return null;
}
public void visit (CreateIndexClause createIndexClause)
public Void visit (CreateIndexClause createIndexClause)
{
_builder.append("create ");
if (createIndexClause.isUnique()) {
@@ -520,12 +544,14 @@ public abstract class BuildVisitor implements ExpressionVisitor
_defaultType = null;
_builder.append(")");
return null;
}
public void visit (DropIndexClause<? extends PersistentRecord> dropIndexClause)
public Void visit (DropIndexClause<? extends PersistentRecord> dropIndexClause)
{
_builder.append("drop index ");
appendIdentifier(dropIndexClause.getName());
return null;
}
protected void bindValue (Object object)
@@ -57,35 +57,35 @@ import com.samskivert.depot.impl.clause.UpdateClause;
/**
* Enumerates visitation methods for every possible SQL expression type.
*/
public interface ExpressionVisitor
public interface ExpressionVisitor<T>
{
public void visit (FieldDefinition fieldOverride);
public void visit (FunctionExp functionExp);
public void visit (EpochSeconds epochSeconds);
public void visit (FromOverride fromOverride);
public void visit (MultiOperator multiOperator);
public void visit (BinaryOperator binaryOperator);
public void visit (IsNull isNull);
public void visit (In in);
public void visit (FullText.Match match);
public void visit (FullText.Rank rank);
public void visit (ColumnExp columnExp);
public void visit (Not not);
public void visit (GroupBy groupBy);
public void visit (ForUpdate forUpdate);
public void visit (OrderBy orderBy);
public void visit (Join join);
public void visit (Limit limit);
public void visit (LiteralExp literalExp);
public void visit (ValueExp valueExp);
public void visit (WhereClause where);
public void visit (Key.Expression<? extends PersistentRecord> key);
public void visit (Exists<? extends PersistentRecord> exists);
public void visit (SelectClause<? extends PersistentRecord> selectClause);
public void visit (UpdateClause<? extends PersistentRecord> updateClause);
public void visit (DeleteClause deleteClause);
public void visit (InsertClause insertClause);
public void visit (CreateIndexClause createIndexClause);
public void visit (DropIndexClause<? extends PersistentRecord> dropIndexClause);
public void visit (Case caseExp);
public T visit (FieldDefinition fieldOverride);
public T visit (FunctionExp functionExp);
public T visit (EpochSeconds epochSeconds);
public T visit (FromOverride fromOverride);
public T visit (MultiOperator multiOperator);
public T visit (BinaryOperator binaryOperator);
public T visit (IsNull isNull);
public T visit (In in);
public T visit (FullText.Match match);
public T visit (FullText.Rank match);
public T visit (ColumnExp columnExp);
public T visit (Not not);
public T visit (GroupBy groupBy);
public T visit (ForUpdate forUpdate);
public T visit (OrderBy orderBy);
public T visit (Join join);
public T visit (Limit limit);
public T visit (LiteralExp literalExp);
public T visit (ValueExp valueExp);
public T visit (WhereClause where);
public T visit (Key.Expression<? extends PersistentRecord> key);
public T visit (Exists<? extends PersistentRecord> exists);
public T visit (SelectClause<? extends PersistentRecord> selectClause);
public T visit (UpdateClause<? extends PersistentRecord> updateClause);
public T visit (DeleteClause deleteClause);
public T visit (InsertClause insertClause);
public T visit (CreateIndexClause createIndexClause);
public T visit (DropIndexClause<? extends PersistentRecord> dropIndexClause);
public T visit (Case caseExp);
}
@@ -61,7 +61,7 @@ public class HSQLBuilder
{
public class HBuildVisitor extends BuildVisitor
{
@Override public void visit (FullText.Match match)
@Override public Void visit (FullText.Match match)
{
// HSQL doesn't have real full text search, so we fake it by creating a condition like
// (lower(COL1) like '%foo%') OR (lower(COL1) like '%bar%') OR ...
@@ -92,16 +92,17 @@ public class HSQLBuilder
// then just OR them all together and we have our query
_ftsCondition = new Or(bits);
_ftsCondition.accept(this);
return null;
}
@Override public void visit (FullText.Rank rank)
@Override public Void visit (FullText.Rank rank)
{
// not implemented for HSQL
_builder.append("0");
return null;
}
@Override
public void visit (MultiOperator operator)
public Void visit (MultiOperator operator)
{
String op;
// HSQL doesn't handle & and | operators
@@ -112,13 +113,12 @@ public class HSQLBuilder
op = "bitor";
} else {
super.visit(operator);
return;
return super.visit(operator);
}
_builder.append(op).append("(");
boolean virgin = true;
for (SQLExpression bit: operator.getConditions()) {
for (SQLExpression bit: operator.getOperands()) {
if (!virgin) {
_builder.append(", ");
}
@@ -126,26 +126,29 @@ public class HSQLBuilder
virgin = false;
}
_builder.append(")");
return null;
}
public void visit (EpochSeconds epochSeconds)
public Void visit (EpochSeconds epochSeconds)
{
_builder.append("datediff('ss', ");
epochSeconds.getArgument().accept(this);
_builder.append(", '1970-01-01')");
return null;
}
@Override
public void visit (CreateIndexClause createIndexClause)
public Void visit (CreateIndexClause createIndexClause)
{
for (Tuple<SQLExpression, Order> field : createIndexClause.getFields()) {
if (!(field.left instanceof ColumnExp)) {
log.warning("This database can't handle complex indexes. Aborting creation.",
"ixName", createIndexClause.getName());
return;
return null;
}
}
super.visit(createIndexClause);
return null;
}
protected HBuildVisitor (DepotTypes types)
@@ -63,17 +63,19 @@ public class MySQLBuilder
{
public class MSBuildVisitor extends BuildVisitor
{
@Override public void visit (FullText.Match match)
@Override public Void visit (FullText.Match match)
{
renderMatch(match.getDefinition());
return null;
}
@Override public void visit (FullText.Rank rank)
@Override public Void visit (FullText.Rank rank)
{
renderMatch(rank.getDefinition());
return null;
}
@Override public void visit (DeleteClause deleteClause)
@Override public Void visit (DeleteClause deleteClause)
{
_builder.append("delete from ");
appendTableName(deleteClause.getPersistentClass());
@@ -87,30 +89,33 @@ public class MySQLBuilder
} finally {
_types.setUseTableAbbreviations(savedFlag);
}
return null;
}
public void visit (EpochSeconds epochSeconds)
public Void visit (EpochSeconds epochSeconds)
{
_builder.append("unix_timestamp(");
epochSeconds.getArgument().accept(this);
_builder.append(")");
return null;
}
@Override
public void visit (CreateIndexClause createIndexClause)
public Void visit (CreateIndexClause createIndexClause)
{
for (Tuple<SQLExpression, Order> field : createIndexClause.getFields()) {
if (!(field.left instanceof ColumnExp)) {
log.warning("This database can't handle complex indexes. Aborting creation.",
"ixName", createIndexClause.getName());
return;
return null;
}
}
super.visit(createIndexClause);
return null;
}
@Override
public void visit (DropIndexClause<? extends PersistentRecord> dropIndexClause)
public Void visit (DropIndexClause<? extends PersistentRecord> dropIndexClause)
{
// MySQL's indexes are scoped on the table, not on the database, and the
// SQL syntax reflects it: DROP INDEX fooIx on fooTable
@@ -118,6 +123,7 @@ public class MySQLBuilder
appendIdentifier(dropIndexClause.getName());
_builder.append(" on ");
appendTableName(dropIndexClause.getPersistentClass());
return null;
}
protected MSBuildVisitor (DepotTypes types)
@@ -53,16 +53,17 @@ public class PostgreSQLBuilder
public class PGBuildVisitor extends BuildVisitor
{
@Override public void visit (FullText.Match match) {
@Override public Void visit (FullText.Match match) {
appendIdentifier("ftsCol_" + match.getDefinition().getName());
_builder.append(" @@ to_tsquery('").
append(translateFTConfig(getFTIndex(match.getDefinition()).configuration())).
append("', ");
bindValue(massageFTQuery(match.getDefinition()));
_builder.append(")");
return null;
}
@Override public void visit (FullText.Rank rank) {
@Override public Void visit (FullText.Rank rank) {
_builder.append(PG83 ? "ts_rank" : "rank").append("(");
appendIdentifier("ftsCol_" + rank.getDefinition().getName());
_builder.append(", to_tsquery('").
@@ -73,12 +74,14 @@ public class PostgreSQLBuilder
append("', ");
bindValue(massageFTQuery(rank.getDefinition()));
_builder.append("), 1)");
return null;
}
@Override public void visit (EpochSeconds epochSeconds) {
@Override public Void visit (EpochSeconds epochSeconds) {
_builder.append("date_part('epoch', ");
epochSeconds.getArgument().accept(this);
_builder.append(")");
return null;
}
// TODO: enable when we can require 1.6 support
@@ -120,13 +123,12 @@ public class PostgreSQLBuilder
_builder.append("\"").append(field).append("\"");
}
// TODO: enable when we can require 1.6 support
protected PGBuildVisitor (DepotTypes types) {
protected PGBuildVisitor (DepotTypes types)
{
super(types);
}
}
public PostgreSQLBuilder (DepotTypes types)
{
super(types);
@@ -78,9 +78,9 @@ public class CreateIndexClause
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
protected Class<? extends PersistentRecord> _pClass;
@@ -57,9 +57,9 @@ public class DeleteClause
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
/** The type of persistent record on which we operate. */
@@ -56,9 +56,9 @@ public class DropIndexClause<T extends PersistentRecord>
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
protected Class<? extends PersistentRecord> _pClass;
@@ -95,9 +95,9 @@ public class UpdateClause<T extends PersistentRecord>
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
/** The class we're updating. */
@@ -23,15 +23,16 @@ package com.samskivert.depot.operator;
import com.samskivert.depot.expression.SQLExpression;
import com.samskivert.depot.expression.ValueExp;
import com.samskivert.depot.operator.SQLOperator.MultiOperator;
import com.samskivert.util.StringUtil;
/**
* A convenient container for implementations of arithmetic operators. Classes that value brevity
* that feel otherwise will use Arithmetic.Add() and Arithmetic.Sub().
*/
public abstract class Arithmetic
public abstract class Arithmetic extends MultiOperator
{
/** The SQL '+' operator. */
public static class Add extends MultiOperator
public static class Add extends Arithmetic
{
public Add (SQLExpression column, Comparable<?> value)
{
@@ -47,10 +48,22 @@ public abstract class Arithmetic
{
return "+";
}
@Override
public Object evaluate (Object[] operands)
{
return evaluate(operands, "+", new Accumulator<Double>() {
@Override public Double accumulate (Double left, Double right) {
return left + right;
}}, new Accumulator<Long>() {
@Override public Long accumulate (Long left, Long right) {
return left + right;
}});
}
}
/** The SQL '-' operator. */
public static class Sub extends MultiOperator
public static class Sub extends Arithmetic
{
public Sub (SQLExpression column, Comparable<?> value)
{
@@ -66,10 +79,22 @@ public abstract class Arithmetic
{
return "-";
}
@Override
public Object evaluate (Object[] operands)
{
return evaluate(operands, "-", new Accumulator<Double>() {
@Override public Double accumulate (Double left, Double right) {
return left - right;
}}, new Accumulator<Long>() {
@Override public Long accumulate (Long left, Long right) {
return left - right;
}});
}
}
/** The SQL '*' operator. */
public static class Mul extends MultiOperator
public static class Mul extends Arithmetic
{
public Mul (SQLExpression column, Comparable<?> value)
{
@@ -85,10 +110,22 @@ public abstract class Arithmetic
{
return "*";
}
@Override
public Object evaluate (Object[] operands)
{
return evaluate(operands, "*", new Accumulator<Double>() {
@Override public Double accumulate (Double left, Double right) {
return left * right;
}}, new Accumulator<Long>() {
@Override public Long accumulate (Long left, Long right) {
return left * right;
}});
}
}
/** The SQL '/' operator. */
public static class Div extends MultiOperator
public static class Div extends Arithmetic
{
public Div (SQLExpression column, Comparable<?> value)
{
@@ -104,10 +141,27 @@ public abstract class Arithmetic
{
return " / "; // Pad with spaces to work-around a MySQL bug.
}
@Override
public Object evaluate (Object[] operands)
{
for (int i = 1; i < operands.length; i ++) {
if (NUMERICAL.apply(operands[i]) == Double.valueOf(0)) {
return new NoValue("Division by zero in: " + StringUtil.toString(operands));
}
}
return evaluate(operands, "/", new Accumulator<Double>() {
@Override public Double accumulate (Double left, Double right) {
return left / right;
}}, new Accumulator<Long>() {
@Override public Long accumulate (Long left, Long right) {
return left / right;
}});
}
}
/** The SQL '&' operator. */
public static class BitAnd extends MultiOperator
public static class BitAnd extends Arithmetic
{
public BitAnd (SQLExpression column, Comparable<?> value)
{
@@ -123,10 +177,19 @@ public abstract class Arithmetic
{
return "&";
}
@Override
public Object evaluate (Object[] operands)
{
return evaluate(operands, "&", null, new Accumulator<Long>() {
@Override public Long accumulate (Long left, Long right) {
return left & right;
}});
}
}
/** The SQL '|' operator. */
public static class BitOr extends MultiOperator
public static class BitOr extends Arithmetic
{
public BitOr (SQLExpression column, Comparable<?> value)
{
@@ -142,5 +205,37 @@ public abstract class Arithmetic
{
return "|";
}
@Override
public Object evaluate (Object[] operands)
{
return evaluate(operands, "|", null, new Accumulator<Long>() {
@Override public Long accumulate (Long left, Long right) {
return left | right;
}});
}
}
public Arithmetic (SQLExpression column, Comparable<?> value)
{
super(column, new ValueExp(value));
}
public Arithmetic (SQLExpression... values)
{
super(values);
}
protected Object evaluate (
Object[] ops, String name, Accumulator<Double> dAcc, Accumulator<Long> iAcc)
{
if (dAcc != null && all(NUMERICAL, ops)) {
return accumulate(NUMERICAL, ops, 0.0, dAcc);
}
if (iAcc != null && all(INTEGRAL, ops)) {
return accumulate(INTEGRAL, ops, 0L, iAcc);
}
return new NoValue("Non-numeric operand for '" + name + "' (" + ops + ")");
}
}
@@ -54,9 +54,9 @@ public abstract class Conditionals
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from SQLExpression
@@ -90,6 +90,15 @@ public abstract class Conditionals
{
return "=";
}
@Override
public Object evaluate (Object left, Object right)
{
if (left == null || right == null) {
return new NoValue("Null operand to '=': (" + left + ", " + right + ")");
}
return left.equals(right);
}
}
/** The SQL '!=' operator. */
@@ -109,6 +118,15 @@ public abstract class Conditionals
{
return "!=";
}
@Override
public Object evaluate (Object left, Object right)
{
if (left == null || right == null) {
return new NoValue("Null operand to '!=': (" + left + ", " + right + ")");
}
return !left.equals(right);
}
}
/** The SQL '<' operator. */
@@ -128,6 +146,18 @@ public abstract class Conditionals
{
return "<";
}
@Override
public Object evaluate (Object left, Object right)
{
if (all(NUMERICAL, left, right)) {
return NUMERICAL.apply(left) < NUMERICAL.apply(right);
}
if (all(STRING, left, right) || all(DATE, left, right)) {
return compare(STRING, left, right) < 0;
}
return new NoValue("Non-comparable operand to '<': (" + left + ", " + right + ")");
}
}
/** The SQL '<=' operator. */
@@ -147,7 +177,19 @@ public abstract class Conditionals
{
return "<=";
}
}
@Override
public Object evaluate (Object left, Object right)
{
if (all(NUMERICAL, left, right)) {
return NUMERICAL.apply(left) <= NUMERICAL.apply(right);
}
if (all(STRING, left, right) || all(DATE, left, right)) {
return compare(STRING, left, right) <= 0;
}
return new NoValue("Non-comparable operand to '<=': (" + left + ", " + right + ")");
}
}
/** The SQL '>' operator. */
public static class GreaterThan extends SQLOperator.BinaryOperator
@@ -166,6 +208,18 @@ public abstract class Conditionals
{
return ">";
}
@Override
public Object evaluate (Object left, Object right)
{
if (all(NUMERICAL, left, right)) {
return NUMERICAL.apply(left) > NUMERICAL.apply(right);
}
if (all(STRING, left, right) || all(DATE, left, right)) {
return compare(STRING, left, right) > 0;
}
return new NoValue("Non-comparable operand to '>': (" + left + ", " + right + ")");
}
}
/** The SQL '>=' operator. */
@@ -185,6 +239,18 @@ public abstract class Conditionals
{
return ">=";
}
@Override
public Object evaluate (Object left, Object right)
{
if (all(NUMERICAL, left, right)) {
return NUMERICAL.apply(left) >= NUMERICAL.apply(right);
}
if (all(STRING, left, right) || all(DATE, left, right)) {
return compare(STRING, left, right) >= 0;
}
return new NoValue("Non-comparable operand to '>=': (" + left + ", " + right + ")");
}
}
/** The SQL 'in (...)' operator. */
@@ -219,9 +285,9 @@ public abstract class Conditionals
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from SQLExpression
@@ -265,6 +331,12 @@ public abstract class Conditionals
{
return " like ";
}
@Override
public Object evaluate (Object left, Object right)
{
return new NoValue("Like operator not implemented");
}
}
/** The SQL ' exists' operator. */
@@ -275,13 +347,11 @@ public abstract class Conditionals
_clause = clause;
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from SQLExpression
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
{
_clause.addClasses(classSet);
@@ -314,9 +384,9 @@ public abstract class Conditionals
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from SQLExpression
@@ -370,9 +440,9 @@ public abstract class Conditionals
implements SQLOperator
{
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from SQLExpression
@@ -396,9 +466,9 @@ public abstract class Conditionals
implements SQLOperator
{
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
public FullText getDefinition ()
@@ -51,6 +51,23 @@ public abstract class Logic
{
return " or ";
}
@Override
public Object evaluate (Object[] values)
{
boolean anyTrue = false;
for (Object value : values) {
if (value instanceof NoValue) {
return value;
}
if (Boolean.TRUE.equals(value)) {
anyTrue = true;
} else if (!Boolean.FALSE.equals(value)) {
return new NoValue("Non-boolean operand to OR: " + value);
}
}
return anyTrue;
}
}
/**
@@ -72,6 +89,25 @@ public abstract class Logic
{
return " and ";
}
@Override
public Object evaluate (Object[] values)
{
// if all operands are true, return true, else if all operands are boolean, return
// false, else return NO_VALUE
boolean allTrue = true;
for (Object value : values) {
if (value instanceof NoValue) {
return value;
}
if (Boolean.FALSE.equals(value)) {
allTrue = false;
} else if (!Boolean.TRUE.equals(value)) {
return new NoValue("Non-boolean operand to AND: " + value);
}
}
return allTrue;
}
}
/**
@@ -91,9 +127,9 @@ public abstract class Logic
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from SQLExpression
@@ -20,8 +20,13 @@
package com.samskivert.depot.operator;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.samskivert.depot.PersistentRecord;
import com.samskivert.depot.expression.SQLExpression;
import com.samskivert.depot.expression.ValueExp;
@@ -37,31 +42,30 @@ public interface SQLOperator extends SQLExpression
/**
* Represents an operator with any number of operands.
*/
public abstract static class MultiOperator
implements SQLOperator
public abstract static class MultiOperator extends BaseOperator
{
public MultiOperator (SQLExpression ... conditions)
public MultiOperator (SQLExpression ... operands)
{
_conditions = conditions;
_operands = operands;
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from SQLExpression
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
{
for (int ii = 0; ii < _conditions.length; ii ++) {
_conditions[ii].addClasses(classSet);
for (int ii = 0; ii < _operands.length; ii ++) {
_operands[ii].addClasses(classSet);
}
}
public SQLExpression[] getConditions ()
public SQLExpression[] getOperands ()
{
return _conditions;
return _operands;
}
/**
@@ -69,26 +73,31 @@ public interface SQLOperator extends SQLExpression
*/
public abstract String operator ();
/**
* Calculates a value
*/
public abstract Object evaluate (Object[] values);
@Override // from Object
public String toString ()
{
StringBuilder builder = new StringBuilder("(");
for (SQLExpression condition : _conditions) {
for (SQLExpression operand : _operands) {
if (builder.length() > 1) {
builder.append(operator());
}
builder.append(condition);
builder.append(operand);
}
return builder.append(")").toString();
}
protected SQLExpression[] _conditions;
protected SQLExpression[] _operands;
}
/**
* Does the real work for simple binary operators such as Equals.
*/
public abstract static class BinaryOperator implements SQLOperator
public abstract static class BinaryOperator extends BaseOperator
{
public BinaryOperator (SQLExpression lhs, SQLExpression rhs)
{
@@ -102,9 +111,9 @@ public interface SQLOperator extends SQLExpression
}
// from SQLExpression
public void accept (ExpressionVisitor builder)
public Object accept (ExpressionVisitor<?> builder)
{
builder.visit(this);
return builder.visit(this);
}
// from SQLExpression
@@ -114,6 +123,8 @@ public interface SQLOperator extends SQLExpression
_rhs.addClasses(classSet);
}
public abstract Object evaluate (Object left, Object right);
/**
* Returns the string representation of the operator.
*/
@@ -138,4 +149,54 @@ public interface SQLOperator extends SQLExpression
protected SQLExpression _lhs;
protected SQLExpression _rhs;
}
public static abstract class BaseOperator implements SQLOperator
{
public static Function<Object, Long> INTEGRAL = new Function<Object, Long>() {
@Override public Long apply (Object o) {
if ((o instanceof Integer) || (o instanceof Long)) {
return ((Number) o).longValue();
}
return null;
}
};
public static Function<Object, Double> NUMERICAL = new Function<Object, Double>() {
@Override public Double apply (Object o) {
return (o instanceof Number) ? ((Number) o).doubleValue() : null;
}
};
public static Function<Object, String> STRING = new Function<Object, String>() {
@Override public String apply (Object o) {
return (o instanceof String) ? (String) o : null;
}
};
public static Function<Object, Date> DATE = new Function<Object, Date>() {
@Override public Date apply (Object o) {
return (o instanceof Date) ? (Date) o : null;
}
};
public static <S, T> boolean all (Function<S, T> fun, S... obj) {
return Iterables.all(Arrays.asList(obj), Predicates.compose(Predicates.isNull(), fun));
}
public static <S, T extends Comparable<T>> int compare (Function<S, T> fun, S lhs, S rhs) {
return fun.apply(lhs).compareTo(fun.apply(rhs));
}
public static <S, T> T accumulate (Function<S, T> fun, S[] ops, T v, Accumulator<T> acc) {
for (S op : ops) {
v = acc.accumulate(v, fun.apply(op));
}
return v;
}
protected static interface Accumulator<T>
{
T accumulate (T left, T right);
}
}
}