Author: fancy
Date: Thu Dec 11 17:29:19 2008
New Revision: 725881
URL: http://svn.apache.org/viewvc?rev=725881&view=rev
Log:
OPENJPA-805 JPQL iteration 1 - add support for COALESCE and NULLIF expressions
Added:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/CoalesceExpression.java
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/NullIfExpression.java
Modified:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Val.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/expressions/TestJPQLScalarExpressions.java
Added:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/CoalesceExpression.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/CoalesceExpression.java?rev=725881&view=auto
==============================================================================
---
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/CoalesceExpression.java
(added)
+++
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/CoalesceExpression.java
Thu Dec 11 17:29:19 2008
@@ -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;
+ }
+}
+
Modified:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java?rev=725881&r1=725880&r2=725881&view=diff
==============================================================================
---
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
(original)
+++
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
Thu Dec 11 17:29:19 2008
@@ -447,4 +447,37 @@
}
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);
+ }
}
Added:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/NullIfExpression.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/NullIfExpression.java?rev=725881&view=auto
==============================================================================
---
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/NullIfExpression.java
(added)
+++
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/NullIfExpression.java
Thu Dec 11 17:29:19 2008
@@ -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;
+ }
+}
+
Modified:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Val.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Val.java?rev=725881&r1=725880&r2=725881&view=diff
==============================================================================
---
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Val.java
(original)
+++
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Val.java
Thu Dec 11 17:29:19 2008
@@ -68,6 +68,8 @@
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
Modified:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java?rev=725881&r1=725880&r2=725881&view=diff
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
(original)
+++
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
Thu Dec 11 17:29:19 2008
@@ -428,4 +428,14 @@
* 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);
}
Modified:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java?rev=725881&r1=725880&r2=725881&view=diff
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
(original)
+++
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
Thu Dec 11 17:29:19 2008
@@ -745,4 +745,14 @@
// 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;
+ }
}
Modified:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java?rev=725881&r1=725880&r2=725881&view=diff
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
(original)
+++
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
Thu Dec 11 17:29:19 2008
@@ -766,6 +766,12 @@
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 @@
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);
}
Modified:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/expressions/TestJPQLScalarExpressions.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/expressions/TestJPQLScalarExpressions.java?rev=725881&r1=725880&r2=725881&view=diff
==============================================================================
---
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/expressions/TestJPQLScalarExpressions.java
(original)
+++
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/expressions/TestJPQLScalarExpressions.java
Thu Dec 11 17:29:19 2008
@@ -69,6 +69,40 @@
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();