OPENJPA-856 JPQ2 JPQL add support for entity type expression

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@736881 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Catalina Wei 2009-01-23 01:19:47 +00:00
parent c5736acdf4
commit d49fdb1ea2
21 changed files with 805 additions and 44 deletions

View File

@ -79,6 +79,11 @@ abstract class AbstractVal
sql.append("1");
}
public void appendType(Select sel, ExpContext ctx, ExpState state,
SQLBuffer sql) {
sql.append("1");
}
public void appendSize(Select sel, ExpContext ctx, ExpState state,
SQLBuffer sql) {
sql.append("1");

View File

@ -91,7 +91,11 @@ class InExpression
public void appendTo(Select sel, ExpContext ctx, ExpState state,
SQLBuffer buf) {
InExpState istate = (InExpState) state;
_const.calculateValue(sel, ctx, istate.constantState, null, null);
if (_val instanceof Type)
_const.calculateValue(sel, ctx, istate.constantState, _val,
istate.valueState);
else
_const.calculateValue(sel, ctx, istate.constantState, null, null);
_val.calculateValue(sel, ctx, istate.valueState, null, null);
List list = null;

View File

@ -247,6 +247,10 @@ public class JDBCExpressionFactory
return new Lit(val, ptype);
}
public Literal newTypeLiteral(Object val, int ptype) {
return new TypeLit(val, ptype);
}
public Value getThis() {
return new PCPath(_type);
}
@ -396,6 +400,10 @@ public class JDBCExpressionFactory
return new Index((Val) val);
}
public Value type(Value val) {
return new Type((Val) val);
}
public Value getObjectId(Value val) {
if (val instanceof Const)
return new ConstGetObjectId((Const) val);

View File

@ -27,6 +27,7 @@ import java.util.ListIterator;
import org.apache.commons.lang.ObjectUtils;
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
import org.apache.openjpa.jdbc.meta.ClassMapping;
import org.apache.openjpa.jdbc.meta.Discriminator;
import org.apache.openjpa.jdbc.meta.FieldMapping;
import org.apache.openjpa.jdbc.meta.ValueMapping;
import org.apache.openjpa.jdbc.schema.Column;
@ -814,6 +815,33 @@ public class PCPath
pstate.field.appendIndex(sql, sel, pstate.joins);;
}
public void appendType(Select sel, ExpContext ctx, ExpState state,
SQLBuffer sql) {
Discriminator disc = null;
ClassMapping sup = _class;
while (sup.getMappedPCSuperclassMapping() != null)
sup = sup.getMappedPCSuperclassMapping();
disc = sup.getDiscriminator();
Column[] cols = null;
if (disc != null)
cols = disc.getColumns();
else
cols = getColumns(state);
if (cols == null) {
sql.append("1");
return;
}
for (int i = 0; i < cols.length; i++) {
if (i > 0)
sql.append(", ");
sql.append(sel.getColumnAlias(cols[i], state.joins));
}
}
public void appendSize(Select sel, ExpContext ctx, ExpState state,
SQLBuffer sql) {
PathExpState pstate = (PathExpState) state;

View File

@ -22,6 +22,7 @@ import java.util.Collection;
import java.util.Map;
import org.apache.openjpa.jdbc.meta.ClassMapping;
import org.apache.openjpa.jdbc.meta.Discriminator;
import org.apache.openjpa.jdbc.sql.SQLBuffer;
import org.apache.openjpa.jdbc.sql.Select;
import org.apache.openjpa.kernel.Filters;
@ -82,6 +83,12 @@ public class Param
return Filters.convert(params[_idx], getType());
}
public Object getValue(ExpContext ctx, ExpState state) {
ParamExpState pstate = (ParamExpState) state;
return (pstate.discValue != null) ? pstate.discValue :
getValue(ctx.params);
}
public Object getSQLValue(Select sel, ExpContext ctx, ExpState state) {
return ((ParamExpState) state).sqlValue;
}
@ -98,6 +105,9 @@ public class Param
public Object sqlValue = null;
public int otherLength = 1;
public ClassMapping mapping = null;
public Discriminator disc = null;
public Object discValue = null;
}
public void calculateValue(Select sel, ExpContext ctx, ExpState state,
@ -108,6 +118,14 @@ public class Param
if (other != null && !_container) {
pstate.sqlValue = other.toDataStoreValue(sel, ctx, otherState, val);
pstate.otherLength = other.length(sel, ctx, otherState);
if (other instanceof Type) {
pstate.mapping = ctx.store.getConfiguration().
getMappingRepositoryInstance().getMapping((Class) val,
ctx.store.getContext().getClassLoader(), true);
pstate.disc = pstate.mapping.getDiscriminator();
pstate.discValue = pstate.disc != null ? pstate.disc.getValue()
: null;
}
} else if (ImplHelper.isManageable(val)) {
ClassMapping mapping = ctx.store.getConfiguration().
getMappingRepositoryInstance().getMapping(val.getClass(),
@ -125,6 +143,10 @@ public class Param
if (pstate.otherLength > 1)
sql.appendValue(((Object[]) pstate.sqlValue)[index],
pstate.getColumn(index));
else if (pstate.cols != null)
sql.appendValue(pstate.sqlValue, pstate.getColumn(index));
else if (pstate.discValue != null)
sql.appendValue(pstate.discValue);
else
sql.appendValue(pstate.sqlValue, pstate.getColumn(index));
}

View File

@ -0,0 +1,84 @@
/*
* 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.ClassMapping;
import org.apache.openjpa.jdbc.meta.Discriminator;
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.util.InternalException;
/**
* Entity Type expression.
*
* @author Catalina Wei
*/
class Type
extends UnaryOp {
Discriminator _disc = null;
public Type(Val val) {
super(val);
setMetaData(val.getMetaData());
_disc = ((ClassMapping) getMetaData()).getDiscriminator();
}
public ExpState initialize(Select sel, ExpContext ctx, int flags) {
// initialize the value with a null test
return initializeValue(sel, ctx, NULL_CMP);
}
public Object load(ExpContext ctx, ExpState state, Result res)
throws SQLException {
Object type = getValue().load(ctx, state, res);
return type.getClass();
}
public void calculateValue(Select sel, ExpContext ctx, ExpState state,
Val other, ExpState otherState) {
super.calculateValue(sel, ctx, state, null, null);
if (_disc != null)
_disc.select(sel, (ClassMapping) getMetaData());
}
public void select(Select sel, ExpContext ctx, ExpState state,
boolean pks) {
getValue().select(sel, ctx, state, pks);
}
public void appendTo(Select sel, ExpContext ctx, ExpState state,
SQLBuffer sql, int index) {
getValue().calculateValue(sel, ctx, state, null, null);
getValue().appendType(sel, ctx, state, sql);
sel.append(sql, state.joins);
}
protected Class getType(Class c) {
return Class.class;
}
protected String getOperator() {
// since we override appendTo(), this method should never be called
throw new InternalException();
}
}

View File

@ -0,0 +1,127 @@
/*
* 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.jdbc.meta.ClassMapping;
import org.apache.openjpa.jdbc.meta.Discriminator;
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.Literal;
import org.apache.openjpa.meta.JavaTypes;
/**
* A type literal value.
*
* @author Catalina Wei
*/
public class TypeLit
extends Const
implements Literal {
private Object _val;
private int _ptype;
/**
* Constructor. Supply literal value.
*/
public TypeLit(Object val, int ptype) {
_val = val;
_ptype = ptype;
}
public Class getType() {
return (_val == null) ? Object.class : _val.getClass();
}
public void setImplicitType(Class type) {
_val = Filters.convert(_val, type);
}
public int getParseType() {
return _ptype;
}
public Object getValue() {
return _val;
}
public void setValue(Object val) {
_val = val;
}
public Object getValue(Object[] params) {
return getValue();
}
public ExpState initialize(Select sel, ExpContext ctx, int flags) {
return new LitExpState();
}
/**
* Expression state.
*/
private static class LitExpState
extends ConstExpState {
public Object sqlValue;
public int otherLength;
public ClassMapping mapping = null;
public Discriminator disc = null;
public Object discValue = null;
}
public void calculateValue(Select sel, ExpContext ctx, ExpState state,
Val other, ExpState otherState) {
super.calculateValue(sel, ctx, state, other, otherState);
LitExpState lstate = (LitExpState) state;
lstate.mapping = (ClassMapping) getMetaData();
lstate.disc = lstate.mapping.getDiscriminator();
lstate.discValue = lstate.disc != null ? lstate.disc.getValue() :
null;
sel.select(((ClassMapping) getMetaData()).getPrimaryKeyColumns(),
lstate.joins);
}
public void appendTo(Select sel, ExpContext ctx, ExpState state,
SQLBuffer sql, int index) {
LitExpState lstate = (LitExpState) state;
if (lstate.otherLength > 1)
sql.appendValue(((Object[]) lstate.sqlValue)[index],
lstate.getColumn(index));
else {
if (lstate.discValue != null)
sql.append(getDiscriminator(lstate));
else
sql.append("1");
}
}
private String getDiscriminator(LitExpState lstate) {
StringBuffer disc = new StringBuffer(lstate.discValue.toString());
switch(lstate.disc.getJavaType()) {
case JavaTypes.INT:
return disc.toString();
case JavaTypes.CHAR:
case JavaTypes.STRING:
default:
return disc.insert(0, "'").append("'").toString();
}
}
}

View File

@ -152,6 +152,12 @@ public interface Val
public void appendIndex(Select sel, ExpContext ctx, ExpState state,
SQLBuffer sql);
/**
* Append the SQL checking the type of this value.
*/
public void appendType(Select sel, ExpContext ctx, ExpState state,
SQLBuffer sql);
/**
* Append the SQL checking the size of this value.
*/

View File

@ -862,6 +862,10 @@ public class FieldMapping
assertStrategy().appendIndex(sql, sel, joins);
}
public void appendType(SQLBuffer sql, Select sel, Joins joins) {
assertStrategy().appendType(sql, sel, joins);
}
public Joins join(Joins joins, boolean forceOuter) {
return assertStrategy().join(joins, forceOuter);
}

View File

@ -185,6 +185,11 @@ public interface FieldStrategy
*/
public void appendIndex(SQLBuffer sql, Select sel, Joins joins);
/**
* Append the entity discriminator value to the given statement.
*/
public void appendType(SQLBuffer sql, Select sel, Joins joins);
/**
* Join this value to the class table. Does nothing by default.
*/

View File

@ -139,6 +139,10 @@ public abstract class AbstractFieldStrategy
sql.append("1");
}
public void appendType(SQLBuffer sql, Select sel, Joins joins) {
sql.append("1");
}
public Joins join(Joins joins, boolean forceOuter) {
return joins;
}

View File

@ -402,6 +402,13 @@ public interface ExpressionFactory {
*/
public Value index(Value target);
/**
* Return the type/class of the given value.
*
* @since 2.0.0
*/
public Value type(Value target);
/**
* Return distinct values of the given value. This is typically used
* within aggregates, for example: max(distinct(path))
@ -445,4 +452,10 @@ public interface ExpressionFactory {
* Return a nullif expression
*/
public Value nullIfExpression(Value val1, Value val2);
/**
* Return a value representing the given constant, which will be
* a {@link Number}, {@link String}, or {@link Boolean} instance.
*/
public Literal newTypeLiteral(Object val, int parseType);
}

View File

@ -480,6 +480,10 @@ public class InMemoryExpressionFactory
return new Lit(val, parseType);
}
public Literal newTypeLiteral(Object val, int parseType) {
return new TypeLit(val, parseType);
}
public Value getThis() {
return new This();
}
@ -635,6 +639,10 @@ public class InMemoryExpressionFactory
return new Index((Val) val);
}
public Value type(Value val) {
return new Type((Val) val);
}
public Value getObjectId(Value val) {
return new GetObjectId((Val) val);
}

View File

@ -32,6 +32,7 @@ public interface Literal
public static final int TYPE_BOOLEAN = 2;
public static final int TYPE_STRING = 3;
public static final int TYPE_SQ_STRING = 4; // single-quoted string
public static final int TYPE_CLASS = 5;
/**
* The value of this literal.

View File

@ -0,0 +1,63 @@
/*
* 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.kernel.exps;
import java.util.Collection;
import java.util.Map;
import org.apache.openjpa.kernel.StoreContext;
import serp.util.Numbers;
/**
* Returns the entity type.
*
* @author Catalina Wei
*/
class Type extends Val {
private final Val _val;
/**
* Constructor. Provide target string and the arguments to the
* indexOf method.
*/
public Type(Val val) {
_val = val;
}
public Class getType() {
return Class.class;
}
public void setImplicitType(Class type) {
}
protected Object eval(Object candidate, Object orig,
StoreContext ctx, Object[] params) {
_val.eval(candidate, orig, ctx, params);
return _val.getType();
}
public void acceptVisit(ExpressionVisitor visitor) {
visitor.enter(this);
_val.acceptVisit(visitor);
visitor.exit(this);
}
}

View File

@ -0,0 +1,72 @@
/*
* 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.kernel.exps;
import org.apache.openjpa.kernel.Filters;
import org.apache.openjpa.kernel.StoreContext;
/**
* Represents a type literal.
*
* @author Catalina Wei
*/
class TypeLit
extends Val
implements Literal {
private Object _val;
private final int _ptype;
/**
* Constructor. Provide constant value.
*/
public TypeLit(Object val, int ptype) {
_val = val;
_ptype = ptype;
}
public Object getValue() {
return _val;
}
public void setValue(Object val) {
_val = val;
}
public int getParseType() {
return _ptype;
}
public Object getValue(Object[] parameters) {
return _val;
}
public Class getType() {
return (_val == null) ? Object.class : _val.getClass();
}
public void setImplicitType(Class type) {
_val = Filters.convert(_val, type);
}
protected Object eval(Object candidate, Object orig,
StoreContext ctx, Object[] params) {
return _val;
}
}

View File

@ -330,14 +330,23 @@ public class JPQLExpressionBuilder
if (aliasNode != null)
proj.setAlias(alias);
exps.projections[i] = proj;
exps.projectionClauses[i] = aliasNode == null ?
assemble(node.id == JJTSCALAREXPRESSION ? firstChild(node)
: node) : alias;
exps.projectionClauses[i] = aliasNode != null ? alias :
projectionClause(node.id == JJTSCALAREXPRESSION ?
firstChild(node) : node);
exps.projectionAliases[i] = alias;
}
return exp;
}
private String projectionClause(JPQLNode node) {
switch (node.id) {
case JJTTYPE:
return projectionClause(firstChild(node));
default:
return assemble(node);
}
}
private void evalQueryOperation(QueryExpressions exps) {
// determine whether we want to select, delete, or update
if (root().id == JJTSELECT || root().id == JJTSUBSELECT)
@ -746,7 +755,10 @@ public class JPQLExpressionBuilder
return eval(onlyChild(node));
case JJTTYPE:
return eval(onlyChild(node));
return getType(onlyChild(node));
case JJTTYPELITERAL:
return getTypeLiteral(node);
case JJTCLASSNAME:
return getPathOrConstant(node);
@ -754,10 +766,10 @@ public class JPQLExpressionBuilder
case JJTCASE:
return eval(onlyChild(node));
case JJTSCASE:
case JJTSIMPLECASE:
return getSimpleCaseExpression(node);
case JJTGCASE:
case JJTGENERALCASE:
return getGeneralCaseExpression(node);
case JJTWHEN:
@ -814,6 +826,10 @@ public class JPQLExpressionBuilder
case JJTPOSITIONALINPUTPARAMETER:
return getParameter(node.text, true);
case JJTCOLLECTIONPARAMETER:
// TODO: support collection valued parameters
return getParameter(onlyChild(node).text, true);
case JJTOR: // x OR y
return factory.or(getExpression(left(node)),
getExpression(right(node)));
@ -892,14 +908,19 @@ public class JPQLExpressionBuilder
factory.lessThanEqual(val1, val3)));
case JJTIN: // x.field [NOT] IN ('a', 'b', 'c')
// TYPE(x...) [NOT] IN (entityTypeLiteral1,...)
Expression inExp = null;
Iterator inIterator = node.iterator();
// the first child is the path
val1 = getValue((JPQLNode) inIterator.next());
JPQLNode first = (JPQLNode) inIterator.next();
val1 = getValue(first);
while (inIterator.hasNext()) {
val2 = getValue((JPQLNode) inIterator.next());
JPQLNode next = (JPQLNode) inIterator.next();
if (first.id == JJTTYPE && next.id == JJTTYPELITERAL)
val2 = getTypeLiteral(next);
else
val2 = getValue(next);
// special case for <value> IN (<subquery>) or
// <value> IN (<single value>)
@ -1360,6 +1381,21 @@ public class JPQLExpressionBuilder
} else if (val instanceof Path) {
return (Path) val;
} else if (val instanceof Value) {
if (val.isVariable()) {
// can be an entity type literal
Class c = resolver.classForName(name, null);
if (c != null) {
Value lit = factory.newTypeLiteral(c, Literal.TYPE_CLASS);
Class<?> candidate = getCandidateType();
ClassMetaData can = getClassMetaData(candidate.getName(), false);
ClassMetaData meta = getClassMetaData(name, false);
if (candidate.isAssignableFrom(c))
lit.setMetaData(meta);
else
lit.setMetaData(can);
return lit;
}
}
return (Value) val;
}
@ -1367,6 +1403,24 @@ public class JPQLExpressionBuilder
new Object[]{ name }, null);
}
private Value getTypeLiteral(JPQLNode node) {
JPQLNode type = onlyChild(node);
final String name = type.text;
final Value val = getVariable(name, false);
if (val instanceof Value && val.isVariable()) {
Class c = resolver.classForName(name, null);
if (c != null) {
Value typeLit = factory.newTypeLiteral(c, Literal.TYPE_CLASS);
typeLit.setMetaData(getClassMetaData(name, false));
return typeLit;
}
}
throw parseException(EX_USER, "not-type-literal",
new Object[]{ name }, null);
}
private Value getPathOrConstant(JPQLNode node) {
// first check to see if the path is an enum or static field, and
// if so, load it
@ -1394,6 +1448,36 @@ public class JPQLExpressionBuilder
}
}
/**
* Process type_discriminator
* type_discriminator ::=
* TYPE(identification_variable |
* single_valued_object_path_expression |
* input_parameter )
*/
private Value getType(JPQLNode node) {
switch (node.id) {
case JJTIDENTIFIER:
return factory.type(getValue(node));
case JJTNAMEDINPUTPARAMETER:
return factory.type(getParameter(node.text, false));
case JJTPOSITIONALINPUTPARAMETER:
return factory.type(getParameter(node.text, true));
default:
// TODO: enforce jpa2.0 spec rules.
// A single_valued_object_field is designated by the name of
// an association field in a one-to-one or many-to-one relationship
// or a field of embeddable class type.
// The type of a single_valued_object_field is the abstract schema
// type of the related entity or embeddable class
Value path = getPath(node, false, true);
return factory.type(path);
}
}
private Path getPath(JPQLNode node) {
return getPath(node, false, true);
}

View File

@ -745,19 +745,26 @@ void between_expression() #BETWEEN : { }
void in_expression() #IN : { }
{
(path() | scalar_function()) [ LOOKAHEAD(1) <NOT> { jjtThis.not = true; }] <IN>
"(" (literal_or_param()
(path() | scalar_function() | type_discriminator()) [ LOOKAHEAD(1) <NOT> { jjtThis.not = true; }] <IN>
("(" (literal_or_param()
(<COMMA> (literal_or_param()))* | subquery())
")"
")"
| collection_valued_input_parameter()
)
}
void entity_type_literal() #TYPELITERAL : { }
{
identification_variable()
}
void literal_or_param() : { }
{
(numeric_literal()
| string_literal()
| boolean_literal()
| input_parameter())
| input_parameter()
| entity_type_literal())
}
@ -941,46 +948,51 @@ void arithmetic_factor() : { }
LOOKAHEAD(2) "(" arithmetic_expression() ")" |
functions_returning_numerics() |
aggregate_select_expression() |
LOOKAHEAD(case_expression()) case_expression() |
subquery()
}
void qualified_path() #QPATH : { }
void qualified_path() #QUALIFIEDPATH : { }
{
general_identification_variable() (LOOKAHEAD(2) <DOT> path_component())+
}
void qualified_identification_variable() #QIDENTIFIER : { }
void qualified_identification_variable() #QUALIFIEDIDENTIFIER : { }
{
( <KEY> "(" identification_variable() ")" #KEY
| <VALUE> "(" identification_variable() ")" #VALUE
| <ENTRY> "(" identification_variable() ")" #ENTRY
( <KEY> "(" identification_variable() ")" #KEY(1)
| <VALUE> "(" identification_variable() ")" #VALUE(1)
| <ENTRY> "(" identification_variable() ")" #ENTRY(1)
)
}
void general_identification_variable() #GIDENTIFIER : { }
void general_identification_variable() #GENERALIDENTIFIER : { }
{
( <KEY> "(" identification_variable() ")" #KEY
| <VALUE> "(" identification_variable() ")" #VALUE
( <KEY> "(" identification_variable() ")" #KEY(1)
| <VALUE> "(" identification_variable() ")" #VALUE(1)
)
}
void entity_type_comp() : { }
{
entity_type_expression()
( <EQ> entity_type_expression() #EQUALS(2)
| <NE> entity_type_expression() #NOTEQUALS(2)
(entity_type_expression() | entity_type_literal())
( <EQ> (entity_type_expression() | entity_type_literal()) #EQUALS(2)
| <NE> (entity_type_expression() | entity_type_literal()) #NOTEQUALS(2)
)
}
void entity_type_expression() #TYPE : { }
void type_discriminator() #TYPE : { }
{
<TYPE> "(" (LOOKAHEAD(identification_variable()) identification_variable()
| LOOKAHEAD(path()) path()
<TYPE> "(" (LOOKAHEAD(path()) path()
| LOOKAHEAD(general_identification_variable()) general_identification_variable()
| LOOKAHEAD(identification_variable()) identification_variable()
| LOOKAHEAD(input_parameter()) input_parameter())
")" |
classname() #TYPE_LITERAL |
")"
}
void entity_type_expression(): { }
{
type_discriminator() |
input_parameter()
}
@ -990,7 +1002,7 @@ void scalar_expression() #SCALAREXPRESSION : { }
LOOKAHEAD(case_expression()) case_expression() |
LOOKAHEAD(string_primary()) string_primary() |
LOOKAHEAD(datetime_primary()) datetime_primary() |
LOOKAHEAD(enum_primary()) enum_primary()
LOOKAHEAD(enum_primary()) enum_primary() |
LOOKAHEAD(entity_type_expression()) entity_type_expression()
}
@ -1004,29 +1016,31 @@ void case_expression() #CASE : { }
<NULLIF> nullif_expression()
}
void general_case_expression() #GCASE : { }
void general_case_expression() #GENERALCASE : { }
{
(when_clause())+
<ELSE> scalar_expression()
<ELSE> (LOOKAHEAD(2) scalar_expression() | entity_type_literal())
<END>
}
void when_clause() #WHEN : { }
{
<WHEN> conditional_expression() <THEN> scalar_expression()
<WHEN> conditional_expression()
<THEN> (LOOKAHEAD(2) scalar_expression() | entity_type_literal())
}
void simple_case_expression() #SCASE : { }
void simple_case_expression() #SIMPLECASE : { }
{
(LOOKAHEAD(2) path() | entity_type_expression())
(LOOKAHEAD(type_discriminator()) type_discriminator() | LOOKAHEAD(path()) path())
(simple_when_clause())+
<ELSE> scalar_expression()
<ELSE> (LOOKAHEAD(2) scalar_expression() | entity_type_literal())
<END>
}
void simple_when_clause() #WHENSCALAR : { }
{
<WHEN> scalar_expression() <THEN> scalar_expression()
<WHEN> (LOOKAHEAD(2) scalar_expression() | entity_type_literal())
<THEN> (LOOKAHEAD(2) scalar_expression() | entity_type_literal())
}
void coalesce_expression() #COALESCE : { }
@ -1409,6 +1423,12 @@ void input_parameter() : { }
}
void collection_valued_input_parameter() #COLLECTIONPARAMETER: { }
{
named_input_parameter() | positional_input_parameter()
}
void named_input_parameter() #NAMEDINPUTPARAMETER :
{ Token t; }
{

View File

@ -72,3 +72,4 @@ query-extensions-error: This JPQL query uses non-standard OpenJPA \
JPQL extensions.
jpql-parse-error: "{1}" while parsing JPQL "{0}". See nested stack trace for \
original parse error.
not-type-literal: The specified node ("{0}") is not a valid entity type literal.

View File

@ -0,0 +1,192 @@
/*
* 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.persistence.jpql.expressions;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.persistence.EntityManager;
import org.apache.openjpa.persistence.common.apps.*;
import org.apache.openjpa.persistence.common.utils.AbstractTestCase;
public class TestEntityTypeExpression extends AbstractTestCase {
private int userid1, userid2, userid3, userid4, userid5, userid6;
public TestEntityTypeExpression(String name) {
super(name, "jpqlclausescactusapp");
}
public void setUp() {
deleteAll(CompUser.class);
EntityManager em = currentEntityManager();
startTx(em);
Address[] add = new Address[]{
new Address("43 Sansome", "SF", "United-Kingdom", "94104"),
new Address("24 Mink", "ANTIOCH", "USA", "94513"),
new Address("23 Ogbete", "CoalCamp", "NIGERIA", "00000"),
new Address("10 Wilshire", "Worcester", "CANADA", "80080"),
new Address("23 Bellflower", "Ogui", null, "02000"),
new Address("22 Montgomery", "SF", null, "50054") };
CompUser user1 = createUser("Seetha", "MAC", add[0], 36, true);
CompUser user2 = createUser("Shannon ", "PC", add[1], 36, false);
CompUser user3 = createUser("Ugo", "PC", add[2], 19, true);
CompUser user4 = createUser("_Jacob", "LINUX", add[3], 10, true);
CompUser user5 = createUser("Famzy", "UNIX", add[4], 29, false);
CompUser user6 = createUser("Shade", "UNIX", add[5], 23, false);
em.persist(user1);
userid1 = user1.getUserid();
em.persist(user2);
userid2 = user2.getUserid();
em.persist(user3);
userid3 = user3.getUserid();
em.persist(user4);
userid4 = user4.getUserid();
em.persist(user5);
userid5 = user5.getUserid();
em.persist(user6);
userid6 = user6.getUserid();
endTx(em);
endEm(em);
}
@SuppressWarnings("unchecked")
public void testTypeExpression() {
EntityManager em = currentEntityManager();
String query = null;
List<CompUser> rs = null;
CompUser user = null;
// TODO: test when support for collection valued parameters is available
// Collection params = new ArrayList();
// params.add(FemaleUser.class);
// params.add(MaleUser.class);
// query = "SELECT e FROM CompUser e where TYPE(e) = :params";
// rs = em.createQuery(query).
// setParameter("params", params).getResultList();
// user = rs.get(0);
// assertEquals("the name is not shannon", "Shannon ", user.getName());
query = "SELECT TYPE(e) FROM MaleUser e where TYPE(e) = MaleUser";
rs = em.createQuery(query).getResultList();
Object type = rs.get(0);
assertEquals(type, MaleUser.class);
query = "SELECT TYPE(e) FROM CompUser e where TYPE(e) = ?1";
rs = em.createQuery(query).
setParameter(1, FemaleUser.class).getResultList();
type = rs.get(0);
assertEquals(type, FemaleUser.class);
query = "SELECT TYPE(e) FROM MaleUser e where TYPE(e) = ?1";
rs = em.createQuery(query).
setParameter(1, MaleUser.class).getResultList();
type = rs.get(0);
assertEquals(type, MaleUser.class);
query = "SELECT e, FemaleUser, a FROM Address a, FemaleUser e " +
" where e.address IS NOT NULL";
List<Object> rs2 = em.createQuery(query).getResultList();
type = ((Object[]) rs2.get(0))[1];
assertEquals(type, FemaleUser.class);
query = "SELECT e FROM CompUser e where TYPE(e) = :typeName";
rs = em.createQuery(query).
setParameter("typeName", FemaleUser.class).getResultList();
user = rs.get(0);
assertEquals("the name is not shannon", "Shannon ", user.getName());
query = "SELECT e FROM CompUser e where TYPE(e) = ?1";
rs = em.createQuery(query).
setParameter(1, FemaleUser.class).getResultList();
user = rs.get(0);
assertEquals("the name is not shannon", "Shannon ", user.getName());
query = "SELECT e FROM CompUser e where TYPE(e) in (?1)";
rs = em.createQuery(query).
setParameter(1, MaleUser.class).getResultList();
user = rs.get(0);
assertEquals(user.getName(), "Seetha");
query = "SELECT e FROM CompUser e where TYPE(e) in (?1, ?2)";
rs = em.createQuery(query).
setParameter(1, FemaleUser.class).setParameter(2, MaleUser.class).
getResultList();
user = rs.get(0);
assertEquals("the name is not shannon", "Shannon ", user.getName());
query = "select sum(e.age) FROM CompUser e GROUP BY e.age" +
" HAVING ABS(e.age) = :param";
Long sum = (Long) em.createQuery(query).
setParameter("param", new Double(36)).getSingleResult();
assertEquals(sum.intValue(), 72);
String[] queries = {
"SELECT e FROM CompUser e where TYPE(e) = MaleUser",
"SELECT e from CompUser e where TYPE(e) in (FemaleUser)",
"SELECT e from CompUser e where TYPE(e) not in (FemaleUser)",
"SELECT e from CompUser e where TYPE(e) in (MaleUser, FemaleUser)",
"SELECT TYPE(e) FROM CompUser e where TYPE(e) = MaleUser",
"SELECT TYPE(e) FROM CompUser e",
"SELECT TYPE(a.user) FROM Address a",
"SELECT MaleUser FROM CompUser e",
"SELECT MaleUser FROM Address a",
"SELECT " +
" CASE TYPE(e) WHEN FemaleUser THEN 'Female' " +
" ELSE 'Male' " +
" END " +
" FROM CompUser e",
};
for (int i = 0; i < queries.length; i++) {
query = queries[i];
List<Object> rs1 = em.createQuery(query).getResultList();
Object obj = rs1.get(0);
obj.toString();
System.err.println(obj+" rs size="+rs1.size());
}
endEm(em);
}
public CompUser createUser(String name, String cName, Address add, int age,
boolean isMale) {
CompUser user = null;
if (isMale) {
user = new MaleUser();
user.setName(name);
user.setComputerName(cName);
user.setAddress(add);
user.setAge(age);
} else {
user = new FemaleUser();
user.setName(name);
user.setComputerName(cName);
user.setAddress(add);
user.setAge(age);
}
return user;
}
}

View File

@ -69,11 +69,11 @@ public class TestJPQLScalarExpressions extends AbstractTestCase {
endEm(em);
}
@SuppressWarnings("unchecked")
public void testCoalesceExpressions() {
EntityManager em = currentEntityManager();
startTx(em);
String query = "SELECT e.name, " +
"COALESCE (e.address.country, 'Unknown')" +
" FROM CompUser e";
@ -86,6 +86,7 @@ public class TestJPQLScalarExpressions extends AbstractTestCase {
endEm(em);
}
@SuppressWarnings("unchecked")
public void testNullIfExpressions() {
EntityManager em = currentEntityManager();
startTx(em);
@ -103,6 +104,7 @@ public class TestJPQLScalarExpressions extends AbstractTestCase {
endEm(em);
}
@SuppressWarnings("unchecked")
public void testSimpleCaseExpressions() {
EntityManager em = currentEntityManager();
@ -129,15 +131,23 @@ public class TestJPQLScalarExpressions extends AbstractTestCase {
Object[] result2 = (Object[]) rs2.get(rs2.size()-1);
assertEquals("the name is not seetha", "Seetha", result2[0]);
// TODO: needs entity-type-expression
String query3 = "SELECT e.name, " +
" CASE TYPE(e) WHEN FemaleUser THEN 'Female' " +
" ELSE 'Male' " +
" END " +
" END as result" +
" FROM CompUser e";
List rs3 = em.createQuery(query3).getResultList();
Object[] result3 = (Object[]) rs3.get(rs3.size()-1);
assertEquals("the result is not female", "Female", result3[1]);
assertEquals("the name is not shade", "Shade", result3[0]);
result3 = (Object[]) rs3.get(0);
assertEquals("the result is not male", "Male", result3[1]);
assertEquals("the name is not seetha", "Seetha", result3[0]);
endEm(em);
}
@SuppressWarnings("unchecked")
public void testGeneralCaseExpressions() {
EntityManager em = currentEntityManager();
startTx(em);
@ -155,7 +165,6 @@ public class TestJPQLScalarExpressions extends AbstractTestCase {
List rs = em.createQuery(query).getResultList();
String update = "UPDATE CompUser e SET e.age = " +
"CASE WHEN e.age > 30 THEN e.age - 1 " +
"WHEN e.age < 15 THEN e.age + 1 " +
@ -169,6 +178,7 @@ public class TestJPQLScalarExpressions extends AbstractTestCase {
endEm(em);
}
@SuppressWarnings("unchecked")
public void testMathFuncOrderByAlias() {
EntityManager em = currentEntityManager();