mirror of https://github.com/apache/openjpa.git
OPENJPA-1185: commit subquery overhaul on behalf of Catalina
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@795934 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
0adf13872e
commit
f74c5d7a69
|
@ -30,7 +30,6 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.openjpa.event.LifecycleEventManager;
|
import org.apache.openjpa.event.LifecycleEventManager;
|
||||||
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
|
|
||||||
import org.apache.openjpa.jdbc.kernel.exps.ExpContext;
|
import org.apache.openjpa.jdbc.kernel.exps.ExpContext;
|
||||||
import org.apache.openjpa.jdbc.kernel.exps.GetColumn;
|
import org.apache.openjpa.jdbc.kernel.exps.GetColumn;
|
||||||
import org.apache.openjpa.jdbc.kernel.exps.JDBCExpressionFactory;
|
import org.apache.openjpa.jdbc.kernel.exps.JDBCExpressionFactory;
|
||||||
|
@ -46,20 +45,20 @@ import org.apache.openjpa.jdbc.meta.ClassMapping;
|
||||||
import org.apache.openjpa.jdbc.meta.FieldMapping;
|
import org.apache.openjpa.jdbc.meta.FieldMapping;
|
||||||
import org.apache.openjpa.jdbc.meta.strats.VerticalClassStrategy;
|
import org.apache.openjpa.jdbc.meta.strats.VerticalClassStrategy;
|
||||||
import org.apache.openjpa.jdbc.schema.Column;
|
import org.apache.openjpa.jdbc.schema.Column;
|
||||||
import org.apache.openjpa.jdbc.schema.ForeignKey;
|
|
||||||
import org.apache.openjpa.jdbc.schema.Table;
|
import org.apache.openjpa.jdbc.schema.Table;
|
||||||
import org.apache.openjpa.jdbc.sql.DBDictionary;
|
import org.apache.openjpa.jdbc.sql.DBDictionary;
|
||||||
import org.apache.openjpa.jdbc.sql.SQLBuffer;
|
import org.apache.openjpa.jdbc.sql.SQLBuffer;
|
||||||
import org.apache.openjpa.jdbc.sql.SQLExceptions;
|
import org.apache.openjpa.jdbc.sql.SQLExceptions;
|
||||||
import org.apache.openjpa.jdbc.sql.Select;
|
import org.apache.openjpa.jdbc.sql.Select;
|
||||||
|
import org.apache.openjpa.jdbc.sql.SelectImpl;
|
||||||
import org.apache.openjpa.jdbc.sql.Union;
|
import org.apache.openjpa.jdbc.sql.Union;
|
||||||
import org.apache.openjpa.kernel.ExpressionStoreQuery;
|
import org.apache.openjpa.kernel.ExpressionStoreQuery;
|
||||||
import org.apache.openjpa.kernel.Filters;
|
import org.apache.openjpa.kernel.Filters;
|
||||||
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||||
import org.apache.openjpa.kernel.OrderingMergedResultObjectProvider;
|
import org.apache.openjpa.kernel.OrderingMergedResultObjectProvider;
|
||||||
import org.apache.openjpa.kernel.QueryContext;
|
|
||||||
import org.apache.openjpa.kernel.QueryHints;
|
import org.apache.openjpa.kernel.QueryHints;
|
||||||
import org.apache.openjpa.kernel.exps.Constant;
|
import org.apache.openjpa.kernel.exps.Constant;
|
||||||
|
import org.apache.openjpa.kernel.exps.Context;
|
||||||
import org.apache.openjpa.kernel.exps.ExpressionFactory;
|
import org.apache.openjpa.kernel.exps.ExpressionFactory;
|
||||||
import org.apache.openjpa.kernel.exps.ExpressionParser;
|
import org.apache.openjpa.kernel.exps.ExpressionParser;
|
||||||
import org.apache.openjpa.kernel.exps.FilterListener;
|
import org.apache.openjpa.kernel.exps.FilterListener;
|
||||||
|
@ -144,10 +143,27 @@ public class JDBCStoreQuery
|
||||||
return new JDBCExpressionFactory((ClassMapping) meta);
|
return new JDBCExpressionFactory((ClassMapping) meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void resetSelect(Context ctx) {
|
||||||
|
List<Context> subselCtxs = ctx.getSubselContexts();
|
||||||
|
if (subselCtxs != null) {
|
||||||
|
for (Context subselCtx : subselCtxs) {
|
||||||
|
SelectImpl sel = (SelectImpl)subselCtx.getSelect();
|
||||||
|
sel.reset();
|
||||||
|
resetSelect(subselCtx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected ResultObjectProvider executeQuery(Executor ex,
|
protected ResultObjectProvider executeQuery(Executor ex,
|
||||||
ClassMetaData base, ClassMetaData[] metas, boolean subclasses,
|
ClassMetaData base, ClassMetaData[] metas, boolean subclasses,
|
||||||
ExpressionFactory[] facts, QueryExpressions[] exps, Object[] params,
|
ExpressionFactory[] facts, QueryExpressions[] exps, Object[] params,
|
||||||
Range range) {
|
Range range) {
|
||||||
|
Context expCtx = exps[0].ctx();
|
||||||
|
if (expCtx != null) {
|
||||||
|
expCtx.resetAliasCount();
|
||||||
|
expCtx.setSelect(null);
|
||||||
|
resetSelect(expCtx);
|
||||||
|
}
|
||||||
if (metas.length > 1 && exps[0].isAggregate())
|
if (metas.length > 1 && exps[0].isAggregate())
|
||||||
throw new UserException(Localizer.forPackage(JDBCStoreQuery.class).
|
throw new UserException(Localizer.forPackage(JDBCStoreQuery.class).
|
||||||
get("mult-mapping-aggregate", Arrays.asList(metas)));
|
get("mult-mapping-aggregate", Arrays.asList(metas)));
|
||||||
|
@ -449,6 +465,13 @@ public class JDBCStoreQuery
|
||||||
private Number executeBulkOperation(ClassMetaData[] metas,
|
private Number executeBulkOperation(ClassMetaData[] metas,
|
||||||
boolean subclasses, ExpressionFactory[] facts, QueryExpressions[] exps,
|
boolean subclasses, ExpressionFactory[] facts, QueryExpressions[] exps,
|
||||||
Object[] params, Map updates) {
|
Object[] params, Map updates) {
|
||||||
|
Context expCtx = exps[0].ctx();
|
||||||
|
if (ctx != null) {
|
||||||
|
expCtx.resetAliasCount();
|
||||||
|
expCtx.setSelect(null);
|
||||||
|
resetSelect(expCtx);
|
||||||
|
}
|
||||||
|
|
||||||
// we cannot execute a bulk delete statement when have mappings in
|
// we cannot execute a bulk delete statement when have mappings in
|
||||||
// multiple tables, so indicate we want to use in-memory with null
|
// multiple tables, so indicate we want to use in-memory with null
|
||||||
ClassMapping[] mappings = (ClassMapping[]) metas;
|
ClassMapping[] mappings = (ClassMapping[]) metas;
|
||||||
|
|
|
@ -114,5 +114,9 @@ abstract class AbstractVal
|
||||||
public Path getPath() {
|
public Path getPath() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.apache.openjpa.jdbc.sql.Select;
|
||||||
import org.apache.openjpa.kernel.Broker;
|
import org.apache.openjpa.kernel.Broker;
|
||||||
import org.apache.openjpa.kernel.Filters;
|
import org.apache.openjpa.kernel.Filters;
|
||||||
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||||
|
import org.apache.openjpa.kernel.exps.Context;
|
||||||
import org.apache.openjpa.kernel.exps.ExpressionVisitor;
|
import org.apache.openjpa.kernel.exps.ExpressionVisitor;
|
||||||
import org.apache.openjpa.meta.ClassMetaData;
|
import org.apache.openjpa.meta.ClassMetaData;
|
||||||
import org.apache.openjpa.meta.FieldMetaData;
|
import org.apache.openjpa.meta.FieldMetaData;
|
||||||
|
@ -217,4 +218,14 @@ class ConstPath
|
||||||
public XMLMetaData getXmlMapping() {
|
public XMLMetaData getXmlMapping() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSchemaAlias(String schemaAlias) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSchemaAlias() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubqueryContext(Context conext) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,7 +167,8 @@ public class JDBCExpressionFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
public Expression not(Expression exp) {
|
public Expression not(Expression exp) {
|
||||||
if (HasContainsExpressionVisitor.hasContains(exp))
|
if (!(exp instanceof IsNotEmptyExpression) &&
|
||||||
|
HasContainsExpressionVisitor.hasContains(exp))
|
||||||
return new NotContainsExpression((Exp) exp);
|
return new NotContainsExpression((Exp) exp);
|
||||||
return new NotExpression((Exp) exp);
|
return new NotExpression((Exp) exp);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ public class Lit
|
||||||
private Object _val;
|
private Object _val;
|
||||||
private int _ptype;
|
private int _ptype;
|
||||||
private boolean _isRaw;
|
private boolean _isRaw;
|
||||||
|
private Object _rawVal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor. Supply literal value.
|
* Constructor. Supply literal value.
|
||||||
|
@ -46,6 +47,8 @@ public class Lit
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class getType() {
|
public Class getType() {
|
||||||
|
if (_isRaw && _rawVal != null)
|
||||||
|
return Raw.class;
|
||||||
return (_val == null) ? Object.class : _val.getClass();
|
return (_val == null) ? Object.class : _val.getClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +80,10 @@ public class Lit
|
||||||
_isRaw = isRaw;
|
_isRaw = isRaw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object getRawValue() {
|
||||||
|
return _rawVal;
|
||||||
|
}
|
||||||
|
|
||||||
public ExpState initialize(Select sel, ExpContext ctx, int flags) {
|
public ExpState initialize(Select sel, ExpContext ctx, int flags) {
|
||||||
return new LitExpState();
|
return new LitExpState();
|
||||||
}
|
}
|
||||||
|
@ -120,7 +127,7 @@ public class Lit
|
||||||
if (!isOrdinal)
|
if (!isOrdinal)
|
||||||
value.append("'");
|
value.append("'");
|
||||||
lstate.sqlValue = new Raw(value.toString());
|
lstate.sqlValue = new Raw(value.toString());
|
||||||
setValue(lstate.sqlValue);
|
_rawVal = lstate.sqlValue;
|
||||||
}
|
}
|
||||||
sql.appendValue(lstate.sqlValue, lstate.getColumn(index));
|
sql.appendValue(lstate.sqlValue, lstate.getColumn(index));
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,10 @@ class NotContainsExpression
|
||||||
|
|
||||||
Select sub = ctx.store.getSQLFactory().newSelect();
|
Select sub = ctx.store.getSQLFactory().newSelect();
|
||||||
sub.setParent(sel, null);
|
sub.setParent(sel, null);
|
||||||
|
// this subselect has the same context as its parent
|
||||||
|
sub.setContext(sel.ctx());
|
||||||
|
// the context select should still belong to parent
|
||||||
|
sub.ctx().setSelect(sel);
|
||||||
ExpState estate = _exp.initialize(sub, ctx, ((NotContainsExpState)
|
ExpState estate = _exp.initialize(sub, ctx, ((NotContainsExpState)
|
||||||
state).contains);
|
state).contains);
|
||||||
sub.where(sub.and(null, estate.joins));
|
sub.where(sub.and(null, estate.joins));
|
||||||
|
|
|
@ -45,6 +45,7 @@ import org.apache.openjpa.kernel.Filters;
|
||||||
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||||
import org.apache.openjpa.kernel.StoreContext;
|
import org.apache.openjpa.kernel.StoreContext;
|
||||||
import org.apache.openjpa.kernel.exps.CandidatePath;
|
import org.apache.openjpa.kernel.exps.CandidatePath;
|
||||||
|
import org.apache.openjpa.kernel.exps.Context;
|
||||||
import org.apache.openjpa.lib.util.Localizer;
|
import org.apache.openjpa.lib.util.Localizer;
|
||||||
import org.apache.openjpa.meta.ClassMetaData;
|
import org.apache.openjpa.meta.ClassMetaData;
|
||||||
import org.apache.openjpa.meta.FieldMetaData;
|
import org.apache.openjpa.meta.FieldMetaData;
|
||||||
|
@ -82,6 +83,7 @@ public class PCPath
|
||||||
private boolean _cid = false;
|
private boolean _cid = false;
|
||||||
private FieldMetaData _xmlfield = null;
|
private FieldMetaData _xmlfield = null;
|
||||||
private boolean _keyPath = false;
|
private boolean _keyPath = false;
|
||||||
|
private String _schemaAlias = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a path starting with the 'this' ptr.
|
* Return a path starting with the 'this' ptr.
|
||||||
|
@ -99,10 +101,12 @@ public class PCPath
|
||||||
|
|
||||||
PCPath other = var.getPCPath();
|
PCPath other = var.getPCPath();
|
||||||
Action action = new Action();
|
Action action = new Action();
|
||||||
|
action.var = var.getName();
|
||||||
if (other == null) {
|
if (other == null) {
|
||||||
_type = UNBOUND_VAR;
|
_type = UNBOUND_VAR;
|
||||||
action.op = Action.UNBOUND_VAR;
|
action.op = Action.UNBOUND_VAR;
|
||||||
action.data = var;
|
action.data = var;
|
||||||
|
_schemaAlias = var.getName();
|
||||||
} else {
|
} else {
|
||||||
// bound variable; copy path
|
// bound variable; copy path
|
||||||
_type = UNACCESSED_VAR;
|
_type = UNACCESSED_VAR;
|
||||||
|
@ -111,6 +115,7 @@ public class PCPath
|
||||||
|
|
||||||
action.op = Action.VAR;
|
action.op = Action.VAR;
|
||||||
action.data = var.getName();
|
action.data = var.getName();
|
||||||
|
_schemaAlias = other._schemaAlias;
|
||||||
}
|
}
|
||||||
_actions.add(action);
|
_actions.add(action);
|
||||||
_cast = var.getType(); // initial type is var type
|
_cast = var.getType(); // initial type is var type
|
||||||
|
@ -131,6 +136,22 @@ public class PCPath
|
||||||
_varName = sub.getCandidateAlias();
|
_varName = sub.getCandidateAlias();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSchemaAlias(String schemaAlias) {
|
||||||
|
if (_schemaAlias == null)
|
||||||
|
_schemaAlias = schemaAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSchemaAlias() {
|
||||||
|
return _schemaAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubqueryContext(Context context) {
|
||||||
|
Action action = lastFieldAction();
|
||||||
|
if (action == null)
|
||||||
|
return;
|
||||||
|
action.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the path as a binding of the given variable.
|
* Set the path as a binding of the given variable.
|
||||||
*/
|
*/
|
||||||
|
@ -420,8 +441,7 @@ public class PCPath
|
||||||
if (act != null && act.op == Action.GET_XPATH)
|
if (act != null && act.op == Action.GET_XPATH)
|
||||||
return ((XMLMetaData) act.data).getType();
|
return ((XMLMetaData) act.data).getType();
|
||||||
|
|
||||||
FieldMetaData fld = act == null ? null :
|
FieldMetaData fld = (act == null) ? null : (FieldMetaData) act.data;
|
||||||
(FieldMetaData) act.data;
|
|
||||||
boolean key = act != null && act.op == Action.GET_KEY;
|
boolean key = act != null && act.op == Action.GET_KEY;
|
||||||
if (fld != null) {
|
if (fld != null) {
|
||||||
switch (fld.getDeclaredTypeCode()) {
|
switch (fld.getDeclaredTypeCode()) {
|
||||||
|
@ -457,6 +477,8 @@ public class PCPath
|
||||||
boolean forceOuter = false;
|
boolean forceOuter = false;
|
||||||
ClassMapping rel = _candidate;
|
ClassMapping rel = _candidate;
|
||||||
|
|
||||||
|
sel.setSchemaAlias(_schemaAlias);
|
||||||
|
|
||||||
// iterate to the final field
|
// iterate to the final field
|
||||||
ClassMapping owner;
|
ClassMapping owner;
|
||||||
ClassMapping from, to;
|
ClassMapping from, to;
|
||||||
|
@ -464,14 +486,27 @@ public class PCPath
|
||||||
Variable var;
|
Variable var;
|
||||||
Iterator itr = (_actions == null) ? null : _actions.iterator();
|
Iterator itr = (_actions == null) ? null : _actions.iterator();
|
||||||
FieldMapping field = null;
|
FieldMapping field = null;
|
||||||
|
Action prevaction = null;
|
||||||
|
boolean isCorrelatedPath = false;
|
||||||
|
boolean fromParentRootInSubselect = navigateFromParentRootInSubselect(sel);
|
||||||
|
|
||||||
while (itr != null && itr.hasNext()) {
|
while (itr != null && itr.hasNext()) {
|
||||||
action = (Action) itr.next();
|
action = (Action) itr.next();
|
||||||
|
|
||||||
// treat subqueries like variables for alias generation purposes
|
// treat subqueries like variables for alias generation purposes
|
||||||
if (action.op == Action.VAR)
|
if (action.op == Action.VAR) {
|
||||||
|
if (sel.getParent() != null && action.var != null &&
|
||||||
|
prevaction != null && prevaction.data != null &&
|
||||||
|
sel.ctx().getVariable(action.var) == null) {
|
||||||
|
//System.out.println("Correlated action var="+action.var);
|
||||||
|
isCorrelatedPath = true;
|
||||||
|
pstate.joins = pstate.joins.setCorrelatedVariable(action.var);
|
||||||
|
} else
|
||||||
pstate.joins = pstate.joins.setVariable((String) action.data);
|
pstate.joins = pstate.joins.setVariable((String) action.data);
|
||||||
else if (action.op == Action.SUBQUERY)
|
}
|
||||||
|
else if (action.op == Action.SUBQUERY) {
|
||||||
pstate.joins = pstate.joins.setSubselect((String) action.data);
|
pstate.joins = pstate.joins.setSubselect((String) action.data);
|
||||||
|
}
|
||||||
else if (action.op == Action.UNBOUND_VAR) {
|
else if (action.op == Action.UNBOUND_VAR) {
|
||||||
// unbound vars are cross-joined to the candidate table
|
// unbound vars are cross-joined to the candidate table
|
||||||
var = (Variable) action.data;
|
var = (Variable) action.data;
|
||||||
|
@ -479,7 +514,15 @@ public class PCPath
|
||||||
if (rel == null)
|
if (rel == null)
|
||||||
throw new IllegalArgumentException(_loc.get(
|
throw new IllegalArgumentException(_loc.get(
|
||||||
"invalid-unbound-var", var.getName()).toString());
|
"invalid-unbound-var", var.getName()).toString());
|
||||||
|
|
||||||
|
if (sel.getParent() != null && action.var != null &&
|
||||||
|
sel.ctx().getVariable(action.var) == null) {
|
||||||
|
//System.out.println("Correlated action var="+action.var);
|
||||||
|
isCorrelatedPath = true;
|
||||||
|
pstate.joins = pstate.joins.setCorrelatedVariable(var.getName());
|
||||||
|
} else
|
||||||
pstate.joins = pstate.joins.setVariable(var.getName());
|
pstate.joins = pstate.joins.setVariable(var.getName());
|
||||||
|
|
||||||
pstate.joins = pstate.joins.crossJoin(_candidate.getTable(),
|
pstate.joins = pstate.joins.crossJoin(_candidate.getTable(),
|
||||||
rel.getTable());
|
rel.getTable());
|
||||||
} else {
|
} else {
|
||||||
|
@ -496,6 +539,13 @@ public class PCPath
|
||||||
pstate.cmpfield = field;
|
pstate.cmpfield = field;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fromParentRootInSubselect) {
|
||||||
|
isCorrelatedPath = true;
|
||||||
|
pstate.joins = pstate.joins.setCorrelatedVariable(_schemaAlias);
|
||||||
|
pstate.joins.setJoinContext(null);
|
||||||
|
}
|
||||||
|
|
||||||
rel = traverseField(pstate, key, forceOuter, false);
|
rel = traverseField(pstate, key, forceOuter, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -543,6 +593,9 @@ public class PCPath
|
||||||
if (action.op == Action.GET_XPATH)
|
if (action.op == Action.GET_XPATH)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
prevaction = action;
|
||||||
|
if (prevaction != null && prevaction.context != null)
|
||||||
|
pstate.joins = pstate.joins.setJoinContext(prevaction.context);
|
||||||
}
|
}
|
||||||
if (_varName != null)
|
if (_varName != null)
|
||||||
pstate.joins = pstate.joins.setVariable(_varName);
|
pstate.joins = pstate.joins.setVariable(_varName);
|
||||||
|
@ -557,9 +610,55 @@ public class PCPath
|
||||||
if ((flags & JOIN_REL) != 0)
|
if ((flags & JOIN_REL) != 0)
|
||||||
joinRelation(pstate, key, forceOuter || (flags & FORCE_OUTER) != 0,
|
joinRelation(pstate, key, forceOuter || (flags & FORCE_OUTER) != 0,
|
||||||
false);
|
false);
|
||||||
|
if (isCorrelatedPath) {
|
||||||
|
// check if there are joins that belong to parent
|
||||||
|
pstate.joins.moveJoinsToParent();
|
||||||
|
}
|
||||||
|
pstate.joins.setJoinContext(null);
|
||||||
|
|
||||||
|
if (_actions == null) {
|
||||||
|
String subqAlias = findSubqAlias(sel);
|
||||||
|
pstate.joins = pstate.joins.setSubselect(subqAlias);
|
||||||
|
pstate.joins.setCorrelatedVariable(_schemaAlias);
|
||||||
|
}
|
||||||
|
|
||||||
return pstate;
|
return pstate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String findSubqAlias(Select sel) {
|
||||||
|
Select pSel = sel.getParent();
|
||||||
|
if (pSel == null)
|
||||||
|
return null;
|
||||||
|
Context pCtx = pSel.ctx();
|
||||||
|
if (pCtx.subquery == null)
|
||||||
|
return null;
|
||||||
|
if (pCtx.getSchema(_schemaAlias) != null)
|
||||||
|
return ((SubQ)pCtx.subquery).getCandidateAlias();
|
||||||
|
return findSubqAlias(pSel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a PCPath is in subselect, and it is simply a navigation
|
||||||
|
* from the parent root, the joins involved in this PCPath
|
||||||
|
* must happen in the main select.
|
||||||
|
*/
|
||||||
|
private boolean navigateFromParentRootInSubselect(Select sel) {
|
||||||
|
if (sel.getParent() == null)
|
||||||
|
return false;
|
||||||
|
Iterator itr = (_actions == null) ? null : _actions.iterator();
|
||||||
|
boolean navigateFromRoot = false;
|
||||||
|
boolean hasVar = false;
|
||||||
|
boolean startsWithSubquery = false;
|
||||||
|
while (itr != null && itr.hasNext()) {
|
||||||
|
Action action = (Action) itr.next();
|
||||||
|
if (action.op == Action.VAR)
|
||||||
|
hasVar = true;
|
||||||
|
else if (action.op == Action.SUBQUERY)
|
||||||
|
startsWithSubquery = true;
|
||||||
|
}
|
||||||
|
return !hasVar && !startsWithSubquery && sel.ctx().getSchema(_schemaAlias) == null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether the given source field joins to the given target field.
|
* Return whether the given source field joins to the given target field.
|
||||||
*/
|
*/
|
||||||
|
@ -846,6 +945,8 @@ public class PCPath
|
||||||
public void appendTo(Select sel, ExpContext ctx, ExpState state,
|
public void appendTo(Select sel, ExpContext ctx, ExpState state,
|
||||||
SQLBuffer sql, int index) {
|
SQLBuffer sql, int index) {
|
||||||
Column col = getColumns(state)[index];
|
Column col = getColumns(state)[index];
|
||||||
|
if (sel != null)
|
||||||
|
sel.setSchemaAlias(_schemaAlias);
|
||||||
|
|
||||||
// if select is null, it means we are not aliasing columns
|
// if select is null, it means we are not aliasing columns
|
||||||
// (e.g., during a bulk update)
|
// (e.g., during a bulk update)
|
||||||
|
@ -977,6 +1078,8 @@ public class PCPath
|
||||||
|
|
||||||
public int op = -1;
|
public int op = -1;
|
||||||
public Object data = null;
|
public Object data = null;
|
||||||
|
public String var = null;
|
||||||
|
public Context context = null;
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return op + "|" + data;
|
return op + "|" + data;
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.apache.openjpa.jdbc.kernel.exps;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.openjpa.jdbc.meta.ClassMapping;
|
import org.apache.openjpa.jdbc.meta.ClassMapping;
|
||||||
|
@ -29,8 +30,10 @@ import org.apache.openjpa.jdbc.sql.SQLBuffer;
|
||||||
import org.apache.openjpa.jdbc.sql.Select;
|
import org.apache.openjpa.jdbc.sql.Select;
|
||||||
import org.apache.openjpa.kernel.exps.AbstractExpressionVisitor;
|
import org.apache.openjpa.kernel.exps.AbstractExpressionVisitor;
|
||||||
import org.apache.openjpa.kernel.exps.Constant;
|
import org.apache.openjpa.kernel.exps.Constant;
|
||||||
|
import org.apache.openjpa.kernel.exps.Context;
|
||||||
import org.apache.openjpa.kernel.exps.Expression;
|
import org.apache.openjpa.kernel.exps.Expression;
|
||||||
import org.apache.openjpa.kernel.exps.QueryExpressions;
|
import org.apache.openjpa.kernel.exps.QueryExpressions;
|
||||||
|
import org.apache.openjpa.kernel.exps.Subquery;
|
||||||
import org.apache.openjpa.kernel.exps.Value;
|
import org.apache.openjpa.kernel.exps.Value;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,6 +46,7 @@ public class SelectConstructor
|
||||||
implements Serializable {
|
implements Serializable {
|
||||||
|
|
||||||
private boolean _extent = false;
|
private boolean _extent = false;
|
||||||
|
private Select _subselect = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if we know the select to have on criteria; to be an extent.
|
* Return true if we know the select to have on criteria; to be an extent.
|
||||||
|
@ -53,6 +57,10 @@ public class SelectConstructor
|
||||||
return _extent;
|
return _extent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSubselect(Select subselect) {
|
||||||
|
_subselect = subselect;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluate the expression, returning a new select and filling in any
|
* Evaluate the expression, returning a new select and filling in any
|
||||||
* associated expression state. Use {@link #select} to then select the data.
|
* associated expression state. Use {@link #select} to then select the data.
|
||||||
|
@ -114,10 +122,25 @@ public class SelectConstructor
|
||||||
*/
|
*/
|
||||||
private Select newSelect(ExpContext ctx, Select parent,
|
private Select newSelect(ExpContext ctx, Select parent,
|
||||||
String alias, QueryExpressions exps, QueryExpressionsState state) {
|
String alias, QueryExpressions exps, QueryExpressionsState state) {
|
||||||
Select sel = ctx.store.getSQLFactory().newSelect();
|
Select sel = parent != null ? _subselect
|
||||||
|
: ctx.store.getSQLFactory().newSelect();
|
||||||
sel.setAutoDistinct((exps.distinct & exps.DISTINCT_AUTO) != 0);
|
sel.setAutoDistinct((exps.distinct & exps.DISTINCT_AUTO) != 0);
|
||||||
sel.setJoinSyntax(ctx.fetch.getJoinSyntax());
|
sel.setJoinSyntax(ctx.fetch.getJoinSyntax());
|
||||||
sel.setParent(parent, alias);
|
sel.setParent(parent, alias);
|
||||||
|
|
||||||
|
if (sel.ctx() == null)
|
||||||
|
sel.setContext(exps.ctx());
|
||||||
|
|
||||||
|
if (parent == null && exps.ctx().getSubselContexts() != null) {
|
||||||
|
// this is the case subselect was created before parent got created
|
||||||
|
List<Context> subselCtxs = exps.ctx().getSubselContexts();
|
||||||
|
for (Context subselCtx : subselCtxs) {
|
||||||
|
Select subsel = (Select) subselCtx.getSelect();
|
||||||
|
Subquery subquery = subselCtx.getSubquery();
|
||||||
|
subsel.setParent(sel, subquery.getCandidateAlias());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
initialize(sel, ctx, exps, state);
|
initialize(sel, ctx, exps, state);
|
||||||
|
|
||||||
if (!sel.getAutoDistinct()) {
|
if (!sel.getAutoDistinct()) {
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.apache.openjpa.jdbc.kernel.exps;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
|
||||||
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
|
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
|
||||||
import org.apache.openjpa.jdbc.meta.ClassMapping;
|
import org.apache.openjpa.jdbc.meta.ClassMapping;
|
||||||
import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
|
import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
|
||||||
|
@ -38,18 +39,19 @@ import org.apache.openjpa.meta.ClassMetaData;
|
||||||
*
|
*
|
||||||
* @author Abe White
|
* @author Abe White
|
||||||
*/
|
*/
|
||||||
class SubQ
|
public class SubQ
|
||||||
extends AbstractVal
|
extends AbstractVal
|
||||||
implements Subquery {
|
implements Subquery {
|
||||||
|
|
||||||
private final ClassMapping _candidate;
|
private final ClassMapping _candidate;
|
||||||
private final boolean _subs;
|
private final boolean _subs;
|
||||||
private final String _alias;
|
private final String _subqAlias;
|
||||||
private final SelectConstructor _cons = new SelectConstructor();
|
private final SelectConstructor _cons = new SelectConstructor();
|
||||||
|
|
||||||
private Class _type = null;
|
private Class _type = null;
|
||||||
private ClassMetaData _meta = null;
|
private ClassMetaData _meta = null;
|
||||||
private QueryExpressions _exps = null;
|
private QueryExpressions _exps = null;
|
||||||
|
private Select _select = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor. Supply candidate, whether subclasses are included in
|
* Constructor. Supply candidate, whether subclasses are included in
|
||||||
|
@ -58,7 +60,14 @@ class SubQ
|
||||||
public SubQ(ClassMapping candidate, boolean subs, String alias) {
|
public SubQ(ClassMapping candidate, boolean subs, String alias) {
|
||||||
_candidate = candidate;
|
_candidate = candidate;
|
||||||
_subs = subs;
|
_subs = subs;
|
||||||
_alias = alias;
|
_subqAlias = alias;
|
||||||
|
_select = (((JDBCConfiguration) candidate.getMappingRepository().
|
||||||
|
getConfiguration()).getSQLFactoryInstance().newSelect());
|
||||||
|
_cons.setSubselect(_select);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getSelect() {
|
||||||
|
return _select;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,6 +77,14 @@ class SubQ
|
||||||
return _candidate;
|
return _candidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getSubs() {
|
||||||
|
return _subs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubqAlias() {
|
||||||
|
return _subqAlias;
|
||||||
|
}
|
||||||
|
|
||||||
public Class getType() {
|
public Class getType() {
|
||||||
if (_exps != null && _type == null) {
|
if (_exps != null && _type == null) {
|
||||||
if (_exps.projections.length == 0)
|
if (_exps.projections.length == 0)
|
||||||
|
@ -93,16 +110,19 @@ class SubQ
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCandidateAlias() {
|
public String getCandidateAlias() {
|
||||||
return _alias;
|
return _subqAlias;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setQueryExpressions(QueryExpressions query) {
|
public void setQueryExpressions(QueryExpressions query) {
|
||||||
_exps = query;
|
_exps = query;
|
||||||
|
_select.setContext(query.ctx());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExpState initialize(Select sel, ExpContext ctx, int flags) {
|
public ExpState initialize(Select sel, ExpContext ctx, int flags) {
|
||||||
if (_exps.projections.length == 1)
|
_select.setParent(sel, null);
|
||||||
return ((Val) _exps.projections[0]).initialize(sel, ctx, flags);
|
if (_exps.projections.length == 1) {
|
||||||
|
return ((Val) _exps.projections[0]).initialize(_select, ctx, flags);
|
||||||
|
}
|
||||||
return ExpState.NULL;
|
return ExpState.NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +200,7 @@ class SubQ
|
||||||
private void appendTo(Select sel, ExpContext ctx, ExpState state,
|
private void appendTo(Select sel, ExpContext ctx, ExpState state,
|
||||||
SQLBuffer sql, int index, boolean size) {
|
SQLBuffer sql, int index, boolean size) {
|
||||||
QueryExpressionsState substate = new QueryExpressionsState();
|
QueryExpressionsState substate = new QueryExpressionsState();
|
||||||
Select sub = _cons.evaluate(ctx, sel, _alias, _exps, substate);
|
Select sub = _cons.evaluate(ctx, sel, _subqAlias, _exps, substate);
|
||||||
_cons.select(sub, ctx, _candidate, _subs, _exps, substate,
|
_cons.select(sub, ctx, _candidate, _subs, _exps, substate,
|
||||||
JDBCFetchConfiguration.EAGER_NONE);
|
JDBCFetchConfiguration.EAGER_NONE);
|
||||||
|
|
||||||
|
|
|
@ -583,6 +583,7 @@ public class FieldMapping
|
||||||
_unq = _info.getJoinUnique(this, false, adapt);
|
_unq = _info.getJoinUnique(this, false, adapt);
|
||||||
_joinTableUniques = _info.getJoinTableUniques(this, false, adapt);
|
_joinTableUniques = _info.getJoinTableUniques(this, false, adapt);
|
||||||
_idx = _info.getJoinIndex(this, adapt);
|
_idx = _info.getJoinIndex(this, adapt);
|
||||||
|
table.setAssociation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ public class Table
|
||||||
private String _comment = null;
|
private String _comment = null;
|
||||||
private int _lineNum = 0;
|
private int _lineNum = 0;
|
||||||
private int _colNum = 0;
|
private int _colNum = 0;
|
||||||
|
private boolean _isAssociation = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor.
|
* Default constructor.
|
||||||
|
@ -84,6 +85,14 @@ public class Table
|
||||||
_schema = schema;
|
_schema = schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAssociation() {
|
||||||
|
_isAssociation = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAssociation() {
|
||||||
|
return _isAssociation;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the table is removed from its schema. Removes all table
|
* Called when the table is removed from its schema. Removes all table
|
||||||
* members, and invalidates the table.
|
* members, and invalidates the table.
|
||||||
|
|
|
@ -47,6 +47,7 @@ import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
|
||||||
import org.apache.openjpa.jdbc.schema.Column;
|
import org.apache.openjpa.jdbc.schema.Column;
|
||||||
import org.apache.openjpa.jdbc.schema.ForeignKey;
|
import org.apache.openjpa.jdbc.schema.ForeignKey;
|
||||||
import org.apache.openjpa.jdbc.schema.Table;
|
import org.apache.openjpa.jdbc.schema.Table;
|
||||||
|
import org.apache.openjpa.kernel.exps.Context;
|
||||||
import org.apache.openjpa.lib.util.Closeable;
|
import org.apache.openjpa.lib.util.Closeable;
|
||||||
import org.apache.openjpa.meta.JavaTypes;
|
import org.apache.openjpa.meta.JavaTypes;
|
||||||
import org.apache.openjpa.util.UnsupportedException;
|
import org.apache.openjpa.util.UnsupportedException;
|
||||||
|
@ -885,7 +886,22 @@ public abstract class AbstractResult
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Joins setJoinContext(Context context) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public void appendTo(SQLBuffer buf) {
|
public void appendTo(SQLBuffer buf) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Joins setCorrelatedVariable(String var) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCorrelatedVariable() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void moveJoinsToParent() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2197,14 +2197,59 @@ public class DBDictionary
|
||||||
Iterator itr = sel.getJoinIterator();
|
Iterator itr = sel.getJoinIterator();
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
while (itr.hasNext()) {
|
while (itr.hasNext()) {
|
||||||
fromSQL.append(toSQL92Join((Join) itr.next(), forUpdate,
|
Join join = (Join) itr.next();
|
||||||
|
if (correlatedJoinCondition(join, sel))
|
||||||
|
continue;
|
||||||
|
fromSQL.append(toSQL92Join(sel, join, forUpdate,
|
||||||
first));
|
first));
|
||||||
first = false;
|
first = false;
|
||||||
|
if (itr.hasNext() && join.isCorrelated()) {
|
||||||
|
fromSQL.append(", ");
|
||||||
|
first = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Iterator itr2 = aliases.iterator(); itr2.hasNext();) {
|
||||||
|
String tableAlias = itr2.next().toString();
|
||||||
|
if (fromSQL.getSQL().indexOf(tableAlias) == -1) {
|
||||||
|
if (!first)
|
||||||
|
fromSQL.append(", ");
|
||||||
|
fromSQL.append(tableAlias);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fromSQL;
|
return fromSQL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean correlatedJoinCondition(Join join, Select sel) {
|
||||||
|
if (!join.isCorrelated())
|
||||||
|
return false;
|
||||||
|
Iterator itr = sel.getJoinIterator();
|
||||||
|
boolean skip = false;
|
||||||
|
//if table1 in join is in the main query, table2 is in
|
||||||
|
//subquery, and table2 participates in other joins
|
||||||
|
//in subquery, the join condition can only be placed in
|
||||||
|
//the where clause in the subquery
|
||||||
|
while (itr.hasNext()) {
|
||||||
|
Join join1 = (Join) itr.next();
|
||||||
|
if (join == join1)
|
||||||
|
continue;
|
||||||
|
if (join.getIndex2() == join1.getIndex1() ||
|
||||||
|
join.getIndex2() == join1.getIndex2()) {
|
||||||
|
skip = true;
|
||||||
|
if (join.getForeignKey() != null){
|
||||||
|
SQLBuffer where = new SQLBuffer(this);
|
||||||
|
where.append("(").append(toTraditionalJoin(join)).append(")");
|
||||||
|
sel.where(where.getSQL());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return skip;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the FROM clause for a select that selects from a tmp table
|
* Return the FROM clause for a select that selects from a tmp table
|
||||||
* created by an inner select.
|
* created by an inner select.
|
||||||
|
@ -2308,9 +2353,11 @@ public class DBDictionary
|
||||||
* Use the given join instance to create SQL joining its tables in
|
* Use the given join instance to create SQL joining its tables in
|
||||||
* the SQL92 style.
|
* the SQL92 style.
|
||||||
*/
|
*/
|
||||||
public SQLBuffer toSQL92Join(Join join, boolean forUpdate, boolean first) {
|
public SQLBuffer toSQL92Join(Select sel, Join join, boolean forUpdate,
|
||||||
|
boolean first) {
|
||||||
SQLBuffer buf = new SQLBuffer(this);
|
SQLBuffer buf = new SQLBuffer(this);
|
||||||
if (first) {
|
boolean corelated = join.isCorrelated();
|
||||||
|
if (first && !corelated) {
|
||||||
buf.append(join.getTable1()).append(" ").
|
buf.append(join.getTable1()).append(" ").
|
||||||
append(join.getAlias1());
|
append(join.getAlias1());
|
||||||
if (forUpdate && tableForUpdateClause != null)
|
if (forUpdate && tableForUpdateClause != null)
|
||||||
|
@ -2318,6 +2365,7 @@ public class DBDictionary
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.append(" ");
|
buf.append(" ");
|
||||||
|
if (!corelated) {
|
||||||
if (join.getType() == Join.TYPE_OUTER)
|
if (join.getType() == Join.TYPE_OUTER)
|
||||||
buf.append(outerJoinClause);
|
buf.append(outerJoinClause);
|
||||||
else if (join.getType() == Join.TYPE_INNER)
|
else if (join.getType() == Join.TYPE_INNER)
|
||||||
|
@ -2325,16 +2373,23 @@ public class DBDictionary
|
||||||
else // cross
|
else // cross
|
||||||
buf.append(crossJoinClause);
|
buf.append(crossJoinClause);
|
||||||
buf.append(" ");
|
buf.append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
buf.append(join.getTable2()).append(" ").append(join.getAlias2());
|
buf.append(join.getTable2()).append(" ").append(join.getAlias2());
|
||||||
if (forUpdate && tableForUpdateClause != null)
|
if (forUpdate && tableForUpdateClause != null)
|
||||||
buf.append(" ").append(tableForUpdateClause);
|
buf.append(" ").append(tableForUpdateClause);
|
||||||
|
|
||||||
|
if (!corelated) {
|
||||||
if (join.getForeignKey() != null)
|
if (join.getForeignKey() != null)
|
||||||
buf.append(" ON ").append(toTraditionalJoin(join));
|
buf.append(" ON ").append(toTraditionalJoin(join));
|
||||||
else if (requiresConditionForCrossJoin &&
|
else if (requiresConditionForCrossJoin &&
|
||||||
join.getType() == Join.TYPE_CROSS)
|
join.getType() == Join.TYPE_CROSS)
|
||||||
buf.append(" ON (1 = 1)");
|
buf.append(" ON (1 = 1)");
|
||||||
|
} else if (join.getForeignKey() != null){
|
||||||
|
SQLBuffer where = new SQLBuffer(this);
|
||||||
|
where.append("(").append(toTraditionalJoin(join)).append(")");
|
||||||
|
sel.where(where.getSQL());
|
||||||
|
}
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,8 @@ public class Join
|
||||||
private int _subs;
|
private int _subs;
|
||||||
private Joins _joins;
|
private Joins _joins;
|
||||||
private boolean _inverse;
|
private boolean _inverse;
|
||||||
|
private boolean _correlated = false;
|
||||||
|
private boolean _isNotMyJoin = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for inner and outer joins.
|
* Constructor for inner and outer joins.
|
||||||
|
@ -189,5 +191,21 @@ public class Join
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isCorrelated() {
|
||||||
|
return _correlated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCorrelated() {
|
||||||
|
_correlated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNotMyJoin() {
|
||||||
|
return _isNotMyJoin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsNotMyJoin() {
|
||||||
|
_isNotMyJoin = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.apache.openjpa.jdbc.sql;
|
||||||
import org.apache.openjpa.jdbc.meta.ClassMapping;
|
import org.apache.openjpa.jdbc.meta.ClassMapping;
|
||||||
import org.apache.openjpa.jdbc.schema.ForeignKey;
|
import org.apache.openjpa.jdbc.schema.ForeignKey;
|
||||||
import org.apache.openjpa.jdbc.schema.Table;
|
import org.apache.openjpa.jdbc.schema.Table;
|
||||||
|
import org.apache.openjpa.kernel.exps.Context;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks joins made when traversing relations in a select.
|
* Tracks joins made when traversing relations in a select.
|
||||||
|
@ -77,4 +78,28 @@ public interface Joins {
|
||||||
* Set the subquery alias.
|
* Set the subquery alias.
|
||||||
*/
|
*/
|
||||||
public Joins setSubselect(String alias);
|
public Joins setSubselect(String alias);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set subquery context when traversing into the next join is
|
||||||
|
* in transition from parent context to subquery.
|
||||||
|
* @param context
|
||||||
|
*/
|
||||||
|
public Joins setJoinContext(Context context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the correlated variable name being traversed into
|
||||||
|
* with the next join.
|
||||||
|
*/
|
||||||
|
public Joins setCorrelatedVariable(String var);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return correlated variable name
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getCorrelatedVariable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move joins that belong to subquery's parent
|
||||||
|
*/
|
||||||
|
public void moveJoinsToParent();
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
|
||||||
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
|
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
|
||||||
import org.apache.openjpa.jdbc.kernel.JDBCStore;
|
import org.apache.openjpa.jdbc.kernel.JDBCStore;
|
||||||
import org.apache.openjpa.kernel.exps.Value;
|
import org.apache.openjpa.kernel.exps.Value;
|
||||||
|
import org.apache.openjpa.kernel.exps.Context;
|
||||||
import org.apache.openjpa.jdbc.meta.ClassMapping;
|
import org.apache.openjpa.jdbc.meta.ClassMapping;
|
||||||
import org.apache.openjpa.jdbc.meta.FieldMapping;
|
import org.apache.openjpa.jdbc.meta.FieldMapping;
|
||||||
import org.apache.openjpa.jdbc.schema.Column;
|
import org.apache.openjpa.jdbc.schema.Column;
|
||||||
|
@ -880,6 +881,18 @@ public class LogicalUnion
|
||||||
boolean force) {
|
boolean force) {
|
||||||
sel.setExpectedResultCount(expectedResultCount, force);
|
sel.setExpectedResultCount(expectedResultCount, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setContext(Context context) {
|
||||||
|
sel.setContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context ctx() {
|
||||||
|
return sel.ctx();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSchemaAlias(String schemaAlias) {
|
||||||
|
sel.setSchemaAlias(schemaAlias);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.apache.openjpa.jdbc.schema.Column;
|
||||||
import org.apache.openjpa.jdbc.schema.ForeignKey;
|
import org.apache.openjpa.jdbc.schema.ForeignKey;
|
||||||
import org.apache.openjpa.jdbc.schema.Table;
|
import org.apache.openjpa.jdbc.schema.Table;
|
||||||
import org.apache.openjpa.kernel.exps.Value;
|
import org.apache.openjpa.kernel.exps.Value;
|
||||||
|
import org.apache.openjpa.kernel.exps.Context;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstraction of a SQL SELECT statement.
|
* Abstraction of a SQL SELECT statement.
|
||||||
|
@ -701,4 +702,21 @@ public interface Select
|
||||||
* Return the alias for the given column, without creating new table alias
|
* Return the alias for the given column, without creating new table alias
|
||||||
*/
|
*/
|
||||||
public String getColumnAlias(Column col, Object path);
|
public String getColumnAlias(Column col, Object path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set JPQL query context for this select
|
||||||
|
* @param context
|
||||||
|
*/
|
||||||
|
public void setContext(Context context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the JPQL query context of this select
|
||||||
|
*/
|
||||||
|
public Context ctx();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record the initial schemaAlias of a join path
|
||||||
|
* @param schemaAlias
|
||||||
|
*/
|
||||||
|
public void setSchemaAlias(String schemaAlias);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@ import java.sql.Statement;
|
||||||
import java.sql.Types;
|
import java.sql.Types;
|
||||||
import java.util.AbstractList;
|
import java.util.AbstractList;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.BitSet;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -56,6 +55,7 @@ import org.apache.openjpa.jdbc.schema.ForeignKey;
|
||||||
import org.apache.openjpa.jdbc.schema.Table;
|
import org.apache.openjpa.jdbc.schema.Table;
|
||||||
import org.apache.openjpa.kernel.StoreContext;
|
import org.apache.openjpa.kernel.StoreContext;
|
||||||
import org.apache.openjpa.kernel.exps.Value;
|
import org.apache.openjpa.kernel.exps.Value;
|
||||||
|
import org.apache.openjpa.kernel.exps.Context;
|
||||||
import org.apache.openjpa.lib.log.Log;
|
import org.apache.openjpa.lib.log.Log;
|
||||||
import org.apache.openjpa.lib.util.Localizer;
|
import org.apache.openjpa.lib.util.Localizer;
|
||||||
import org.apache.openjpa.util.ApplicationIds;
|
import org.apache.openjpa.util.ApplicationIds;
|
||||||
|
@ -119,9 +119,6 @@ public class SelectImpl
|
||||||
// 'parent.address.street' for the purposes of comparisons
|
// 'parent.address.street' for the purposes of comparisons
|
||||||
private Map _aliases = null;
|
private Map _aliases = null;
|
||||||
|
|
||||||
// to cache table alias using Table as the key
|
|
||||||
private Map _tableAliases = null;
|
|
||||||
|
|
||||||
// map of indexes to table aliases like 'TABLENAME t0'
|
// map of indexes to table aliases like 'TABLENAME t0'
|
||||||
private SortedMap _tables = null;
|
private SortedMap _tables = null;
|
||||||
|
|
||||||
|
@ -166,12 +163,11 @@ public class SelectImpl
|
||||||
private SelectImpl _from = null;
|
private SelectImpl _from = null;
|
||||||
protected SelectImpl _outer = null;
|
protected SelectImpl _outer = null;
|
||||||
|
|
||||||
// bitSet indicating if an alias is removed from parent select
|
// JPQL Query context this select is associated with
|
||||||
// bit 0 : correspond to alias 0
|
private Context _ctx = null;
|
||||||
// bit 1 : correspond to alias 1, etc.
|
|
||||||
// if the bit is set, the corresponding alias has been removed from parent
|
// A path navigation is begin with this schema alias
|
||||||
// and recorded under subselect.
|
private String _schemaAlias = null;
|
||||||
private BitSet _removedAliasFromParent = new BitSet(16);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to return the proper table alias for the given alias index.
|
* Helper method to return the proper table alias for the given alias index.
|
||||||
|
@ -206,6 +202,21 @@ public class SelectImpl
|
||||||
_selects._dict = _dict;
|
_selects._dict = _dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setContext(Context context) {
|
||||||
|
if (_ctx == null) {
|
||||||
|
_ctx = context;
|
||||||
|
_ctx.setSelect(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context ctx() {
|
||||||
|
return _ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSchemaAlias(String schemaAlias) {
|
||||||
|
_schemaAlias = schemaAlias;
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////
|
/////////////////////////////////
|
||||||
// SelectExecutor implementation
|
// SelectExecutor implementation
|
||||||
/////////////////////////////////
|
/////////////////////////////////
|
||||||
|
@ -521,7 +532,7 @@ public class SelectImpl
|
||||||
|
|
||||||
public void setParent(Select parent, String path) {
|
public void setParent(Select parent, String path) {
|
||||||
if (path != null)
|
if (path != null)
|
||||||
_subPath = path + ':';
|
_subPath = path;
|
||||||
else
|
else
|
||||||
_subPath = null;
|
_subPath = null;
|
||||||
|
|
||||||
|
@ -546,62 +557,6 @@ public class SelectImpl
|
||||||
else
|
else
|
||||||
_joinSyntax = _parent._joinSyntax;
|
_joinSyntax = _parent._joinSyntax;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_parent.getAliases() == null || _subPath == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_parent._aliases.size() <= 1)
|
|
||||||
return;
|
|
||||||
// Do not remove aliases for databases that use SYNTAX_DATABASE (oracle)
|
|
||||||
if(_parent._joinSyntax != JoinSyntaxes.SYNTAX_DATABASE) {
|
|
||||||
// resolve aliases for subselect from parent
|
|
||||||
Set<Map.Entry> entries = _parent.getAliases().entrySet();
|
|
||||||
for (Map.Entry entry : entries) {
|
|
||||||
Object key = entry.getKey();
|
|
||||||
Integer alias = (Integer) entry.getValue();
|
|
||||||
if (key.toString().indexOf(_subPath) != -1 ||
|
|
||||||
_parent.findTableAlias(alias) == false) {
|
|
||||||
if (_aliases == null)
|
|
||||||
_aliases = new HashMap();
|
|
||||||
_aliases.put(key, alias);
|
|
||||||
|
|
||||||
Object tableString = _parent.getTables().get(alias);
|
|
||||||
if (_tables == null)
|
|
||||||
_tables = new TreeMap();
|
|
||||||
_tables.put(alias, tableString);
|
|
||||||
|
|
||||||
_removedAliasFromParent.set(alias.intValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_aliases != null) {
|
|
||||||
// aliases moved into subselect should be removed from parent
|
|
||||||
entries = _aliases.entrySet();
|
|
||||||
for (Map.Entry entry : entries) {
|
|
||||||
Object key = entry.getKey();
|
|
||||||
Integer alias = (Integer) entry.getValue();
|
|
||||||
if (key.toString().indexOf(_subPath) != -1 ||
|
|
||||||
_parent.findTableAlias(alias) == false) {
|
|
||||||
_parent.removeAlias(key);
|
|
||||||
|
|
||||||
Object tableString = _parent.getTables().get(alias);
|
|
||||||
_parent.removeTable(alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean findTableAlias(Integer alias) {
|
|
||||||
// if alias is defined and referenced, return true.
|
|
||||||
String value = "t" + alias.toString() + ".";
|
|
||||||
if (_tableAliases != null)
|
|
||||||
if (_tableAliases.containsValue(value))
|
|
||||||
return _tables.containsKey(alias);
|
|
||||||
else
|
|
||||||
return _joins != null;
|
|
||||||
else
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map getAliases() {
|
public Map getAliases() {
|
||||||
|
@ -773,17 +728,6 @@ public class SelectImpl
|
||||||
* Return the alias for the given column.
|
* Return the alias for the given column.
|
||||||
*/
|
*/
|
||||||
private String getColumnAlias(String col, Table table, PathJoins pj) {
|
private String getColumnAlias(String col, Table table, PathJoins pj) {
|
||||||
String tableAlias = null;
|
|
||||||
if (pj == null || pj.path() == null) {
|
|
||||||
if (_tableAliases == null)
|
|
||||||
_tableAliases = new HashMap();
|
|
||||||
tableAlias = (String) _tableAliases.get(table);
|
|
||||||
if (tableAlias == null) {
|
|
||||||
tableAlias = getTableAlias(table, pj).toString();
|
|
||||||
_tableAliases.put(table, tableAlias);
|
|
||||||
}
|
|
||||||
return new StringBuilder(tableAlias).append(col).toString();
|
|
||||||
}
|
|
||||||
return getTableAlias(table, pj).append(col).toString();
|
return getTableAlias(table, pj).append(col).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1663,10 +1607,7 @@ public class SelectImpl
|
||||||
if ((_flags & OUTER) != 0)
|
if ((_flags & OUTER) != 0)
|
||||||
pj = (PathJoins) outer(pj);
|
pj = (PathJoins) outer(pj);
|
||||||
if (record) {
|
if (record) {
|
||||||
if (!pj.isEmpty())
|
|
||||||
removeParentJoins(pj);
|
|
||||||
if (!pj.isEmpty()) {
|
if (!pj.isEmpty()) {
|
||||||
removeJoinsFromSubselects(pj);
|
|
||||||
if (_joins == null)
|
if (_joins == null)
|
||||||
_joins = new SelectJoins(this);
|
_joins = new SelectJoins(this);
|
||||||
if (_joins.joins() == null)
|
if (_joins.joins() == null)
|
||||||
|
@ -1679,43 +1620,6 @@ public class SelectImpl
|
||||||
return pj;
|
return pj;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove any joins already in our parent select from the given non-empty
|
|
||||||
* join set.
|
|
||||||
*/
|
|
||||||
private void removeParentJoins(PathJoins pj) {
|
|
||||||
if (_parent == null)
|
|
||||||
return;
|
|
||||||
if (_parent._joins != null && !_parent._joins.isEmpty()) {
|
|
||||||
boolean removed = false;
|
|
||||||
if (!_removedAliasFromParent.isEmpty()) {
|
|
||||||
for (Iterator itr = pj.joins().iterator(); itr.hasNext();) {
|
|
||||||
Join jn = (Join) itr.next();
|
|
||||||
if (_aliases.containsValue(jn.getIndex1()))
|
|
||||||
removed = _parent._joins.joins().remove(jn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!removed)
|
|
||||||
pj.joins().removeAll(_parent._joins.joins());
|
|
||||||
}
|
|
||||||
if (!pj.isEmpty())
|
|
||||||
_parent.removeParentJoins(pj);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the given non-empty joins from the joins of our subselects.
|
|
||||||
*/
|
|
||||||
private void removeJoinsFromSubselects(PathJoins pj) {
|
|
||||||
if (_subsels == null)
|
|
||||||
return;
|
|
||||||
SelectImpl sub;
|
|
||||||
for (int i = 0; i < _subsels.size(); i++) {
|
|
||||||
sub = (SelectImpl) _subsels.get(i);
|
|
||||||
if (sub._joins != null && !sub._joins.isEmpty())
|
|
||||||
sub._joins.joins().removeAll(pj.joins());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public SelectExecutor whereClone(int sels) {
|
public SelectExecutor whereClone(int sels) {
|
||||||
if (sels < 1)
|
if (sels < 1)
|
||||||
sels = 1;
|
sels = 1;
|
||||||
|
@ -1732,6 +1636,7 @@ public class SelectImpl
|
||||||
sel._flags &= ~EAGER_TO_MANY;
|
sel._flags &= ~EAGER_TO_MANY;
|
||||||
sel._flags &= ~FORCE_COUNT;
|
sel._flags &= ~FORCE_COUNT;
|
||||||
sel._joinSyntax = _joinSyntax;
|
sel._joinSyntax = _joinSyntax;
|
||||||
|
sel._schemaAlias = _schemaAlias;
|
||||||
if (_aliases != null)
|
if (_aliases != null)
|
||||||
sel._aliases = new HashMap(_aliases);
|
sel._aliases = new HashMap(_aliases);
|
||||||
if (_tables != null)
|
if (_tables != null)
|
||||||
|
@ -1888,6 +1793,8 @@ public class SelectImpl
|
||||||
public void append(SQLBuffer buf, Joins joins) {
|
public void append(SQLBuffer buf, Joins joins) {
|
||||||
if (joins == null || joins.isEmpty())
|
if (joins == null || joins.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
if (_joinSyntax == JoinSyntaxes.SYNTAX_SQL92)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!buf.isEmpty())
|
if (!buf.isEmpty())
|
||||||
buf.append(" AND ");
|
buf.append(" AND ");
|
||||||
|
@ -1915,6 +1822,10 @@ public class SelectImpl
|
||||||
return and((PathJoins) joins1, (PathJoins) joins2, true);
|
return and((PathJoins) joins1, (PathJoins) joins2, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Select getSelect() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Combine the given joins.
|
* Combine the given joins.
|
||||||
*/
|
*/
|
||||||
|
@ -1925,21 +1836,26 @@ public class SelectImpl
|
||||||
|
|
||||||
SelectJoins sj = new SelectJoins(this);
|
SelectJoins sj = new SelectJoins(this);
|
||||||
if (j1 == null || j1.isEmpty()) {
|
if (j1 == null || j1.isEmpty()) {
|
||||||
|
if (j2.getSelect() == this) {
|
||||||
if (nullJoins)
|
if (nullJoins)
|
||||||
sj.setJoins(j2.joins());
|
sj.setJoins(j2.joins());
|
||||||
else
|
else
|
||||||
sj.setJoins(new JoinSet(j2.joins()));
|
sj.setJoins(new JoinSet(j2.joins()));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
JoinSet set;
|
JoinSet set = null;
|
||||||
|
if (j1.getSelect() == this) {
|
||||||
if (nullJoins)
|
if (nullJoins)
|
||||||
set = j1.joins();
|
set = j1.joins();
|
||||||
else
|
else
|
||||||
set = new JoinSet(j1.joins());
|
set = new JoinSet(j1.joins());
|
||||||
|
|
||||||
if (j2 != null && !j2.isEmpty())
|
if (j2 != null && !j2.isEmpty()
|
||||||
|
&& j2.getSelect() == this)
|
||||||
set.addAll(j2.joins());
|
set.addAll(j2.joins());
|
||||||
sj.setJoins(set);
|
sj.setJoins(set);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// null previous joins; all are combined into this one
|
// null previous joins; all are combined into this one
|
||||||
if (nullJoins && j1 != null)
|
if (nullJoins && j1 != null)
|
||||||
|
@ -2070,72 +1986,111 @@ public class SelectImpl
|
||||||
if (_from != null)
|
if (_from != null)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
Integer i = null;
|
||||||
Object key = table.getFullName();
|
Object key = table.getFullName();
|
||||||
if (pj != null && pj.path() != null)
|
if (pj != null && pj.path() != null)
|
||||||
key = new Key(pj.path().toString(), key);
|
key = new Key(pj.path().toString(), key);
|
||||||
|
|
||||||
|
if (_ctx != null)
|
||||||
|
i = findAliasForQuery(table, pj, key, create);
|
||||||
|
|
||||||
|
if (i != null)
|
||||||
|
return i.intValue();
|
||||||
|
|
||||||
// check out existing aliases
|
// check out existing aliases
|
||||||
Integer i = findAlias(table, key, false, null);
|
i = findAlias(table, key);
|
||||||
|
|
||||||
if (i != null)
|
if (i != null)
|
||||||
return i.intValue();
|
return i.intValue();
|
||||||
if (!create)
|
if (!create)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
// not found; create alias
|
// not found; create alias
|
||||||
i = Numbers.valueOf(aliasSize());
|
i = Numbers.valueOf(aliasSize(null));
|
||||||
|
// System.out.println("GetTableIndex\t"+
|
||||||
|
// ((_parent != null) ? "Sub" :"") +
|
||||||
|
// " created alias: "+
|
||||||
|
// i.intValue()+ " "+ key);
|
||||||
recordTableAlias(table, key, i);
|
recordTableAlias(table, key, i);
|
||||||
return i.intValue();
|
return i.intValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private Integer findAliasForQuery(Table table, PathJoins pj, Object key,
|
||||||
* Attempt to find the alias for the given key.
|
boolean create) {
|
||||||
*
|
Integer i = null;
|
||||||
* @param fromParent whether a parent is checking its subselects
|
SelectImpl sel = this;
|
||||||
* @param fromSub the subselect checking its parent
|
String alias = _schemaAlias;
|
||||||
*/
|
if (isPathInThisContext(pj) || table.isAssociation())
|
||||||
private Integer findAlias(Table table, Object key, boolean fromParent,
|
alias = null;
|
||||||
SelectImpl fromSub) {
|
|
||||||
|
// find the context where this alias is defined
|
||||||
|
Context ctx = (alias != null) ?
|
||||||
|
_ctx.findContext(alias) : null;
|
||||||
|
if (ctx != null)
|
||||||
|
sel = (SelectImpl) ctx.getSelect();
|
||||||
|
|
||||||
|
if (!create)
|
||||||
|
i = sel.findAlias(table, key); // find in parent and in myself
|
||||||
|
else
|
||||||
|
i = sel.getAlias(table, key); // find in myself
|
||||||
|
if (i != null)
|
||||||
|
return i;
|
||||||
|
|
||||||
|
if (create) { // create here
|
||||||
|
i = sel.createAlias(table, key);
|
||||||
|
} else if (ctx != null && ctx != ctx()) { // create in other select
|
||||||
|
i = ((SelectImpl)ctx.getSelect()).createAlias(table, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPathInThisContext(PathJoins pj) {
|
||||||
|
// currCtx is set from Action, it is reset to null after the PCPath initialization
|
||||||
|
Context currCtx = pj == null ? null : ((PathJoinsImpl)pj).context;
|
||||||
|
|
||||||
|
// lastCtx is set to currCtx after the SelectJoins.join. pj.lastCtx and pj.path string are
|
||||||
|
// the last snapshot of pj. They will be used together for later table alias resolution in
|
||||||
|
// the getColumnAlias().
|
||||||
|
Context lastCtx = pj == null ? null : ((PathJoinsImpl)pj).lastContext;
|
||||||
|
Context thisCtx = currCtx == null ? lastCtx : currCtx;
|
||||||
|
String corrVar = pj == null ? null : pj.getCorrelatedVariable();
|
||||||
|
|
||||||
|
return (pj != null && pj.path() != null &&
|
||||||
|
(corrVar == null || (thisCtx != null && ctx() == thisCtx)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer getAlias(Table table, Object key) {
|
||||||
|
Integer alias = null;
|
||||||
|
if (_aliases != null)
|
||||||
|
alias = (Integer) _aliases.get(key);
|
||||||
|
return alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int createAlias(Table table, Object key) {
|
||||||
|
Integer i = Numbers.valueOf(ctx().nextAlias());
|
||||||
|
// System.out.println("\t"+
|
||||||
|
// ((_parent != null) ? "Sub" :"") +
|
||||||
|
// "Query created alias: "+
|
||||||
|
// i.intValue()+ " "+ key);
|
||||||
|
recordTableAlias(table, key, i);
|
||||||
|
return i.intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer findAlias(Table table, Object key) {
|
||||||
Integer alias = null;
|
Integer alias = null;
|
||||||
if (_aliases != null) {
|
if (_aliases != null) {
|
||||||
alias = (Integer) ((fromParent) ? _aliases.remove(key)
|
alias = (Integer) _aliases.get(key);
|
||||||
: _aliases.get(key));
|
|
||||||
if (alias != null) {
|
if (alias != null) {
|
||||||
if (fromParent)
|
|
||||||
_tables.remove(alias);
|
|
||||||
return alias;
|
return alias;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!fromParent && _parent != null) {
|
if (_parent != null) {
|
||||||
boolean removeAliasFromParent = key.toString().indexOf(":") != -1;
|
alias = _parent.findAlias(table, key);
|
||||||
alias = _parent.findAlias(table, key, removeAliasFromParent, this);
|
|
||||||
if (alias != null) {
|
if (alias != null) {
|
||||||
if (removeAliasFromParent) {
|
|
||||||
recordTableAlias(table, key, alias);
|
|
||||||
_removedAliasFromParent.set(alias.intValue());
|
|
||||||
}
|
|
||||||
return alias;
|
return alias;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_subsels != null) {
|
|
||||||
SelectImpl sub;
|
|
||||||
for (int i = 0; i < _subsels.size(); i++) {
|
|
||||||
sub = (SelectImpl) _subsels.get(i);
|
|
||||||
if (sub == fromSub)
|
|
||||||
continue;
|
|
||||||
if (alias != null) {
|
|
||||||
if (sub._aliases != null)
|
|
||||||
sub._aliases.remove(key);
|
|
||||||
if (sub._tables != null)
|
|
||||||
sub._tables.remove(alias);
|
|
||||||
} else {
|
|
||||||
if (key instanceof String) {
|
|
||||||
alias = sub.findAlias(table, key, true, null);
|
|
||||||
if (!fromParent && alias != null)
|
|
||||||
recordTableAlias(table, key, alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return alias;
|
return alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2157,26 +2112,16 @@ public class SelectImpl
|
||||||
/**
|
/**
|
||||||
* Calculate total number of aliases.
|
* Calculate total number of aliases.
|
||||||
*/
|
*/
|
||||||
private int aliasSize() {
|
private int aliasSize(SelectImpl fromSub) {
|
||||||
return aliasSize(false, null);
|
int aliases = (_parent == null) ? 0
|
||||||
}
|
: _parent.aliasSize(this);
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate total number of aliases.
|
|
||||||
*
|
|
||||||
* @param fromParent whether a parent is checking its subselects
|
|
||||||
* @param fromSub the subselect checking its parent
|
|
||||||
*/
|
|
||||||
private int aliasSize(boolean fromParent, SelectImpl fromSub) {
|
|
||||||
int aliases = (fromParent || _parent == null) ? 0
|
|
||||||
: _parent.aliasSize(false, this);
|
|
||||||
aliases += (_aliases == null) ? 0 : _aliases.size();
|
aliases += (_aliases == null) ? 0 : _aliases.size();
|
||||||
if (_subsels != null) {
|
if (_subsels != null) {
|
||||||
SelectImpl sub;
|
SelectImpl sub;
|
||||||
for (int i = 0; i < _subsels.size(); i++) {
|
for (int i = 0; i < _subsels.size(); i++) {
|
||||||
sub = (SelectImpl) _subsels.get(i);
|
sub = (SelectImpl) _subsels.get(i);
|
||||||
if (sub != fromSub)
|
if (sub != fromSub)
|
||||||
aliases += sub.aliasSize(true, null);
|
aliases += sub.aliasSize(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return aliases;
|
return aliases;
|
||||||
|
@ -2269,6 +2214,37 @@ public class SelectImpl
|
||||||
private static class Placeholder {
|
private static class Placeholder {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
_aliases = null;
|
||||||
|
_eager = null;
|
||||||
|
_eagerKeys = null;
|
||||||
|
_expectedResultCount = 0;
|
||||||
|
_flags = 0;
|
||||||
|
_from = null;
|
||||||
|
_grouped = null;
|
||||||
|
_grouping = null;
|
||||||
|
_having = null;
|
||||||
|
_joins = null;
|
||||||
|
_joinSyntax = 0;
|
||||||
|
_nullIds = 0;
|
||||||
|
_ordered = null;
|
||||||
|
_ordering = null;
|
||||||
|
_orders = 0;
|
||||||
|
_outer = null;
|
||||||
|
_parent = null;
|
||||||
|
_placeholders = 0;
|
||||||
|
_preJoins = null;
|
||||||
|
_schemaAlias = null;
|
||||||
|
_selects._aliases = null;
|
||||||
|
_selects._ids = null;
|
||||||
|
_subPath = null;
|
||||||
|
_subsels = null;
|
||||||
|
_tables = null;
|
||||||
|
_where = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key type used for aliases.
|
* Key type used for aliases.
|
||||||
*/
|
*/
|
||||||
|
@ -2599,6 +2575,21 @@ public class SelectImpl
|
||||||
return this;
|
return this;
|
||||||
return new PathJoinsImpl().setSubselect(alias);
|
return new PathJoinsImpl().setSubselect(alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Joins setCorrelatedVariable(String var) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Joins setJoinContext(Context ctx) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCorrelatedVariable() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void moveJoinsToParent() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2609,6 +2600,13 @@ public class SelectImpl
|
||||||
|
|
||||||
protected StringBuffer path = null;
|
protected StringBuffer path = null;
|
||||||
protected String var = null;
|
protected String var = null;
|
||||||
|
protected String correlatedVar = null;
|
||||||
|
protected Context context = null;
|
||||||
|
protected Context lastContext = null;
|
||||||
|
|
||||||
|
public Select getSelect() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isOuter() {
|
public boolean isOuter() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -2642,9 +2640,25 @@ public class SelectImpl
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getVariable() {
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Joins setCorrelatedVariable(String var) {
|
||||||
|
this.correlatedVar = var;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCorrelatedVariable() {
|
||||||
|
return correlatedVar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Joins setJoinContext(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Joins setSubselect(String alias) {
|
public Joins setSubselect(String alias) {
|
||||||
if (!alias.endsWith(":"))
|
|
||||||
alias += ':';
|
|
||||||
append(alias);
|
append(alias);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -2700,6 +2714,9 @@ public class SelectImpl
|
||||||
return "PathJoinsImpl<" + hashCode() + ">: "
|
return "PathJoinsImpl<" + hashCode() + ">: "
|
||||||
+ String.valueOf(path);
|
+ String.valueOf(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void moveJoinsToParent() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2718,6 +2735,10 @@ public class SelectImpl
|
||||||
_sel = sel;
|
_sel = sel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Select getSelect() {
|
||||||
|
return _sel;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isOuter() {
|
public boolean isOuter() {
|
||||||
return _outer;
|
return _outer;
|
||||||
}
|
}
|
||||||
|
@ -2778,9 +2799,14 @@ public class SelectImpl
|
||||||
// until we get past the local table
|
// until we get past the local table
|
||||||
String var = this.var;
|
String var = this.var;
|
||||||
this.var = null;
|
this.var = null;
|
||||||
|
Context ctx = context;
|
||||||
|
context = null;
|
||||||
|
|
||||||
int alias1 = _sel.getTableIndex(localTable, this, true);
|
int alias1 = _sel.getTableIndex(localTable, this, true);
|
||||||
this.append(var);
|
this.append(var);
|
||||||
|
this.append(correlatedVar);
|
||||||
|
context = ctx;
|
||||||
|
|
||||||
int alias2 = _sel.getTableIndex(foreignTable, this, true);
|
int alias2 = _sel.getTableIndex(foreignTable, this, true);
|
||||||
Join j = new Join(localTable, alias1, foreignTable, alias2,
|
Join j = new Join(localTable, alias1, foreignTable, alias2,
|
||||||
null, false);
|
null, false);
|
||||||
|
@ -2789,7 +2815,10 @@ public class SelectImpl
|
||||||
if (_joins == null)
|
if (_joins == null)
|
||||||
_joins = new JoinSet();
|
_joins = new JoinSet();
|
||||||
_joins.add(j);
|
_joins.add(j);
|
||||||
|
setCorrelated(j);
|
||||||
_outer = false;
|
_outer = false;
|
||||||
|
lastContext = context;
|
||||||
|
context = null;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2817,6 +2846,8 @@ public class SelectImpl
|
||||||
// until we get past the local table
|
// until we get past the local table
|
||||||
String var = this.var;
|
String var = this.var;
|
||||||
this.var = null;
|
this.var = null;
|
||||||
|
Context ctx = context;
|
||||||
|
context = null;
|
||||||
|
|
||||||
// get first table alias before updating path; if there is a from
|
// get first table alias before updating path; if there is a from
|
||||||
// select then we shouldn't actually create a join object, since
|
// select then we shouldn't actually create a join object, since
|
||||||
|
@ -2825,13 +2856,19 @@ public class SelectImpl
|
||||||
Table table1 = null;
|
Table table1 = null;
|
||||||
int alias1 = -1;
|
int alias1 = -1;
|
||||||
if (createJoin) {
|
if (createJoin) {
|
||||||
|
boolean createIndex = true;
|
||||||
table1 = (inverse) ? fk.getPrimaryKeyTable() : fk.getTable();
|
table1 = (inverse) ? fk.getPrimaryKeyTable() : fk.getTable();
|
||||||
alias1 = _sel.getTableIndex(table1, this, true);
|
if (correlatedVar != null)
|
||||||
|
createIndex = false; // not to create here
|
||||||
|
alias1 = _sel.getTableIndex(table1, this, createIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the path with the relation name before getting pk alias
|
// update the path with the relation name before getting pk alias
|
||||||
this.append(name);
|
this.append(name);
|
||||||
this.append(var);
|
this.append(var);
|
||||||
|
this.append(correlatedVar);
|
||||||
|
context = ctx;
|
||||||
|
|
||||||
if (toMany) {
|
if (toMany) {
|
||||||
_sel._flags |= IMPLICIT_DISTINCT;
|
_sel._flags |= IMPLICIT_DISTINCT;
|
||||||
_sel._flags |= TO_MANY;
|
_sel._flags |= TO_MANY;
|
||||||
|
@ -2839,9 +2876,16 @@ public class SelectImpl
|
||||||
_outer = outer;
|
_outer = outer;
|
||||||
|
|
||||||
if (createJoin) {
|
if (createJoin) {
|
||||||
|
boolean createIndex = true;
|
||||||
Table table2 = (inverse) ? fk.getTable()
|
Table table2 = (inverse) ? fk.getTable()
|
||||||
: fk.getPrimaryKeyTable();
|
: fk.getPrimaryKeyTable();
|
||||||
int alias2 = _sel.getTableIndex(table2, this, true);
|
if (table2.isAssociation())
|
||||||
|
createIndex = true;
|
||||||
|
else if (context == _sel.ctx())
|
||||||
|
createIndex = true;
|
||||||
|
else if (correlatedVar != null)
|
||||||
|
createIndex = false;
|
||||||
|
int alias2 = _sel.getTableIndex(table2, this, createIndex);
|
||||||
Join j = new Join(table1, alias1, table2, alias2, fk, inverse);
|
Join j = new Join(table1, alias1, table2, alias2, fk, inverse);
|
||||||
j.setType((outer) ? Join.TYPE_OUTER : Join.TYPE_INNER);
|
j.setType((outer) ? Join.TYPE_OUTER : Join.TYPE_INNER);
|
||||||
|
|
||||||
|
@ -2850,10 +2894,91 @@ public class SelectImpl
|
||||||
if (_joins.add(j) && (subs == Select.SUBS_JOINABLE
|
if (_joins.add(j) && (subs == Select.SUBS_JOINABLE
|
||||||
|| subs == Select.SUBS_NONE))
|
|| subs == Select.SUBS_NONE))
|
||||||
j.setRelation(target, subs, clone(_sel));
|
j.setRelation(target, subs, clone(_sel));
|
||||||
|
|
||||||
|
setCorrelated(j);
|
||||||
}
|
}
|
||||||
|
lastContext = context;
|
||||||
|
context = null;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setCorrelated(Join j) {
|
||||||
|
if (_sel._parent == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_sel._aliases == null) {
|
||||||
|
j.setIsNotMyJoin();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object aliases[] = _sel._aliases.values().toArray();
|
||||||
|
boolean found1 = false;
|
||||||
|
boolean found2 = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < aliases.length; i++) {
|
||||||
|
int alias = ((Integer)aliases[i]).intValue();
|
||||||
|
if (alias == j.getIndex1())
|
||||||
|
found1 = true;
|
||||||
|
if (alias == j.getIndex2())
|
||||||
|
found2 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found1 && found2)
|
||||||
|
return;
|
||||||
|
else if (!found1 && !found2) {
|
||||||
|
j.setIsNotMyJoin();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
j.setCorrelated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void moveJoinsToParent() {
|
||||||
|
if (_joins == null)
|
||||||
|
return;
|
||||||
|
Join j = null;
|
||||||
|
List<Join> removed = new ArrayList<Join>(5);
|
||||||
|
for (Iterator itr = _joins.iterator(); itr.hasNext();) {
|
||||||
|
j = (Join) itr.next();
|
||||||
|
if (j.isNotMyJoin()) {
|
||||||
|
addJoinsToParent(_sel._parent, j);
|
||||||
|
removed.add(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Join join : removed) {
|
||||||
|
_joins.remove(join);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addJoinsToParent(SelectImpl parent, Join join) {
|
||||||
|
if (parent._aliases == null)
|
||||||
|
return;
|
||||||
|
Object aliases[] = parent._aliases.values().toArray();
|
||||||
|
boolean found1 = false;
|
||||||
|
boolean found2 = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < aliases.length; i++) {
|
||||||
|
int alias = ((Integer)aliases[i]).intValue();
|
||||||
|
if (alias == join.getIndex1())
|
||||||
|
found1 = true;
|
||||||
|
if (alias == join.getIndex2())
|
||||||
|
found2 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found1 && found2) {
|
||||||
|
// this is my join, add join
|
||||||
|
if (parent._joins == null)
|
||||||
|
parent._joins = new SelectJoins(parent);
|
||||||
|
SelectJoins p = parent._joins;
|
||||||
|
if (p.joins() == null)
|
||||||
|
p.setJoins(new JoinSet());
|
||||||
|
p.joins().add(join);
|
||||||
|
}
|
||||||
|
else if (parent._parent != null)
|
||||||
|
addJoinsToParent(parent._parent, join);
|
||||||
|
}
|
||||||
|
|
||||||
public SelectJoins clone(SelectImpl sel) {
|
public SelectJoins clone(SelectImpl sel) {
|
||||||
SelectJoins sj = new SelectJoins(sel);
|
SelectJoins sj = new SelectJoins(sel);
|
||||||
sj.var = var;
|
sj.var = var;
|
||||||
|
@ -3067,6 +3192,25 @@ public class SelectImpl
|
||||||
_idents = null;
|
_idents = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Joins setCorrelatedVariable(String var) {
|
||||||
|
if (var == null)
|
||||||
|
return this;
|
||||||
|
return new SelectJoins(this).setCorrelatedVariable(var);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Joins setJoinContext(Context ctx) {
|
||||||
|
if (ctx == null)
|
||||||
|
return this;
|
||||||
|
return new SelectJoins(this).setJoinContext(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCorrelatedVariable() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void moveJoinsToParent() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3106,5 +3250,11 @@ interface PathJoins
|
||||||
* Null the set of {@link Join} elements.
|
* Null the set of {@link Join} elements.
|
||||||
*/
|
*/
|
||||||
public void nullJoins();
|
public void nullJoins();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The select owner of this join
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Select getSelect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -169,10 +169,12 @@ public abstract class AbstractExpressionBuilder {
|
||||||
type = TYPE_OBJECT;
|
type = TYPE_OBJECT;
|
||||||
else
|
else
|
||||||
meta = getMetaData(type, false);
|
meta = getMetaData(type, false);
|
||||||
if (meta != null)
|
if (meta != null) {
|
||||||
addAccessPath(meta);
|
addAccessPath(meta);
|
||||||
|
addSchemaToContext(id, meta);
|
||||||
|
}
|
||||||
|
|
||||||
Value var;
|
Value var = null;
|
||||||
if (bind)
|
if (bind)
|
||||||
var = factory.newBoundVariable(id, type);
|
var = factory.newBoundVariable(id, type);
|
||||||
else
|
else
|
||||||
|
@ -182,6 +184,8 @@ public abstract class AbstractExpressionBuilder {
|
||||||
if (_seenVars == null)
|
if (_seenVars == null)
|
||||||
_seenVars = new HashMap<String,Value>();
|
_seenVars = new HashMap<String,Value>();
|
||||||
_seenVars.put(id, var);
|
_seenVars.put(id, var);
|
||||||
|
|
||||||
|
addVariableToContext(id, var);
|
||||||
return var;
|
return var;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -521,5 +525,28 @@ public abstract class AbstractExpressionBuilder {
|
||||||
* Returns the current string being parsed; used for error messages.
|
* Returns the current string being parsed; used for error messages.
|
||||||
*/
|
*/
|
||||||
protected abstract String currentQuery ();
|
protected abstract String currentQuery ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the schema alias to the current JPQL query context.
|
||||||
|
* @param alias
|
||||||
|
* @param meta
|
||||||
|
*/
|
||||||
|
protected abstract void addSchemaToContext(String alias,
|
||||||
|
ClassMetaData meta);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the variable associated with the schema alias (id) to
|
||||||
|
* the current JPQL query context.
|
||||||
|
* @param id
|
||||||
|
* @param var
|
||||||
|
*/
|
||||||
|
protected abstract void addVariableToContext(String id, Value var);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the variable associated with the schema alias (id).
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected abstract Value getSeenVariable(String id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -196,4 +196,14 @@ public class CandidatePath
|
||||||
public XMLMetaData getXmlMapping() {
|
public XMLMetaData getXmlMapping() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSchemaAlias(String schemaAlias) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSchemaAlias() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubqueryContext(Context conext) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,224 @@
|
||||||
|
/*
|
||||||
|
* 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.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.ParsedJPQL;
|
||||||
|
import org.apache.openjpa.meta.ClassMetaData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JPQL / Criteria Query Context
|
||||||
|
* @since 2.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Context implements Serializable {
|
||||||
|
|
||||||
|
public final ParsedJPQL parsed;
|
||||||
|
public ClassMetaData meta;
|
||||||
|
public String schemaAlias;
|
||||||
|
public Subquery subquery;
|
||||||
|
public Expression from = null;
|
||||||
|
private Context parent = null;
|
||||||
|
private List<Context> subsels = null;
|
||||||
|
private Object select = null;
|
||||||
|
private int aliasCount = -1;
|
||||||
|
private Map<String,Value> variables = new HashMap<String,Value>();
|
||||||
|
private Map<String,ClassMetaData> schemas =
|
||||||
|
new HashMap<String,ClassMetaData>();
|
||||||
|
|
||||||
|
public Context(ParsedJPQL parsed, Subquery subquery, Context parent) {
|
||||||
|
this.parsed = parsed;
|
||||||
|
this.subquery = subquery;
|
||||||
|
this.parent = parent;
|
||||||
|
if (subquery != null) {
|
||||||
|
this.select = subquery.getSelect();
|
||||||
|
parent.addSubselContext(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubquery(Subquery subquery) {
|
||||||
|
this.subquery = subquery;
|
||||||
|
this.select = subquery.getSelect();
|
||||||
|
parent.addSubselContext(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassMetaData meta() {
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String schemaAlias() {
|
||||||
|
return schemaAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Subquery subquery() {
|
||||||
|
return subquery;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns next table alias to be created.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int nextAlias() {
|
||||||
|
Context p = this;
|
||||||
|
while (p.subquery != null) {
|
||||||
|
p = p.parent;
|
||||||
|
}
|
||||||
|
p.aliasCount++;
|
||||||
|
return p.aliasCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset alias count for prepared query cache
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void resetAliasCount() {
|
||||||
|
Context p = this;
|
||||||
|
while (p.subquery != null) {
|
||||||
|
p = p.parent;
|
||||||
|
}
|
||||||
|
p.aliasCount = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the select for this context.
|
||||||
|
* @param select
|
||||||
|
*/
|
||||||
|
public void setSelect(Object select) {
|
||||||
|
this.select = select;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the select associated with this context.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Object getSelect() {
|
||||||
|
return select;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the subquery context in this context.
|
||||||
|
* @param sub
|
||||||
|
*/
|
||||||
|
private void addSubselContext(Context sub) {
|
||||||
|
if (subsels == null)
|
||||||
|
subsels = new ArrayList<Context>();
|
||||||
|
subsels.add(sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the subquery context.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public List<Context> getSubselContexts() {
|
||||||
|
return subsels;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the subquery in this context.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Subquery getSubquery() {
|
||||||
|
return subquery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParent(Context parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addVariable(String id, Value var) {
|
||||||
|
variables.put(id.toLowerCase(), var);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSchema(String id, ClassMetaData meta) {
|
||||||
|
schemas.put(id.toLowerCase(), meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassMetaData getSchema(String id) {
|
||||||
|
if (id != null)
|
||||||
|
return schemas.get(id.toLowerCase());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an alias and return its associated variable.
|
||||||
|
* @param var
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Value getVariable(String var) {
|
||||||
|
Value variable = var == null ? null
|
||||||
|
: variables.get(var.toLowerCase());
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an alias find the context of its associated
|
||||||
|
* variable where it is defined.
|
||||||
|
* @param alias
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Context findContext(String alias) {
|
||||||
|
Value var = getVariable(alias);
|
||||||
|
if (var != null)
|
||||||
|
return this;
|
||||||
|
for (Context p = parent; p != null; ) {
|
||||||
|
var = p.getVariable(alias);
|
||||||
|
if (var != null)
|
||||||
|
return p;
|
||||||
|
p = p.parent;
|
||||||
|
}
|
||||||
|
if (subsels != null) {
|
||||||
|
for (Context subsel : subsels) {
|
||||||
|
if (subsel != null) {
|
||||||
|
var = subsel.getVariable(alias);
|
||||||
|
if (var != null)
|
||||||
|
return subsel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an alias find the variable in JPQL contexts.
|
||||||
|
* @param alias
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Value findVariable(String alias) {
|
||||||
|
Value var = getVariable(alias);
|
||||||
|
if (var != null)
|
||||||
|
return var;
|
||||||
|
for (Context p = parent; p != null; ) {
|
||||||
|
var = p.getVariable(alias);
|
||||||
|
if (var != null)
|
||||||
|
return var;
|
||||||
|
p = p.parent;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -67,4 +67,15 @@ public interface Path
|
||||||
* @return Return xmlmapping
|
* @return Return xmlmapping
|
||||||
*/
|
*/
|
||||||
public XMLMetaData getXmlMapping();
|
public XMLMetaData getXmlMapping();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the schema alias (the identification variable)
|
||||||
|
* this path is begin with.
|
||||||
|
* @param schemaAlias
|
||||||
|
*/
|
||||||
|
public void setSchemaAlias(String schemaAlias);
|
||||||
|
|
||||||
|
public String getSchemaAlias();
|
||||||
|
|
||||||
|
public void setSubqueryContext(Context context);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,12 @@ import java.io.Serializable;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
import org.apache.commons.collections.map.LinkedMap;
|
import org.apache.commons.collections.map.LinkedMap;
|
||||||
import org.apache.openjpa.kernel.QueryOperations;
|
import org.apache.openjpa.kernel.QueryOperations;
|
||||||
import org.apache.openjpa.kernel.StoreQuery;
|
import org.apache.openjpa.kernel.StoreQuery;
|
||||||
|
import org.apache.openjpa.kernel.exps.Context;
|
||||||
import org.apache.openjpa.meta.ClassMetaData;
|
import org.apache.openjpa.meta.ClassMetaData;
|
||||||
import org.apache.openjpa.meta.FieldMetaData;
|
import org.apache.openjpa.meta.FieldMetaData;
|
||||||
|
|
||||||
|
@ -69,8 +71,25 @@ public class QueryExpressions
|
||||||
public String[] fetchInnerPaths = StoreQuery.EMPTY_STRINGS;
|
public String[] fetchInnerPaths = StoreQuery.EMPTY_STRINGS;
|
||||||
public Value[] range = EMPTY_VALUES;
|
public Value[] range = EMPTY_VALUES;
|
||||||
private Boolean _aggregate = null;
|
private Boolean _aggregate = null;
|
||||||
|
private Stack<Context> _contexts = null;
|
||||||
public Object state;
|
public Object state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set reference to the JPQL query contexts.
|
||||||
|
* @param contexts
|
||||||
|
*/
|
||||||
|
public void setContexts(Stack<Context> contexts) {
|
||||||
|
_contexts = contexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current JPQL query context.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Context ctx() {
|
||||||
|
return _contexts.peek();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether this is an aggregate results.
|
* Whether this is an aggregate results.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -41,6 +41,10 @@ class SubQ
|
||||||
_alias = alias;
|
_alias = alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object getSelect() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public String getCandidateAlias() {
|
public String getCandidateAlias() {
|
||||||
return _alias;
|
return _alias;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,4 +36,6 @@ public interface Subquery
|
||||||
* Set the parsed subquery.
|
* Set the parsed subquery.
|
||||||
*/
|
*/
|
||||||
public void setQueryExpressions(QueryExpressions query);
|
public void setQueryExpressions(QueryExpressions query);
|
||||||
|
|
||||||
|
public Object getSelect();
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,4 +135,8 @@ public abstract class Val
|
||||||
public Path getPath() {
|
public Path getPath() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,4 +90,6 @@ public interface Value
|
||||||
public Value getSelectAs();
|
public Value getSelectAs();
|
||||||
|
|
||||||
public Path getPath();
|
public Path getPath();
|
||||||
|
|
||||||
|
public String getName();
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ import org.apache.openjpa.kernel.exps.QueryExpressions;
|
||||||
import org.apache.openjpa.kernel.exps.Resolver;
|
import org.apache.openjpa.kernel.exps.Resolver;
|
||||||
import org.apache.openjpa.kernel.exps.Subquery;
|
import org.apache.openjpa.kernel.exps.Subquery;
|
||||||
import org.apache.openjpa.kernel.exps.Value;
|
import org.apache.openjpa.kernel.exps.Value;
|
||||||
|
import org.apache.openjpa.kernel.exps.Context;
|
||||||
import org.apache.openjpa.lib.util.Localizer;
|
import org.apache.openjpa.lib.util.Localizer;
|
||||||
import org.apache.openjpa.lib.util.Localizer.Message;
|
import org.apache.openjpa.lib.util.Localizer.Message;
|
||||||
import org.apache.openjpa.lib.log.Log;
|
import org.apache.openjpa.lib.log.Log;
|
||||||
|
@ -99,7 +100,7 @@ public class JPQLExpressionBuilder
|
||||||
? (ParsedJPQL) parsedQuery
|
? (ParsedJPQL) parsedQuery
|
||||||
: parsedQuery instanceof String
|
: parsedQuery instanceof String
|
||||||
? getParsedQuery((String) parsedQuery)
|
? getParsedQuery((String) parsedQuery)
|
||||||
: null, null));
|
: null, null, null));
|
||||||
|
|
||||||
if (ctx().parsed == null)
|
if (ctx().parsed == null)
|
||||||
throw new InternalException(parsedQuery + "");
|
throw new InternalException(parsedQuery + "");
|
||||||
|
@ -271,11 +272,15 @@ public class JPQLExpressionBuilder
|
||||||
|
|
||||||
QueryExpressions getQueryExpressions() {
|
QueryExpressions getQueryExpressions() {
|
||||||
QueryExpressions exps = new QueryExpressions();
|
QueryExpressions exps = new QueryExpressions();
|
||||||
|
exps.setContexts(contexts);
|
||||||
|
|
||||||
evalQueryOperation(exps);
|
evalQueryOperation(exps);
|
||||||
|
|
||||||
Expression filter = null;
|
Expression filter = null;
|
||||||
filter = and(evalFromClause(root().id == JJTSELECT), filter);
|
Expression from = ctx().from;
|
||||||
|
if (from == null)
|
||||||
|
from = evalFromClause(root().id == JJTSELECT);
|
||||||
|
filter = and(from, filter);
|
||||||
filter = and(evalWhereClause(), filter);
|
filter = and(evalWhereClause(), filter);
|
||||||
filter = and(evalSelectClause(exps), filter);
|
filter = and(evalSelectClause(exps), filter);
|
||||||
|
|
||||||
|
@ -413,7 +418,7 @@ public class JPQLExpressionBuilder
|
||||||
exps.ascending[i] = node.getChildCount() <= 1 ||
|
exps.ascending[i] = node.getChildCount() <= 1 ||
|
||||||
lastChild(node).id == JJTASCENDING ? true : false;
|
lastChild(node).id == JJTASCENDING ? true : false;
|
||||||
}
|
}
|
||||||
// check if order by selec item alias
|
// check if order by select item result alias
|
||||||
for (int i = 0; i < ordercount; i++) {
|
for (int i = 0; i < ordercount; i++) {
|
||||||
if (exps.orderingClauses[i] != null &&
|
if (exps.orderingClauses[i] != null &&
|
||||||
!exps.orderingClauses[i].equals(""))
|
!exps.orderingClauses[i].equals(""))
|
||||||
|
@ -543,12 +548,15 @@ public class JPQLExpressionBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expression evalFromClause(boolean needsAlias) {
|
private Expression evalFromClause(boolean needsAlias) {
|
||||||
Expression exp = null;
|
|
||||||
|
|
||||||
// build up the alias map in the FROM clause
|
// build up the alias map in the FROM clause
|
||||||
JPQLNode from = root().findChildByID(JJTFROM, false);
|
JPQLNode from = root().findChildByID(JJTFROM, false);
|
||||||
if (from == null)
|
if (from == null)
|
||||||
throw parseException(EX_USER, "no-from-clause", null, null);
|
throw parseException(EX_USER, "no-from-clause", null, null);
|
||||||
|
return evalFromClause(from, needsAlias);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expression evalFromClause(JPQLNode from, boolean needsAlias) {
|
||||||
|
Expression exp = null;
|
||||||
|
|
||||||
for (int i = 0; i < from.children.length; i++) {
|
for (int i = 0; i < from.children.length; i++) {
|
||||||
JPQLNode node = from.children[i];
|
JPQLNode node = from.children[i];
|
||||||
|
@ -573,7 +581,7 @@ public class JPQLExpressionBuilder
|
||||||
|
|
||||||
private Expression bindVariableForKeyPath(Path path, String alias,
|
private Expression bindVariableForKeyPath(Path path, String alias,
|
||||||
Expression exp) {
|
Expression exp) {
|
||||||
if (alias != null && !isSeendVariable(alias)) {
|
if (alias != null && ctx().findVariable(alias) == null) {
|
||||||
// subquery may have KEY range over a variable
|
// subquery may have KEY range over a variable
|
||||||
// that is not defined.
|
// that is not defined.
|
||||||
JPQLNode key = root().findChildByID(JJTKEY, true);
|
JPQLNode key = root().findChildByID(JJTKEY, true);
|
||||||
|
@ -584,6 +592,29 @@ public class JPQLExpressionBuilder
|
||||||
}
|
}
|
||||||
return exp;
|
return exp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Expression getSubquery(String alias, Path path, Expression exp) {
|
||||||
|
FieldMetaData fmd = path.last();
|
||||||
|
ClassMetaData candidate = getFieldType(fmd);
|
||||||
|
if (candidate == null && fmd.isElementCollection())
|
||||||
|
candidate = fmd.getDefiningMetaData();
|
||||||
|
|
||||||
|
setCandidate(candidate, alias);
|
||||||
|
|
||||||
|
Context subContext = ctx();
|
||||||
|
Subquery subquery = ctx().getSubquery();
|
||||||
|
if (subquery == null){
|
||||||
|
subquery = factory.newSubquery(candidate, true, alias);
|
||||||
|
subContext.setSubquery(subquery);
|
||||||
|
}
|
||||||
|
Path subpath = factory.newPath(subquery);
|
||||||
|
subpath.setMetaData(candidate);
|
||||||
|
subquery.setMetaData(candidate);
|
||||||
|
exp = bindVariableForKeyPath(path, alias, exp);
|
||||||
|
exp = and(exp, factory.equal(path, subpath));
|
||||||
|
return exp;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a join condition to the given expression.
|
* Adds a join condition to the given expression.
|
||||||
*
|
*
|
||||||
|
@ -604,14 +635,8 @@ public class JPQLExpressionBuilder
|
||||||
JPQLNode alias = node.getChildCount() >= 2 ? right(node) : null;
|
JPQLNode alias = node.getChildCount() >= 2 ? right(node) : null;
|
||||||
// OPENJPA-15 support subquery's from clause do not start with
|
// OPENJPA-15 support subquery's from clause do not start with
|
||||||
// identification_variable_declaration()
|
// identification_variable_declaration()
|
||||||
if (inner && ctx().subquery != null && ctx().schemaAlias == null) {
|
if (inner && ctx().getParent() != null && ctx().schemaAlias == null) {
|
||||||
setCandidate(getFieldType(path.last()), alias.text);
|
return getSubquery(alias.text, path, exp);
|
||||||
|
|
||||||
Path subpath = factory.newPath(ctx().subquery);
|
|
||||||
subpath.setMetaData(ctx().subquery.getMetaData());
|
|
||||||
exp = bindVariableForKeyPath(path, alias.text, exp);
|
|
||||||
exp = and(exp, factory.equal(path, subpath));
|
|
||||||
return exp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return addJoin(path, alias, exp);
|
return addJoin(path, alias, exp);
|
||||||
|
@ -666,23 +691,13 @@ public class JPQLExpressionBuilder
|
||||||
} else {
|
} else {
|
||||||
alias = right(node).text;
|
alias = right(node).text;
|
||||||
JPQLNode left = left(node);
|
JPQLNode left = left(node);
|
||||||
|
addSchemaToContext(alias, cmd);
|
||||||
|
|
||||||
// check to see if the we are referring to a path in the from
|
// check to see if the we are referring to a path in the from
|
||||||
// clause, since we might be in a subquery against a collection
|
// clause, since we might be in a subquery against a collection
|
||||||
if (isPath(left)) {
|
if (isPath(left)) {
|
||||||
Path path = getPath(left);
|
Path path = getPath(left);
|
||||||
FieldMetaData fmd = path.last();
|
return getSubquery(alias, path, exp);
|
||||||
ClassMetaData candidate = getFieldType(fmd);
|
|
||||||
|
|
||||||
if (candidate == null && fmd.isElementCollection())
|
|
||||||
candidate = fmd.getDefiningMetaData();
|
|
||||||
|
|
||||||
setCandidate(candidate, alias);
|
|
||||||
|
|
||||||
Path subpath = factory.newPath(ctx().subquery);
|
|
||||||
subpath.setMetaData(ctx().subquery.getMetaData());
|
|
||||||
exp = bindVariableForKeyPath(path, alias, exp);
|
|
||||||
return and(exp, factory.equal(path, subpath));
|
|
||||||
} else {
|
} else {
|
||||||
// we have an alias: bind it as a variable
|
// we have an alias: bind it as a variable
|
||||||
Value var = getVariable(alias, true);
|
Value var = getVariable(alias, true);
|
||||||
|
@ -764,8 +779,15 @@ public class JPQLExpressionBuilder
|
||||||
return super.getVariable(id.toLowerCase(), bind);
|
return super.getVariable(id.toLowerCase(), bind);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isSeendVariable(String id) {
|
protected Value getDefinedVariable(String id) {
|
||||||
return id != null && super.isSeenVariable(id.toLowerCase());
|
return ctx().getVariable(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isSeenVariable(String var) {
|
||||||
|
Context c = ctx().findContext(var);
|
||||||
|
if (c != null)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1181,7 +1203,11 @@ public class JPQLExpressionBuilder
|
||||||
return eval(onlyChild(node));
|
return eval(onlyChild(node));
|
||||||
|
|
||||||
case JJTCOUNT:
|
case JJTCOUNT:
|
||||||
return factory.count(getValue(lastChild(node)));
|
JPQLNode c = lastChild(node);
|
||||||
|
if (c.id == JJTIDENTIFIER)
|
||||||
|
// count(e)
|
||||||
|
return factory.count(getPath(node, false, true));
|
||||||
|
return factory.count(getValue(c));
|
||||||
|
|
||||||
case JJTMAX:
|
case JJTMAX:
|
||||||
return factory.max(getNumberValue(onlyChild(node)));
|
return factory.max(getNumberValue(onlyChild(node)));
|
||||||
|
@ -1392,12 +1418,22 @@ public class JPQLExpressionBuilder
|
||||||
|
|
||||||
// parse the subquery
|
// parse the subquery
|
||||||
ParsedJPQL parsed = new ParsedJPQL(node.parser.jpql, node);
|
ParsedJPQL parsed = new ParsedJPQL(node.parser.jpql, node);
|
||||||
|
Context parent = ctx();
|
||||||
|
Context subContext = new Context(parsed, null, ctx());
|
||||||
|
contexts.push(subContext);
|
||||||
|
subContext.setParent(parent);
|
||||||
|
|
||||||
ClassMetaData candidate = getCandidateMetaData(node);
|
ClassMetaData candidate = getCandidateMetaData(node);
|
||||||
Subquery subq = factory.newSubquery(candidate, subclasses, alias);
|
Subquery subq = subContext.getSubquery();
|
||||||
|
if (subq == null) {
|
||||||
|
subq = factory.newSubquery(candidate, subclasses, alias);
|
||||||
|
subContext.setSubquery(subq);
|
||||||
|
}
|
||||||
subq.setMetaData(candidate);
|
subq.setMetaData(candidate);
|
||||||
|
|
||||||
contexts.push(new Context(parsed, subq));
|
// evaluate from clause for resolving variables defined in subquery
|
||||||
|
JPQLNode from = node.getChild(1);
|
||||||
|
subContext.from = evalFromClause(from, true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
QueryExpressions subexp = getQueryExpressions();
|
QueryExpressions subexp = getQueryExpressions();
|
||||||
|
@ -1506,7 +1542,14 @@ public class JPQLExpressionBuilder
|
||||||
if (cmd != null) {
|
if (cmd != null) {
|
||||||
// handle the case where the class name is the alias
|
// handle the case where the class name is the alias
|
||||||
// for the candidate (we don't use variables for this)
|
// for the candidate (we don't use variables for this)
|
||||||
Value thiz = factory.getThis();
|
Value thiz = null;
|
||||||
|
if (ctx().subquery == null ||
|
||||||
|
ctx().getSchema(name.toLowerCase()) == null) {
|
||||||
|
thiz = factory.getThis();
|
||||||
|
} else {
|
||||||
|
thiz = factory.newPath(ctx().subquery);
|
||||||
|
}
|
||||||
|
((Path)thiz).setSchemaAlias(name);
|
||||||
thiz.setMetaData(cmd);
|
thiz.setMetaData(cmd);
|
||||||
return thiz;
|
return thiz;
|
||||||
} else if (val instanceof Path) {
|
} else if (val instanceof Path) {
|
||||||
|
@ -1734,6 +1777,7 @@ public class JPQLExpressionBuilder
|
||||||
if (ctx().subquery != null) {
|
if (ctx().subquery != null) {
|
||||||
path = factory.newPath(ctx().subquery);
|
path = factory.newPath(ctx().subquery);
|
||||||
path.setMetaData(ctx().subquery.getMetaData());
|
path.setMetaData(ctx().subquery.getMetaData());
|
||||||
|
factory.bindVariable(val, path);
|
||||||
} else {
|
} else {
|
||||||
path = factory.newPath();
|
path = factory.newPath();
|
||||||
path.setMetaData(ctx().meta);
|
path.setMetaData(ctx().meta);
|
||||||
|
@ -1748,6 +1792,8 @@ public class JPQLExpressionBuilder
|
||||||
throw parseException(EX_USER, "path-invalid",
|
throw parseException(EX_USER, "path-invalid",
|
||||||
new Object[]{ assemble(node), name }, null);
|
new Object[]{ assemble(node), name }, null);
|
||||||
|
|
||||||
|
path.setSchemaAlias(name);
|
||||||
|
|
||||||
// walk through the children and assemble the path
|
// walk through the children and assemble the path
|
||||||
boolean allowNull = !inner;
|
boolean allowNull = !inner;
|
||||||
for (int i = 1; i < node.children.length; i++) {
|
for (int i = 1; i < node.children.length; i++) {
|
||||||
|
@ -1758,6 +1804,9 @@ public class JPQLExpressionBuilder
|
||||||
}
|
}
|
||||||
path = (Path) traversePath(path, node.children[i].text, pcOnly,
|
path = (Path) traversePath(path, node.children[i].text, pcOnly,
|
||||||
allowNull);
|
allowNull);
|
||||||
|
if (ctx().getParent() != null && ctx().getVariable(path.getSchemaAlias()) == null) {
|
||||||
|
path.setSubqueryContext(ctx());
|
||||||
|
}
|
||||||
|
|
||||||
// all traversals but the first one will always be inner joins
|
// all traversals but the first one will always be inner joins
|
||||||
allowNull = false;
|
allowNull = false;
|
||||||
|
@ -1902,17 +1951,23 @@ public class JPQLExpressionBuilder
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Context {
|
protected void addSchemaToContext(String id, ClassMetaData meta) {
|
||||||
|
ctx().addSchema(id.toLowerCase(), meta);
|
||||||
private final ParsedJPQL parsed;
|
|
||||||
private ClassMetaData meta;
|
|
||||||
private String schemaAlias;
|
|
||||||
private Subquery subquery;
|
|
||||||
|
|
||||||
Context(ParsedJPQL parsed, Subquery subquery) {
|
|
||||||
this.parsed = parsed;
|
|
||||||
this.subquery = subquery;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void addVariableToContext(String id, Value var) {
|
||||||
|
ctx().addVariable(id, var);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Value getSeenVariable(String var) {
|
||||||
|
Context c = ctx();
|
||||||
|
Value v = c.getVariable(var);
|
||||||
|
if (v != null)
|
||||||
|
return v;
|
||||||
|
if (c.getParent() != null)
|
||||||
|
return c.getParent().findVariable(var);
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////
|
////////////////////////////
|
||||||
|
|
|
@ -24,6 +24,7 @@ import java.util.List;
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
import javax.persistence.Query;
|
import javax.persistence.Query;
|
||||||
|
|
||||||
|
import org.apache.openjpa.persistence.query.Customer.CreditRating;
|
||||||
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
|
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,12 +91,12 @@ public class TestSubquery
|
||||||
"(SELECT MAX(m3.datePublished) "+
|
"(SELECT MAX(m3.datePublished) "+
|
||||||
"FROM Magazine m3 "+
|
"FROM Magazine m3 "+
|
||||||
"WHERE m3.idPublisher.id = p.id)) ",
|
"WHERE m3.idPublisher.id = p.id)) ",
|
||||||
// outstanding problem subqueries:
|
"select o from Order o where o.amount > " +
|
||||||
// "select o from Order o where o.amount > (select count(o) from Order o)",
|
" (select count(o) from Order o)",
|
||||||
// "select o from Order o where o.amount > (select count(o2) from
|
"select o from Order o where o.amount > " +
|
||||||
// Order o2)",
|
"(select count(o2) from Order o2)",
|
||||||
// "select c from Customer c left join c.orders o where not exists"
|
"select c from Customer c left join c.orders o where not exists"
|
||||||
// + " (select o2 from c.orders o2 where o2 = o)",
|
+ " (select o2 from c.orders o2 where o2 = o)",
|
||||||
};
|
};
|
||||||
|
|
||||||
static String[] querys_jpa20 = new String[] {
|
static String[] querys_jpa20 = new String[] {
|
||||||
|
@ -206,4 +207,16 @@ public class TestSubquery
|
||||||
q.getResultList();
|
q.getResultList();
|
||||||
em.close();
|
em.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testUpdateWithCorrelatedSubquery() {
|
||||||
|
String update = "update Customer c set c.creditRating = ?1 where EXISTS" +
|
||||||
|
" (select o from in(c.orders) o)";
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
em.getTransaction().begin();
|
||||||
|
CreditRating creditRating = CreditRating.GOOD;
|
||||||
|
int updateCount = em.createQuery(update).
|
||||||
|
setParameter(1, creditRating).executeUpdate();
|
||||||
|
em.getTransaction().rollback();
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue