mirror of https://github.com/apache/openjpa.git
OPENJPA-1195: Datastore function for in general usage as well as JPA 2.0. CriteriaQuery Parameter indexing during registration.
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@797257 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
001c9e4fd2
commit
7099c084fb
|
@ -18,6 +18,10 @@
|
|||
*/
|
||||
package org.apache.openjpa.jdbc.kernel.exps;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.openjpa.jdbc.sql.Joins;
|
||||
import org.apache.openjpa.jdbc.sql.Result;
|
||||
import org.apache.openjpa.jdbc.sql.SQLBuffer;
|
||||
|
@ -43,18 +47,21 @@ public class Args
|
|||
* Constructor. Supply values being combined.
|
||||
*/
|
||||
public Args(Val val1, Val val2) {
|
||||
int len1 = (val1 instanceof Args) ? ((Args) val1)._args.length : 1;
|
||||
int len2 = (val2 instanceof Args) ? ((Args) val2)._args.length : 1;
|
||||
this(new Val[]{val1, val2});
|
||||
}
|
||||
|
||||
_args = new Val[len1 + len2];
|
||||
if (val1 instanceof Args)
|
||||
System.arraycopy(((Args) val1)._args, 0, _args, 0, len1);
|
||||
else
|
||||
_args[0] = val1;
|
||||
if (val2 instanceof Args)
|
||||
System.arraycopy(((Args) val2)._args, 0, _args, len1, len2);
|
||||
else
|
||||
_args[len1] = val2;
|
||||
public Args (Val... values) {
|
||||
List<Val> list = new ArrayList<Val>();
|
||||
if (values != null) {
|
||||
for (Val v : values) {
|
||||
if (v instanceof Args) {
|
||||
list.addAll(Arrays.asList(((Args)v)._args));
|
||||
} else {
|
||||
list.add(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
_args = list.toArray(new Val[list.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -166,6 +173,12 @@ public class Args
|
|||
|
||||
public void appendTo(Select sel, ExpContext ctx, ExpState state,
|
||||
SQLBuffer sql, int index) {
|
||||
ArgsExpState astate = (ArgsExpState) state;
|
||||
for (int i = 0; i < _args.length; i++) {
|
||||
_args[i].appendTo(sel, ctx, astate.states[i], sql, index);
|
||||
if (i < _args.length-1)
|
||||
sql.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
public void appendIsEmpty(Select sel, ExpContext ctx, ExpState state,
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 org.apache.openjpa.jdbc.kernel.exps;
|
||||
|
||||
import org.apache.openjpa.kernel.exps.Arguments;
|
||||
|
||||
/**
|
||||
* A unary operator that executes a datastore specific function with zero or more arguments.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class DatastoreFunction extends UnaryOp {
|
||||
private final String _functionName;
|
||||
|
||||
public DatastoreFunction(String name, Class<?> resultType, Arguments args) {
|
||||
super((Val)args);
|
||||
_functionName = name;
|
||||
setImplicitType(resultType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getOperator() {
|
||||
return _functionName;
|
||||
}
|
||||
|
||||
}
|
|
@ -298,6 +298,17 @@ public class JDBCExpressionFactory
|
|||
return new Args((Val) v1, (Val) v2);
|
||||
}
|
||||
|
||||
public Arguments newArgumentList(Value... vs) {
|
||||
if (vs == null)
|
||||
return new Args(null);
|
||||
Val[] vals = new Val[vs.length];
|
||||
int i = 0;
|
||||
for (Value v : vs) {
|
||||
vals[i++] = (Val)v;
|
||||
}
|
||||
return new Args(vals);
|
||||
}
|
||||
|
||||
public Value newUnboundVariable(String name, Class type) {
|
||||
return new Variable(name, type);
|
||||
}
|
||||
|
@ -503,4 +514,8 @@ public class JDBCExpressionFactory
|
|||
val2 = getLiteralRawString(val2);
|
||||
return new NullIfExpression((Val) val1, (Val) val2);
|
||||
}
|
||||
|
||||
public Value newFunction(String functionName, Class<?> resultType, Value... args) {
|
||||
return new DatastoreFunction(functionName, resultType, newArgumentList(args));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,24 +32,30 @@ class Args
|
|||
extends Val
|
||||
implements Arguments {
|
||||
|
||||
private final List _args = new ArrayList(3);
|
||||
private final List<Value> _args = new ArrayList<Value>(3);
|
||||
|
||||
/**
|
||||
* Constructor. Supply values being combined.
|
||||
*/
|
||||
public Args(Value val1, Value val2) {
|
||||
if (val1 instanceof Args)
|
||||
_args.addAll(((Args) val1)._args);
|
||||
else
|
||||
_args.add(val1);
|
||||
if (val2 instanceof Args)
|
||||
_args.addAll(((Args) val2)._args);
|
||||
else
|
||||
_args.add(val2);
|
||||
this(new Value[]{val1, val2});
|
||||
}
|
||||
|
||||
public Args(Value...values) {
|
||||
if (values == null) {
|
||||
return;
|
||||
}
|
||||
for (Value v : values) {
|
||||
if (v instanceof Args) {
|
||||
_args.addAll(((Args)v)._args);
|
||||
} else {
|
||||
_args.add(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Value[] getValues() {
|
||||
return (Value[]) _args.toArray(new Value[_args.size()]);
|
||||
return _args.toArray(new Value[_args.size()]);
|
||||
}
|
||||
|
||||
public Class getType() {
|
||||
|
|
|
@ -270,6 +270,12 @@ public interface ExpressionFactory {
|
|||
*/
|
||||
public Arguments newArgumentList(Value arg1, Value arg2);
|
||||
|
||||
/**
|
||||
* Return a function argument list consisting of the given values, any
|
||||
* of which may itself be an argument list.
|
||||
*/
|
||||
public Arguments newArgumentList(Value... values);
|
||||
|
||||
/**
|
||||
* Return an unbound variable. This method will only be called once for
|
||||
* a given named unbound variable. The type may be <code>Object</code>
|
||||
|
@ -481,4 +487,9 @@ public interface ExpressionFactory {
|
|||
* a {@link Number}, {@link String}, or {@link Boolean} instance.
|
||||
*/
|
||||
public Literal newTypeLiteral(Object val, int parseType);
|
||||
|
||||
/**
|
||||
* Return a value representing the given datastore function with the given arguments.
|
||||
*/
|
||||
public Value newFunction(String functionName, Class<?> resultType, Value... args);
|
||||
}
|
||||
|
|
|
@ -526,6 +526,10 @@ public class InMemoryExpressionFactory
|
|||
return new Args(val1, val2);
|
||||
}
|
||||
|
||||
public Arguments newArgumentList(Value... values) {
|
||||
return new Args(values);
|
||||
}
|
||||
|
||||
public Value newUnboundVariable(String name, Class type) {
|
||||
UnboundVariable var = new UnboundVariable(type);
|
||||
if (_unbounds == null)
|
||||
|
@ -788,4 +792,8 @@ public class InMemoryExpressionFactory
|
|||
public Value nullIfExpression(Value val1, Value val2) {
|
||||
return new NullIf((Val) val1, (Val) val2);
|
||||
}
|
||||
|
||||
public Value newFunction(String functionName, Class<?> resultType, Value... args) {
|
||||
throw new AbstractMethodError();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -329,6 +329,10 @@ public abstract class CriteriaTest extends TestCase {
|
|||
return auditor.getSQLs();
|
||||
}
|
||||
|
||||
void executeAndCompareSQL(CriteriaQuery<?> q, String expectedSQL) {
|
||||
executeAndCompareSQL(em.createQuery(q), expectedSQL);
|
||||
}
|
||||
|
||||
String extractSQL(Exception e) {
|
||||
Throwable t = e.getCause();
|
||||
if (t instanceof ReportingSQLException)
|
||||
|
|
|
@ -802,4 +802,58 @@ public class TestTypesafeCriteria extends CriteriaTest {
|
|||
|
||||
assertEquivalence(q, jpql);
|
||||
}
|
||||
|
||||
/**
|
||||
* 0-arg function works only if there is a other projection items to determine the table to select from.
|
||||
*/
|
||||
public void testFunctionWithNoArgument() {
|
||||
String jpql = "SELECT c.balanceOwed FROM Customer c";
|
||||
String sql = "SELECT CURRENT_USER(), t0.balanceOwed FROM CR_CUST t0";
|
||||
|
||||
CriteriaQuery<Tuple> q = cb.createTupleQuery();
|
||||
Root<Customer> c = q.from(Customer.class);
|
||||
q.multiselect(cb.function("CURRENT_USER", String.class, (Expression<?>[])null), c.get(Customer_.balanceOwed));
|
||||
|
||||
executeAndCompareSQL(q, sql);
|
||||
// assertEquivalence(q, jpql);
|
||||
}
|
||||
|
||||
public void testFunctionWithOneArgument() {
|
||||
String jpql = "SELECT MAX(c.balanceOwed) FROM Customer c";
|
||||
|
||||
CriteriaQuery<Tuple> q = cb.createTupleQuery();
|
||||
Root<Customer> c = q.from(Customer.class);
|
||||
q.multiselect(cb.function("MAX", Integer.class, c.get(Customer_.balanceOwed)));
|
||||
|
||||
assertEquivalence(q, jpql);
|
||||
|
||||
}
|
||||
|
||||
public void testFunctionWithTwoArgument() {
|
||||
String jpql = "SELECT MOD(c.balanceOwed,10) FROM Customer c";
|
||||
|
||||
CriteriaQuery<Tuple> q = cb.createTupleQuery();
|
||||
Root<Customer> c = q.from(Customer.class);
|
||||
q.multiselect(cb.function("MOD", Integer.class, c.get(Customer_.balanceOwed), cb.literal(10)));
|
||||
|
||||
assertEquivalence(q, jpql);
|
||||
|
||||
}
|
||||
|
||||
public void testFunctionWithFunctionArgumentInOrderBy() {
|
||||
String jpql = "SELECT MOD(c.balanceOwed,10) FROM Customer c WHERE LENGTH(c.name)>3 ORDER BY LENGTH(c.name)";
|
||||
String sql = "SELECT MOD(t0.balanceOwed, ?), LENGTH(t0.name) FROM CR_CUST t0 WHERE (LENGTH(t0.name) > ?) " +
|
||||
"ORDER BY LENGTH(t0.name) ASC";
|
||||
|
||||
CriteriaQuery<Tuple> q = cb.createTupleQuery();
|
||||
Root<Customer> c = q.from(Customer.class);
|
||||
Expression<Integer> nameLength = cb.function("LENGTH", Integer.class, c.get(Customer_.name));
|
||||
q.multiselect(cb.function("MOD", Integer.class, c.get(Customer_.balanceOwed), cb.literal(10)));
|
||||
q.where(cb.greaterThan(nameLength, 3));
|
||||
q.orderBy(cb.asc(nameLength));
|
||||
|
||||
executeAndCompareSQL(q, sql);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -246,7 +246,7 @@ public class CriteriaBuilder implements QueryBuilder, ExpressionParser {
|
|||
|
||||
public <T> Expression<T> function(String name, Class<T> type,
|
||||
Expression<?>... args) {
|
||||
throw new AbstractMethodError();
|
||||
return new Expressions.DatabaseFunction(name, type, args);
|
||||
}
|
||||
|
||||
public Predicate ge(Expression<? extends Number> x,
|
||||
|
|
|
@ -81,8 +81,7 @@ public class CriteriaQueryImpl<T> implements CriteriaQuery<T>, AliasContext {
|
|||
private static String ALIAS_BASE = "autoAlias";
|
||||
|
||||
// Auto-generated Parameter name
|
||||
private int autoParameterCount = 0;
|
||||
private static String PARAM_BASE = "autoParam";
|
||||
private static String PARAM_BASE = "*autoParam";
|
||||
|
||||
private Map<Selection<?>,Value> _variables = new HashMap<Selection<?>, Value>();
|
||||
private Map<Selection<?>,Value> _values = new HashMap<Selection<?>, Value>();
|
||||
|
@ -194,9 +193,13 @@ public class CriteriaQueryImpl<T> implements CriteriaQuery<T>, AliasContext {
|
|||
if (_paramTypes == null) {
|
||||
_paramTypes = new LinkedHashMap<ParameterExpression<?>, Class<?>>();
|
||||
}
|
||||
if (_paramTypes.containsKey(p)) {
|
||||
return;
|
||||
}
|
||||
p.setIndex(_paramTypes.size());
|
||||
_paramTypes.put(p, p.getJavaType());
|
||||
if (p.getName() == null)
|
||||
p.assignAutoName(PARAM_BASE + (++autoParameterCount));
|
||||
p.assignAutoName(PARAM_BASE + p.getIndex());
|
||||
}
|
||||
|
||||
public Set<ParameterExpression<?>> getParameters() {
|
||||
|
|
|
@ -47,12 +47,12 @@ public class Expressions {
|
|||
* Handles null expression.
|
||||
*/
|
||||
static Value toValue(ExpressionImpl<?> e, ExpressionFactory factory,
|
||||
MetamodelImpl model, CriteriaQueryImpl q) {
|
||||
MetamodelImpl model, CriteriaQueryImpl<?> q) {
|
||||
return (e == null) ? factory.getNull() : e.toValue(factory, model, q);
|
||||
}
|
||||
|
||||
static void setImplicitTypes(Value v1, Value v2, Class<?> expected,
|
||||
CriteriaQueryImpl q) {
|
||||
CriteriaQueryImpl<?> q) {
|
||||
JPQLExpressionBuilder.setImplicitTypes(v1, v2, expected,
|
||||
q.getMetamodel(), q.getParameterTypes(), q.toString());
|
||||
}
|
||||
|
@ -97,6 +97,24 @@ public class Expressions {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Functional Expression applies a function on a list of input Expressions.
|
||||
*
|
||||
*
|
||||
* @param <X> the type of the resultant expression
|
||||
*/
|
||||
public abstract static class FunctionalExpression<X> extends ExpressionImpl<X>{
|
||||
protected final ExpressionImpl<?>[] args;
|
||||
|
||||
public FunctionalExpression(Class<X> t, Expression<?>... args) {
|
||||
super(t);
|
||||
int len = args == null ? 0 : args.length;
|
||||
this.args = new ExpressionImpl<?>[len];
|
||||
for (int i = 0; args != null && i < args.length; i++) {
|
||||
this.args[i] = (ExpressionImpl<?>)args[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binary Logical Expression applies a binary function on a pair of
|
||||
|
@ -216,6 +234,24 @@ public class Expressions {
|
|||
}
|
||||
}
|
||||
|
||||
public static class DatabaseFunction<T> extends FunctionalExpression<T> {
|
||||
private final String functionName;
|
||||
private final Class<T> resultType;
|
||||
public DatabaseFunction(String name, Class<T> resultType, Expression<?>... exps) {
|
||||
super(resultType, exps);
|
||||
functionName = name;
|
||||
this.resultType = resultType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value toValue(ExpressionFactory factory, MetamodelImpl model,
|
||||
CriteriaQueryImpl<?> q) {
|
||||
return factory.newFunction(functionName, getJavaType(),
|
||||
new Expressions.ListArgument(resultType, args).toValue(factory, model, q));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class Type<X>
|
||||
extends UnaryFunctionalExpression<Class<? extends X>> {
|
||||
public Type(PathImpl<?, X> path) {
|
||||
|
@ -1346,4 +1382,30 @@ public class Expressions {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is composed of one or more expressions.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public static class ListArgument<T> extends ExpressionImpl<T> {
|
||||
private final ExpressionImpl<?>[] _args;
|
||||
public ListArgument(Class<T> cls, ExpressionImpl<?>... args) {
|
||||
super(cls);
|
||||
_args = args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.apache.openjpa.kernel.exps.Arguments toValue(
|
||||
ExpressionFactory factory, MetamodelImpl model, CriteriaQueryImpl<?> q) {
|
||||
org.apache.openjpa.kernel.exps.Value[] kvs = new org.apache.openjpa.kernel.exps.Value[_args.length];
|
||||
int i = 0;
|
||||
for (ExpressionImpl<?> arg : _args) {
|
||||
kvs[i++] = arg.toValue(factory, model, q);
|
||||
}
|
||||
org.apache.openjpa.kernel.exps.Arguments e = factory.newArgumentList(kvs);
|
||||
e.setImplicitType(getJavaType());
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ public class ParameterExpressionImpl<T> extends ExpressionImpl<T>
|
|||
implements ParameterExpression<T>, QueryParameter<T> {
|
||||
|
||||
private String _autoName = null;
|
||||
private int _index = 0; // index of the parameter as seen by the kernel
|
||||
private final ParameterImpl<T> _delegate;
|
||||
|
||||
/**
|
||||
|
@ -128,6 +129,14 @@ public class ParameterExpressionImpl<T> extends ExpressionImpl<T>
|
|||
return false;
|
||||
}
|
||||
|
||||
void setIndex(int index) {
|
||||
_index = index;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return _index;
|
||||
}
|
||||
|
||||
public final boolean isValueAssignable(Object v) {
|
||||
return _delegate.isValueAssignable(v);
|
||||
}
|
||||
|
@ -155,6 +164,7 @@ public class ParameterExpressionImpl<T> extends ExpressionImpl<T>
|
|||
? factory.newCollectionValuedParameter(paramKey, clzz)
|
||||
: factory.newParameter(paramKey, clzz);
|
||||
param.setMetaData(meta);
|
||||
param.setIndex(_index);
|
||||
|
||||
return param;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue