mirror of https://github.com/apache/openjpa.git
OPENJPA-805 JPQL iteration 1 - add support for COALESCE and NULLIF expressions
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@725881 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
cabd344397
commit
debe82b8d0
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* 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 java.sql.SQLException;
|
||||
|
||||
import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
|
||||
import org.apache.openjpa.jdbc.sql.Joins;
|
||||
import org.apache.openjpa.jdbc.sql.Raw;
|
||||
import org.apache.openjpa.jdbc.sql.Result;
|
||||
import org.apache.openjpa.jdbc.sql.SQLBuffer;
|
||||
import org.apache.openjpa.jdbc.sql.Select;
|
||||
import org.apache.openjpa.kernel.Filters;
|
||||
import org.apache.openjpa.kernel.exps.ExpressionVisitor;
|
||||
import org.apache.openjpa.meta.ClassMetaData;
|
||||
|
||||
/**
|
||||
* Coalesce expression.
|
||||
*
|
||||
* @author Catalina Wei
|
||||
*/
|
||||
public class CoalesceExpression
|
||||
extends AbstractVal {
|
||||
|
||||
private final Val[] _vals;
|
||||
private ClassMetaData _meta = null;
|
||||
private Class _cast = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public CoalesceExpression(Val[] vals) {
|
||||
_vals = vals;
|
||||
}
|
||||
|
||||
public Val[] getVal() {
|
||||
return _vals;
|
||||
}
|
||||
|
||||
public Class getType() {
|
||||
if (_cast != null)
|
||||
return _cast;
|
||||
Class type = _vals[0].getType();
|
||||
for (int i = 1; i < _vals.length; i++)
|
||||
type = Filters.promote(type, _vals[i].getType());
|
||||
if (type == Raw.class)
|
||||
return String.class;
|
||||
return type;
|
||||
}
|
||||
|
||||
public ExpState initialize(Select sel, ExpContext ctx, int flags) {
|
||||
ExpState[] states = new ExpState[_vals.length];
|
||||
Joins joins = null;
|
||||
for (int i = 0; i < _vals.length; i++) {
|
||||
states[i] = _vals[i].initialize(sel, ctx, flags);
|
||||
if (joins == null)
|
||||
joins = states[i].joins;
|
||||
else
|
||||
joins = sel.and(joins, states[i].joins);
|
||||
}
|
||||
return new CoalesceExpState(joins, states);
|
||||
}
|
||||
|
||||
private static class CoalesceExpState
|
||||
extends ExpState {
|
||||
|
||||
public ExpState[] states;
|
||||
|
||||
public CoalesceExpState(Joins joins, ExpState[] states) {
|
||||
super(joins);
|
||||
this.states = states;
|
||||
}
|
||||
}
|
||||
|
||||
public void appendTo(Select sel, ExpContext ctx, ExpState state,
|
||||
SQLBuffer buf, int index) {
|
||||
CoalesceExpState cstate = (CoalesceExpState) state;
|
||||
|
||||
buf.append(" COALESCE ");
|
||||
buf.append("(");
|
||||
|
||||
for (int i = 0; i < _vals.length; i++) {
|
||||
if (i > 0)
|
||||
buf.append(",");
|
||||
_vals[i].appendTo(sel, ctx, cstate.states[i], buf, 0);
|
||||
}
|
||||
|
||||
buf.append(")");
|
||||
}
|
||||
|
||||
public void selectColumns(Select sel, ExpContext ctx, ExpState state,
|
||||
boolean pks) {
|
||||
CoalesceExpState cstate = (CoalesceExpState) state;
|
||||
|
||||
for (int i = 0; i < _vals.length; i++)
|
||||
_vals[i].selectColumns(sel, ctx, cstate.states[i], pks);
|
||||
}
|
||||
|
||||
public void acceptVisit(ExpressionVisitor visitor) {
|
||||
visitor.enter(this);
|
||||
for (int i = 0; i < _vals.length; i++)
|
||||
_vals[i].acceptVisit(visitor);
|
||||
visitor.exit(this);
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return Val.COALESCE_VAL;
|
||||
}
|
||||
|
||||
public void calculateValue(Select sel, ExpContext ctx, ExpState state,
|
||||
Val other, ExpState otherState) {
|
||||
CoalesceExpState cstate = (CoalesceExpState) state;
|
||||
for (int i = 0; i < _vals.length; i++)
|
||||
_vals[i].calculateValue(sel, ctx, cstate.states[i], null, null);
|
||||
}
|
||||
|
||||
public void groupBy(Select sel, ExpContext ctx, ExpState state) {
|
||||
sel.groupBy(newSQLBuffer(sel, ctx, state));
|
||||
}
|
||||
|
||||
public int length(Select sel, ExpContext ctx, ExpState state) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
private SQLBuffer newSQLBuffer(Select sel, ExpContext ctx, ExpState state) {
|
||||
calculateValue(sel, ctx, state, null, null);
|
||||
SQLBuffer buf = new SQLBuffer(ctx.store.getDBDictionary());
|
||||
appendTo(sel, ctx, state, buf, 0);
|
||||
return buf;
|
||||
}
|
||||
|
||||
public Object load(ExpContext ctx, ExpState state, Result res)
|
||||
throws SQLException {
|
||||
return Filters.convert(res.getObject(this,
|
||||
JavaSQLTypes.JDBC_DEFAULT, null), getType());
|
||||
}
|
||||
|
||||
public void orderBy(Select sel, ExpContext ctx, ExpState state,
|
||||
boolean asc) {
|
||||
sel.orderBy(newSQLBuffer(sel, ctx, state), asc, false, getSelectAs());
|
||||
}
|
||||
|
||||
public void select(Select sel, ExpContext ctx, ExpState state, boolean pks){
|
||||
sel.select(newSQLBuffer(sel, ctx, state), this);
|
||||
}
|
||||
|
||||
public ClassMetaData getMetaData() {
|
||||
return _meta;
|
||||
}
|
||||
|
||||
public void setImplicitType(Class type) {
|
||||
_cast = type;
|
||||
}
|
||||
|
||||
public void setMetaData(ClassMetaData meta) {
|
||||
_meta = meta;
|
||||
}
|
||||
}
|
||||
|
|
@ -447,4 +447,37 @@ public class JDBCExpressionFactory
|
|||
}
|
||||
return new WhenScalar((Val) val1, (Val) val2);
|
||||
}
|
||||
|
||||
public Value coalesceExpression(Value[] vals) {;
|
||||
Object[] values = new Val[vals.length];
|
||||
for (int i = 0; i < vals.length; i++) {
|
||||
if (vals[i] instanceof Lit) {
|
||||
Lit val = (Lit) vals[i];
|
||||
StringBuffer value = new StringBuffer(val.getValue().toString());
|
||||
if (val.getParseType() == Literal.TYPE_SQ_STRING)
|
||||
value.insert(0, "'").append("'");
|
||||
val.setValue(new Raw(value.toString()));
|
||||
}
|
||||
values[i] = vals[i];
|
||||
}
|
||||
return new CoalesceExpression((Val[]) values);
|
||||
}
|
||||
|
||||
public Value nullIfExpression(Value val1, Value val2) {
|
||||
if (val1 instanceof Lit) {
|
||||
Lit val = (Lit) val1;
|
||||
StringBuffer value = new StringBuffer(val.getValue().toString());
|
||||
if (val.getParseType() == Literal.TYPE_SQ_STRING)
|
||||
value.insert(0, "'").append("'");
|
||||
val.setValue(new Raw(value.toString()));
|
||||
}
|
||||
if (val2 instanceof Lit) {
|
||||
Lit val = (Lit) val2;
|
||||
StringBuffer value = new StringBuffer(val.getValue().toString());
|
||||
if (val.getParseType() == Literal.TYPE_SQ_STRING)
|
||||
value.insert(0, "'").append("'");
|
||||
val.setValue(new Raw(value.toString()));
|
||||
}
|
||||
return new NullIfExpression((Val) val1, (Val) val2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* 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 java.sql.SQLException;
|
||||
|
||||
import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
|
||||
import org.apache.openjpa.jdbc.sql.Raw;
|
||||
import org.apache.openjpa.jdbc.sql.Result;
|
||||
import org.apache.openjpa.jdbc.sql.SQLBuffer;
|
||||
import org.apache.openjpa.jdbc.sql.Select;
|
||||
import org.apache.openjpa.kernel.Filters;
|
||||
import org.apache.openjpa.kernel.exps.ExpressionVisitor;
|
||||
import org.apache.openjpa.meta.ClassMetaData;
|
||||
|
||||
/**
|
||||
* NullIf expression.
|
||||
*
|
||||
* @author Catalina Wei
|
||||
*/
|
||||
public class NullIfExpression
|
||||
extends AbstractVal {
|
||||
|
||||
private final Val _val1;
|
||||
private final Val _val2;
|
||||
private ClassMetaData _meta = null;
|
||||
private Class _cast = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public NullIfExpression(Val val1, Val val2) {
|
||||
_val1 = val1;
|
||||
_val2 = val2;
|
||||
}
|
||||
|
||||
public Val getVal1() {
|
||||
return _val1;
|
||||
}
|
||||
|
||||
public Val getVal2() {
|
||||
return _val2;
|
||||
}
|
||||
|
||||
public Class getType() {
|
||||
if (_cast != null)
|
||||
return _cast;
|
||||
Class c1 = _val1.getType();
|
||||
Class c2 = _val2.getType();
|
||||
Class type = Filters.promote(c1, c2);
|
||||
if (type == Raw.class)
|
||||
return String.class;
|
||||
return type;
|
||||
}
|
||||
|
||||
public ExpState initialize(Select sel, ExpContext ctx, int flags) {
|
||||
ExpState s1 = _val1.initialize(sel, ctx, 0);
|
||||
ExpState s2 = _val2.initialize(sel, ctx, 0);
|
||||
return new BinaryOpExpState(sel.and(s1.joins, s2.joins), s1, s2);
|
||||
}
|
||||
|
||||
public void appendTo(Select sel, ExpContext ctx, ExpState state,
|
||||
SQLBuffer buf, int index) {
|
||||
BinaryOpExpState bstate = (BinaryOpExpState) state;
|
||||
|
||||
buf.append(" NULLIF ");
|
||||
buf.append("(");
|
||||
|
||||
_val1.appendTo(sel, ctx, bstate.state1, buf, 0);
|
||||
buf.append(",");
|
||||
_val2.appendTo(sel, ctx, bstate.state2, buf, 0);
|
||||
|
||||
buf.append(")");
|
||||
}
|
||||
|
||||
public void selectColumns(Select sel, ExpContext ctx, ExpState state,
|
||||
boolean pks) {
|
||||
BinaryOpExpState bstate = (BinaryOpExpState) state;
|
||||
_val1.selectColumns(sel, ctx, bstate.state1, true);
|
||||
_val2.selectColumns(sel, ctx, bstate.state2, true);
|
||||
}
|
||||
|
||||
public void acceptVisit(ExpressionVisitor visitor) {
|
||||
visitor.enter(this);
|
||||
_val1.acceptVisit(visitor);
|
||||
_val2.acceptVisit(visitor);
|
||||
visitor.exit(this);
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return Val.NULLIF_VAL;
|
||||
}
|
||||
|
||||
public void calculateValue(Select sel, ExpContext ctx, ExpState state,
|
||||
Val other, ExpState otherState) {
|
||||
BinaryOpExpState bstate = (BinaryOpExpState) state;
|
||||
_val1.calculateValue(sel, ctx, bstate.state1, _val2, bstate.state2);
|
||||
_val2.calculateValue(sel, ctx, bstate.state2, _val1, bstate.state1);
|
||||
}
|
||||
|
||||
public void groupBy(Select sel, ExpContext ctx, ExpState state) {
|
||||
sel.groupBy(newSQLBuffer(sel, ctx, state));
|
||||
}
|
||||
|
||||
public int length(Select sel, ExpContext ctx, ExpState state) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
private SQLBuffer newSQLBuffer(Select sel, ExpContext ctx, ExpState state) {
|
||||
calculateValue(sel, ctx, state, null, null);
|
||||
SQLBuffer buf = new SQLBuffer(ctx.store.getDBDictionary());
|
||||
appendTo(sel, ctx, state, buf, 0);
|
||||
return buf;
|
||||
}
|
||||
|
||||
public Object load(ExpContext ctx, ExpState state, Result res)
|
||||
throws SQLException {
|
||||
return Filters.convert(res.getObject(this,
|
||||
JavaSQLTypes.JDBC_DEFAULT, null), getType());
|
||||
}
|
||||
|
||||
public void orderBy(Select sel, ExpContext ctx, ExpState state,
|
||||
boolean asc) {
|
||||
sel.orderBy(newSQLBuffer(sel, ctx, state), asc, false, getSelectAs());
|
||||
}
|
||||
|
||||
public void select(Select sel, ExpContext ctx, ExpState state, boolean pks){
|
||||
sel.select(newSQLBuffer(sel, ctx, state), this);
|
||||
}
|
||||
|
||||
public ClassMetaData getMetaData() {
|
||||
return _meta;
|
||||
}
|
||||
|
||||
public void setImplicitType(Class type) {
|
||||
_cast = type;
|
||||
}
|
||||
|
||||
public void setMetaData(ClassMetaData meta) {
|
||||
_meta = meta;
|
||||
}
|
||||
}
|
||||
|
|
@ -68,6 +68,8 @@ public interface Val
|
|||
public final int GENERALCASE_VAL = 14;
|
||||
public final int WHENCONDITION_VAL = 15;
|
||||
public final int WHENSCALAR_VAL = 16;
|
||||
public final int COALESCE_VAL = 17;
|
||||
public final int NULLIF_VAL = 18;
|
||||
|
||||
/**
|
||||
* Initialize the value. This method should recursively initialize any
|
||||
|
|
|
@ -428,4 +428,14 @@ public interface ExpressionFactory {
|
|||
* Return a when scalar_expression clause
|
||||
*/
|
||||
public Expression whenScalar(Value val1, Value val2);
|
||||
|
||||
/**
|
||||
* Return a coalesce expression
|
||||
*/
|
||||
public Value coalesceExpression(Value[] val);
|
||||
|
||||
/**
|
||||
* Return a nullif expression
|
||||
*/
|
||||
public Value nullIfExpression(Value val1, Value val2);
|
||||
}
|
||||
|
|
|
@ -745,4 +745,14 @@ public class InMemoryExpressionFactory
|
|||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public Value coalesceExpression(Value[] val) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public Value nullIfExpression(Value val1, Value val2) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -766,6 +766,12 @@ public class JPQLExpressionBuilder
|
|||
case JJTWHENSCALAR:
|
||||
return getWhenScalar(node);
|
||||
|
||||
case JJTCOALESCE:
|
||||
return getCoalesceExpression(node);
|
||||
|
||||
case JJTNULLIF:
|
||||
return getNullIfExpression(node);
|
||||
|
||||
case JJTWHERE: // top-level WHERE clause
|
||||
return getExpression(onlyChild(node));
|
||||
|
||||
|
@ -1502,6 +1508,23 @@ public class JPQLExpressionBuilder
|
|||
return factory.whenScalar((Value) val1, (Value) val2);
|
||||
}
|
||||
|
||||
private Value getCoalesceExpression(JPQLNode node) {
|
||||
int nChild = node.getChildCount();
|
||||
|
||||
Object vals[] = new Value[nChild];
|
||||
for (int i = 0; i < nChild; i++)
|
||||
vals[i] = eval(node.children[i]);
|
||||
|
||||
return factory.coalesceExpression((Value[]) vals);
|
||||
}
|
||||
|
||||
private Value getNullIfExpression(JPQLNode node) {
|
||||
Object val1 = eval(firstChild(node));
|
||||
Object val2 = eval(secondChild(node));
|
||||
|
||||
return factory.nullIfExpression((Value) val1, (Value) val2);
|
||||
}
|
||||
|
||||
private Value getValue(JPQLNode node) {
|
||||
return getValue(node, VAR_PATH);
|
||||
}
|
||||
|
|
|
@ -69,6 +69,40 @@ public class TestJPQLScalarExpressions extends AbstractTestCase {
|
|||
endEm(em);
|
||||
}
|
||||
|
||||
public void testCoalesceExpressions() {
|
||||
EntityManager em = currentEntityManager();
|
||||
startTx(em);
|
||||
|
||||
|
||||
String query = "SELECT e.name, " +
|
||||
"COALESCE (e.address.country, 'Unknown')" +
|
||||
" FROM CompUser e";
|
||||
List rs = em.createQuery(query).getResultList();
|
||||
Object[] result = (Object[]) rs.get(rs.size()-1);
|
||||
assertEquals("the name is not shade", "Shade", result[0]);
|
||||
assertEquals("Unknown", result[1]);
|
||||
|
||||
endTx(em);
|
||||
endEm(em);
|
||||
}
|
||||
|
||||
public void testNullIfExpressions() {
|
||||
EntityManager em = currentEntityManager();
|
||||
startTx(em);
|
||||
|
||||
String query = "SELECT e.name, " +
|
||||
"NULLIF (e.address.country, 'USA')" +
|
||||
" FROM CompUser e";
|
||||
|
||||
List rs = em.createQuery(query).getResultList();
|
||||
Object[] result = (Object[]) rs.get(1);
|
||||
assertEquals("the name is not shannon ", "Shannon ", result[0]);
|
||||
assertNull("is not null", result[1]);
|
||||
|
||||
endTx(em);
|
||||
endEm(em);
|
||||
}
|
||||
|
||||
public void testSimpleCaseExpressions() {
|
||||
EntityManager em = currentEntityManager();
|
||||
|
||||
|
|
Loading…
Reference in New Issue