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:
Catalina Wei 2008-12-12 01:29:19 +00:00
parent cabd344397
commit debe82b8d0
8 changed files with 446 additions and 0 deletions

View File

@ -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;
}
}

View File

@ -447,4 +447,37 @@ public class JDBCExpressionFactory
} }
return new WhenScalar((Val) val1, (Val) val2); 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);
}
} }

View File

@ -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;
}
}

View File

@ -68,6 +68,8 @@ public interface Val
public final int GENERALCASE_VAL = 14; public final int GENERALCASE_VAL = 14;
public final int WHENCONDITION_VAL = 15; public final int WHENCONDITION_VAL = 15;
public final int WHENSCALAR_VAL = 16; 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 * Initialize the value. This method should recursively initialize any

View File

@ -428,4 +428,14 @@ public interface ExpressionFactory {
* Return a when scalar_expression clause * Return a when scalar_expression clause
*/ */
public Expression whenScalar(Value val1, Value val2); 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);
} }

View File

@ -745,4 +745,14 @@ public class InMemoryExpressionFactory
// TODO Auto-generated method stub // TODO Auto-generated method stub
return null; 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;
}
} }

View File

@ -766,6 +766,12 @@ public class JPQLExpressionBuilder
case JJTWHENSCALAR: case JJTWHENSCALAR:
return getWhenScalar(node); return getWhenScalar(node);
case JJTCOALESCE:
return getCoalesceExpression(node);
case JJTNULLIF:
return getNullIfExpression(node);
case JJTWHERE: // top-level WHERE clause case JJTWHERE: // top-level WHERE clause
return getExpression(onlyChild(node)); return getExpression(onlyChild(node));
@ -1502,6 +1508,23 @@ public class JPQLExpressionBuilder
return factory.whenScalar((Value) val1, (Value) val2); 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) { private Value getValue(JPQLNode node) {
return getValue(node, VAR_PATH); return getValue(node, VAR_PATH);
} }

View File

@ -69,6 +69,40 @@ public class TestJPQLScalarExpressions extends AbstractTestCase {
endEm(em); 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() { public void testSimpleCaseExpressions() {
EntityManager em = currentEntityManager(); EntityManager em = currentEntityManager();