#1348 - ENH: Add support for bitwiseAnd / bitwiseAny / bitwiseAll .. expressions for use with integer/long "Flags" properties

This commit is contained in:
Rob Bygrave
2018-03-14 16:23:16 +13:00
parent 4bc1f2ede4
commit 602a35d0b6
29 changed files with 652 additions and 52 deletions
@@ -327,6 +327,30 @@ public interface ExpressionFactory {
*/
Expression allEq(Map<String, Object> propertyMap);
/**
* Add expression for ANY of the given bit flags to be set.
*
* @param propertyName The property that holds the flags value
* @param flags The flags we are looking for
*/
Expression bitwiseAny(String propertyName, long flags);
/**
* Add expression for ALL of the given bit flags to be set.
*
* @param propertyName The property that holds the flags value
* @param flags The flags we are looking for
*/
Expression bitwiseAll(String propertyName, long flags);
/**
* Add bitwise AND expression of the given bit flags to compare with the match/mask.
*
* @param propertyName The property that holds the flags value
* @param flags The flags we are looking for
*/
Expression bitwiseAnd(String propertyName, long flags, long match);
/**
* Add raw expression with a single parameter.
* <p>
+51 -4
View File
@@ -482,7 +482,7 @@ public interface ExpressionList<T> {
/**
* Extended version for setDistinct in conjunction with "findSingleAttributeList";
*
* <p>
* <pre>{@code
*
* List<CountedValue<Order.Status>> orderStatusCount =
@@ -878,7 +878,7 @@ public interface ExpressionList<T> {
/**
* In - using a subQuery.
*
* <p>
* This is exactly the same as in() and provided due to "in" being a Kotlin keyword
* (and hence to avoid the slightly ugly escaping when using in() in Kotlin)
*/
@@ -888,7 +888,7 @@ public interface ExpressionList<T> {
/**
* In - property has a value in the array of values.
*
* <p>
* This is exactly the same as in() and provided due to "in" being a Kotlin keyword
* (and hence to avoid the slightly ugly escaping when using in() in Kotlin)
*/
@@ -898,7 +898,7 @@ public interface ExpressionList<T> {
/**
* In - property has a value in the collection of values.
*
* <p>
* This is exactly the same as in() and provided due to "in" being a Kotlin keyword
* (and hence to avoid the slightly ugly escaping when using in() in Kotlin)
*/
@@ -996,6 +996,53 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> arrayIsNotEmpty(String propertyName);
/**
* Add expression for ANY of the given bit flags to be set.
* <pre>{@code
*
* where().bitwiseAny("flags", BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
*
* }</pre>
*
* @param propertyName The property that holds the flags value
* @param flags The flags we are looking for
*/
ExpressionList<T> bitwiseAny(String propertyName, long flags);
/**
* Add expression for ALL of the given bit flags to be set.
* <p>
* <pre>{@code
*
* where().bitwiseAll("flags", BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
*
* }</pre>
*
* @param propertyName The property that holds the flags value
* @param flags The flags we are looking for
*/
ExpressionList<T> bitwiseAll(String propertyName, long flags);
/**
* Add bitwise AND expression of the given bit flags to compare with the match/mask.
* <p>
* <pre>{@code
*
* // Flags Bulk + Size = Size
* // ... meaning Bulk is not set and Size is set
*
* long selectedFlags = BwFlags.HAS_BULK + BwFlags.HAS_SIZE;
* long mask = BwFlags.HAS_SIZE; // Only Size flag set
*
* where().bitwiseAnd("flags", selectedFlags, mask)
*
* }</pre>
*
* @param propertyName The property that holds the flags value
* @param flags The flags we are looking for
*/
ExpressionList<T> bitwiseAnd(String propertyName, long flags, long match);
/**
* Add raw expression with a single parameter.
* <p>
@@ -1,8 +1,8 @@
package io.ebeaninternal.api;
import io.ebeaninternal.server.core.DbExpressionHandler;
import io.ebeaninternal.server.core.SpiOrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.expression.platform.DbExpressionHandler;
import java.util.List;
@@ -1,19 +0,0 @@
package io.ebeaninternal.server.core;
/**
* Not supported JSON or ARRAY expression handler.
*/
abstract class BaseDbExpression implements DbExpressionHandler {
private final String concatOperator;
BaseDbExpression(String concatOperator) {
this.concatOperator = concatOperator;
}
@Override
public String getConcatOperator() {
return concatOperator;
}
}
@@ -42,6 +42,8 @@ import io.ebeaninternal.server.deploy.parse.DeployInherit;
import io.ebeaninternal.server.deploy.parse.DeployUtil;
import io.ebeaninternal.server.dto.DtoBeanManager;
import io.ebeaninternal.server.expression.DefaultExpressionFactory;
import io.ebeaninternal.server.expression.platform.DbExpressionHandler;
import io.ebeaninternal.server.expression.platform.DbExpressionHandlerFactory;
import io.ebeaninternal.server.persist.Binder;
import io.ebeaninternal.server.persist.DefaultPersister;
import io.ebeaninternal.server.persist.platform.MultiValueBind;
@@ -267,18 +269,7 @@ public class InternalConfiguration {
* Return the JSON expression handler for the given database platform.
*/
private DbExpressionHandler getDbExpressionHandler(DatabasePlatform databasePlatform) {
Platform platform = databasePlatform.getPlatform();
String concatOperator = databasePlatform.getConcatOperator();
switch (platform) {
case POSTGRES:
return new PostgresDbExpression(concatOperator);
case ORACLE:
return new OracleDbExpression(concatOperator);
case SQLSERVER:
return new SqlServerDbExpression(concatOperator);
default:
return new BasicDbExpression(concatOperator);
}
return DbExpressionHandlerFactory.from(databasePlatform);
}
private MultiValueBind createMultiValueBind(Platform platform) {
@@ -0,0 +1,63 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import java.io.IOException;
/**
* Bitwise expression.
*/
class BitwiseExpression extends AbstractExpression {
protected final BitwiseOp operator;
private final String compare;
protected final long flags;
private final long match;
BitwiseExpression(String propertyName, BitwiseOp operator, long flags, String compare, long match) {
super(propertyName);
this.operator = operator;
this.flags = flags;
this.compare = compare;
this.match = match;
}
@Override
public void writeDocQuery(DocQueryContext context) throws IOException {
throw new IllegalStateException("Not supported for document queries");
}
@Override
public void queryPlanHash(StringBuilder builder) {
builder.append("Bitwise[");
builder.append(propName).append(" op:").append(operator).append(" cp:").append(compare);
builder.append(" ?2]");
}
@Override
public int queryBindHash() {
return Long.hashCode(flags);
}
@Override
public boolean isSameByBind(SpiExpression other) {
BitwiseExpression that = (BitwiseExpression) other;
return operator == that.operator && compare.equals(that.compare) && flags == that.flags && match == that.match;
}
@Override
public void addSql(SpiExpressionRequest request) {
// Use DB specific expression handling
request.getDbPlatformHandler().bitwise(request, propName, operator, flags, compare, match);
}
@Override
public void addBindValues(SpiExpressionRequest request) {
request.addBindValue(flags);
request.addBindValue(match);
}
}
@@ -0,0 +1,22 @@
package io.ebeaninternal.server.expression;
/**
* Bitwise expression operator.
*/
public enum BitwiseOp {
/**
* All flags are set (derived expression BITAND flags == flags).
*/
ALL,
/**
* Any flags are set (derived expression BITAND flags != 0).
*/
ANY,
/**
* BITAND flags == match.
*/
AND
}
@@ -136,6 +136,21 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
return new ArrayIsEmptyExpression(propertyName, false);
}
@Override
public Expression bitwiseAny(String propertyName, long flags) {
return new BitwiseExpression(propertyName, BitwiseOp.ANY, flags, "!=", 0L);
}
@Override
public Expression bitwiseAll(String propertyName, long flags) {
return new BitwiseExpression(propertyName, BitwiseOp.ALL, flags, "=", flags);
}
@Override
public Expression bitwiseAnd(String propertyName, long flags, long match) {
return new BitwiseExpression(propertyName, BitwiseOp.AND, flags, "=", match);
}
/**
* Equal To - property equal to the given value.
*/
@@ -699,6 +699,24 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return this;
}
@Override
public ExpressionList<T> bitwiseAny(String propertyName, long flags) {
add(expr.bitwiseAny(propertyName, flags));
return this;
}
@Override
public ExpressionList<T> bitwiseAll(String propertyName, long flags) {
add(expr.bitwiseAll(propertyName, flags));
return this;
}
@Override
public ExpressionList<T> bitwiseAnd(String propertyName, long flags, long match) {
add(expr.bitwiseAnd(propertyName, flags, match));
return this;
}
@Override
public ExpressionList<T> eq(String propertyName, Object value) {
add(expr.eq(propertyName, value));
@@ -2,10 +2,10 @@ package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.SpiExpressionList;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.core.DbExpressionHandler;
import io.ebeaninternal.server.core.SpiOrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.DeployParser;
import io.ebeaninternal.server.expression.platform.DbExpressionHandler;
import io.ebeaninternal.server.persist.Binder;
import io.ebeaninternal.server.type.DataBind;
@@ -533,6 +533,21 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
return exprList.arrayIsNotEmpty(propertyName);
}
@Override
public ExpressionList<T> bitwiseAny(String propertyName, long flags) {
return exprList.bitwiseAny(propertyName, flags);
}
@Override
public ExpressionList<T> bitwiseAll(String propertyName, long flags) {
return exprList.bitwiseAll(propertyName, flags);
}
@Override
public ExpressionList<T> bitwiseAnd(String propertyName, long flags, long match) {
return exprList.bitwiseAnd(propertyName, flags, match);
}
@Override
public ExpressionList<T> ge(String propertyName, Object value) {
return exprList.ge(propertyName, value);
@@ -0,0 +1,60 @@
package io.ebeaninternal.server.expression.platform;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.expression.BitwiseOp;
/**
* Not supported JSON or ARRAY expression handler.
*/
abstract class BaseDbExpression implements DbExpressionHandler {
private final String concatOperator;
BaseDbExpression(String concatOperator) {
this.concatOperator = concatOperator;
}
@Override
public String getConcatOperator() {
return concatOperator;
}
@Override
public void bitwise(SpiExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match) {
String bitOp = bitOp(operator);
request.append("(").append(propName).append(" ").append(bitOp).append(" ? ").append(compare).append(" ?)");
}
private String bitOp(BitwiseOp operator) {
switch (operator) {
case ANY:
case AND:
case ALL:
return "&";
default:
throw new IllegalArgumentException("Unknown Bitwise operator " + operator + " not handled?");
}
}
/**
* Common alternative where the bitwise operation is a function (specifically bitand is used - H2 and Oracle).
*/
protected void bitwiseFunction(SpiExpressionRequest request, String propName, BitwiseOp operator, String compare) {
String funcName = functionName(operator);
request.append(funcName).append("(").append(propName).append(", ?) ").append(compare).append(" ?");
}
protected String functionName(BitwiseOp operator) {
switch (operator) {
case AND:
case ANY:
case ALL:
return "bitand";
default:
throw new IllegalArgumentException("Unknown Bitwise operator " + operator + " not handled?");
}
}
}
@@ -1,4 +1,4 @@
package io.ebeaninternal.server.core;
package io.ebeaninternal.server.expression.platform;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.expression.Op;
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.core;
package io.ebeaninternal.server.expression.platform;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.expression.BitwiseOp;
import io.ebeaninternal.server.expression.Op;
/**
@@ -28,4 +29,8 @@ public interface DbExpressionHandler {
*/
void arrayIsEmpty(SpiExpressionRequest request, String propName, boolean empty);
/**
* Add the bitwise expression.
*/
void bitwise(SpiExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match);
}
@@ -0,0 +1,31 @@
package io.ebeaninternal.server.expression.platform;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
public class DbExpressionHandlerFactory {
/**
* Create and return the appropriate platform specific handing of expressions.
*/
public static DbExpressionHandler from(DatabasePlatform databasePlatform) {
Platform platform = databasePlatform.getPlatform();
String concatOperator = databasePlatform.getConcatOperator();
switch (platform) {
case H2:
return new H2DbExpression(concatOperator);
case POSTGRES:
return new PostgresDbExpression(concatOperator);
case MYSQL:
return new MySqlDbExpression(concatOperator);
case ORACLE:
return new OracleDbExpression(concatOperator);
case SQLSERVER:
return new SqlServerDbExpression(concatOperator);
default:
return new BasicDbExpression(concatOperator);
}
}
}
@@ -0,0 +1,20 @@
package io.ebeaninternal.server.expression.platform;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.expression.BitwiseOp;
/**
* H2 handling of platform specific expressions.
*/
class H2DbExpression extends BasicDbExpression {
H2DbExpression(String concatOperator) {
super(concatOperator);
}
@Override
public void bitwise(SpiExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match) {
bitwiseFunction(request, propName, operator, compare);
}
}
@@ -0,0 +1,12 @@
package io.ebeaninternal.server.expression.platform;
/**
* MySql specific handling of platform specific expressions.
*/
class MySqlDbExpression extends BasicDbExpression {
MySqlDbExpression(String concatOperator) {
super(concatOperator);
}
}
@@ -1,10 +1,11 @@
package io.ebeaninternal.server.core;
package io.ebeaninternal.server.expression.platform;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.expression.BitwiseOp;
import io.ebeaninternal.server.expression.Op;
/**
* Oracle JSON expression handler, ARRAY expressions not supported.
* Oracle handling of platform specific expressions. ARRAY expressions not supported.
*/
public class OracleDbExpression extends BaseDbExpression {
@@ -25,6 +26,11 @@ public class OracleDbExpression extends BaseDbExpression {
}
}
@Override
public void bitwise(SpiExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match) {
bitwiseFunction(request, propName, operator, compare);
}
@Override
public void arrayContains(SpiExpressionRequest request, String propName, boolean contains, Object... values) {
throw new IllegalStateException("ARRAY expressions not supported on Oracle");
@@ -1,4 +1,4 @@
package io.ebeaninternal.server.core;
package io.ebeaninternal.server.expression.platform;
/**
* Helper for determining type casting for JSON and ARRAY expressions.
@@ -1,4 +1,4 @@
package io.ebeaninternal.server.core;
package io.ebeaninternal.server.expression.platform;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.expression.Op;
@@ -1,4 +1,4 @@
package io.ebeaninternal.server.core;
package io.ebeaninternal.server.expression.platform;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.expression.Op;
@@ -2,9 +2,9 @@ package io.ebeaninternal.server.persist;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebeaninternal.api.BindParams;
import io.ebeaninternal.server.core.DbExpressionHandler;
import io.ebeaninternal.server.core.Message;
import io.ebeaninternal.server.core.timezone.DataTimeZone;
import io.ebeaninternal.server.expression.platform.DbExpressionHandler;
import io.ebeaninternal.server.persist.platform.MultiValueBind;
import io.ebeaninternal.server.transaction.TransactionManager;
import io.ebeaninternal.server.type.DataBind;