use of org.datanucleus.store.query.expression.DyadicExpression in project datanucleus-rdbms by datanucleus.
the class QueryToSQLMapper method processPrimaryExpression.
/* (non-Javadoc)
* @see org.datanucleus.query.evaluator.AbstractExpressionEvaluator#processPrimaryExpression(org.datanucleus.query.expression.PrimaryExpression)
*/
protected Object processPrimaryExpression(PrimaryExpression expr) {
SQLExpression sqlExpr = null;
if (expr.getLeft() != null) {
if (expr.getLeft() instanceof DyadicExpression && expr.getLeft().getOperator() == Expression.OP_CAST) {
String exprCastName = null;
if (expr.getLeft().getLeft() instanceof PrimaryExpression) {
exprCastName = "CAST_" + ((PrimaryExpression) expr.getLeft().getLeft()).getId();
} else if (expr.getLeft().getLeft() instanceof VariableExpression) {
exprCastName = "CAST_" + ((VariableExpression) expr.getLeft().getLeft()).getId();
} else if (expr.getLeft().getLeft() instanceof InvokeExpression) {
exprCastName = "CAST_" + expr.getLeft().getLeft();
} else {
throw new NucleusException("Don't currently support cast of " + expr.getLeft().getLeft());
}
expr.getLeft().getLeft().evaluate(this);
sqlExpr = stack.pop();
JavaTypeMapping mapping = sqlExpr.getJavaTypeMapping();
if (mapping instanceof EmbeddedMapping) {
// Cast of an embedded field, so use same table
// Extract what we are casting it to
Literal castLitExpr = (Literal) expr.getLeft().getRight();
Class castType = resolveClass((String) castLitExpr.getLiteral());
AbstractClassMetaData castCmd = ec.getMetaDataManager().getMetaDataForClass(castType, clr);
JavaTypeMapping discMapping = ((EmbeddedMapping) mapping).getDiscriminatorMapping();
if (discMapping != null) {
// Should have a discriminator always when casting this
SQLExpression discExpr = exprFactory.newExpression(stmt, sqlExpr.getSQLTable(), discMapping);
Object discVal = castCmd.getDiscriminatorValue();
SQLExpression discValExpr = exprFactory.newLiteral(stmt, discMapping, discVal);
BooleanExpression discRestrictExpr = discExpr.eq(discValExpr);
Iterator<String> subclassIter = storeMgr.getSubClassesForClass(castType.getName(), true, clr).iterator();
while (subclassIter.hasNext()) {
String subclassName = subclassIter.next();
AbstractClassMetaData subtypeCmd = storeMgr.getMetaDataManager().getMetaDataForClass(subclassName, clr);
discVal = subtypeCmd.getDiscriminatorValue();
discValExpr = exprFactory.newLiteral(stmt, discMapping, discVal);
BooleanExpression subtypeExpr = discExpr.eq(discValExpr);
discRestrictExpr = discRestrictExpr.ior(subtypeExpr);
}
stmt.whereAnd(discRestrictExpr, true);
}
SQLTableMapping tblMapping = new SQLTableMapping(sqlExpr.getSQLTable(), castCmd, sqlExpr.getJavaTypeMapping());
setSQLTableMappingForAlias(exprCastName, tblMapping);
SQLTableMapping sqlMapping = getSQLTableMappingForPrimaryExpression(stmt, exprCastName, expr, Boolean.FALSE);
if (sqlMapping == null) {
throw new NucleusException("PrimaryExpression " + expr + " is not yet supported");
}
sqlExpr = exprFactory.newExpression(stmt, sqlMapping.table, sqlMapping.mapping);
stack.push(sqlExpr);
return sqlExpr;
}
// Evaluate the cast
expr.getLeft().evaluate(this);
sqlExpr = stack.pop();
// Extract what we are casting it to
Literal castLitExpr = (Literal) expr.getLeft().getRight();
AbstractClassMetaData castCmd = ec.getMetaDataManager().getMetaDataForClass(resolveClass((String) castLitExpr.getLiteral()), clr);
SQLTableMapping tblMapping = new SQLTableMapping(sqlExpr.getSQLTable(), castCmd, sqlExpr.getJavaTypeMapping());
setSQLTableMappingForAlias(exprCastName, tblMapping);
SQLTableMapping sqlMapping = getSQLTableMappingForPrimaryExpression(stmt, exprCastName, expr, Boolean.FALSE);
if (sqlMapping == null) {
throw new NucleusException("PrimaryExpression " + expr + " is not yet supported");
}
sqlExpr = exprFactory.newExpression(stmt, sqlMapping.table, sqlMapping.mapping);
stack.push(sqlExpr);
return sqlExpr;
} else if (expr.getLeft() instanceof ParameterExpression) {
// "{paramExpr}.field[.field[.field]]"
// Need parameter values to process this
setNotPrecompilable();
ParameterExpression paramExpr = (ParameterExpression) expr.getLeft();
Symbol paramSym = compilation.getSymbolTable().getSymbol(paramExpr.getId());
if (paramSym.getValueType() != null && paramSym.getValueType().isArray()) {
// Special case : array "methods" (particularly "length")
String first = expr.getTuples().get(0);
processParameterExpression(paramExpr, true);
SQLExpression paramSqlExpr = stack.pop();
sqlExpr = exprFactory.invokeMethod(stmt, "ARRAY", first, paramSqlExpr, null);
stack.push(sqlExpr);
return sqlExpr;
}
// Create Literal for the parameter (since we need to perform operations on it)
processParameterExpression(paramExpr, true);
SQLExpression paramSqlExpr = stack.pop();
SQLLiteral lit = (SQLLiteral) paramSqlExpr;
Object paramValue = lit.getValue();
List<String> tuples = expr.getTuples();
Iterator<String> tuplesIter = tuples.iterator();
Object objValue = paramValue;
while (tuplesIter.hasNext()) {
String fieldName = tuplesIter.next();
if (objValue == null) {
NucleusLogger.QUERY.warn(">> Compilation of " + expr + " : need to direct through field \"" + fieldName + "\" on null value, hence not compilable!");
// Null value, and we have further path to navigate TODO Handle this "NPE"
break;
}
objValue = getValueForObjectField(objValue, fieldName);
// Using literal value of parameter, so cannot precompile it
setNotPrecompilable();
}
if (objValue == null) {
sqlExpr = exprFactory.newLiteral(stmt, null, null);
stack.push(sqlExpr);
return sqlExpr;
}
JavaTypeMapping m = exprFactory.getMappingForType(objValue.getClass(), false);
sqlExpr = exprFactory.newLiteral(stmt, m, objValue);
stack.push(sqlExpr);
return sqlExpr;
} else if (expr.getLeft() instanceof VariableExpression) {
// "{varExpr}.field[.field[.field]]"
VariableExpression varExpr = (VariableExpression) expr.getLeft();
processVariableExpression(varExpr);
SQLExpression varSqlExpr = stack.pop();
if (varSqlExpr instanceof UnboundExpression) {
// Bind as CROSS JOIN for now
processUnboundExpression((UnboundExpression) varSqlExpr);
varSqlExpr = stack.pop();
}
Class varType = clr.classForName(varSqlExpr.getJavaTypeMapping().getType());
if (varSqlExpr.getSQLStatement() == stmt.getParentStatement()) {
// Use parent mapper to get the mapping for this field since it has the table
SQLTableMapping sqlMapping = parentMapper.getSQLTableMappingForPrimaryExpression(stmt, null, expr, Boolean.FALSE);
if (sqlMapping == null) {
throw new NucleusException("PrimaryExpression " + expr.getId() + " is not yet supported");
}
// TODO Cater for the table required to join to not being the primary table of the outer query
// This should check on
// getDatastoreAdapter().supportsOption(RDBMSAdapter.ACCESS_PARENTQUERY_IN_SUBQUERY))
sqlExpr = exprFactory.newExpression(varSqlExpr.getSQLStatement(), sqlMapping.table, sqlMapping.mapping);
stack.push(sqlExpr);
return sqlExpr;
}
SQLTableMapping varTblMapping = getSQLTableMappingForAlias(varExpr.getId());
if (varTblMapping == null) {
throw new NucleusUserException("Variable " + varExpr.getId() + " is not yet bound, so cannot get field " + expr.getId());
}
if (varTblMapping.cmd == null) {
throw new NucleusUserException("Variable " + varExpr.getId() + " of type " + varType.getName() + " cannot evaluate " + expr.getId());
}
SQLTableMapping sqlMapping = getSQLTableMappingForPrimaryExpression(varSqlExpr.getSQLStatement(), varExpr.getId(), expr, Boolean.FALSE);
sqlExpr = exprFactory.newExpression(sqlMapping.table.getSQLStatement(), sqlMapping.table, sqlMapping.mapping);
stack.push(sqlExpr);
return sqlExpr;
} else if (expr.getLeft() instanceof InvokeExpression) {
InvokeExpression invokeExpr = (InvokeExpression) expr.getLeft();
SQLExpression invokedSqlExpr = getInvokedSqlExpressionForInvokeExpression(invokeExpr);
processInvokeExpression(invokeExpr, invokedSqlExpr);
SQLExpression invokeSqlExpr = stack.pop();
Table tbl = invokeSqlExpr.getSQLTable().getTable();
if (expr.getTuples().size() > 1) {
throw new NucleusUserException("Dont currently support evaluating " + expr.getId() + " on " + invokeSqlExpr);
}
SQLTable invokeSqlTbl = invokeSqlExpr.getSQLTable();
if (invokedSqlExpr.getJavaTypeMapping() instanceof OptionalMapping && invokeExpr.getOperation().equals("get") && expr.getTuples().size() == 1) {
OptionalMapping opMapping = (OptionalMapping) invokedSqlExpr.getJavaTypeMapping();
if (opMapping.getWrappedMapping() instanceof PersistableMapping) {
// Special case of Optional.get().{field}, so we need to join to the related table
AbstractMemberMetaData mmd = invokedSqlExpr.getJavaTypeMapping().getMemberMetaData();
AbstractClassMetaData otherCmd = ec.getMetaDataManager().getMetaDataForClass(mmd.getCollection().getElementType(), clr);
Table otherTbl = storeMgr.getDatastoreClass(otherCmd.getFullClassName(), clr);
// Optional type so do LEFT OUTER JOIN since if it is null then we would eliminate all other results
invokeSqlTbl = stmt.join(JoinType.LEFT_OUTER_JOIN, invokeSqlExpr.getSQLTable(), opMapping.getWrappedMapping(), otherTbl, null, otherTbl.getIdMapping(), null, null, true);
tbl = invokeSqlTbl.getTable();
}
}
if (tbl instanceof DatastoreClass) {
// Table of a class, so assume to have field in the table of the class
// TODO Allow joins to superclasses if required
JavaTypeMapping mapping = ((DatastoreClass) tbl).getMemberMapping(expr.getId());
if (mapping == null) {
throw new NucleusUserException("Dont currently support evaluating " + expr.getId() + " on " + invokeSqlExpr + ". The field " + expr.getId() + " doesnt exist in table " + tbl);
}
sqlExpr = exprFactory.newExpression(stmt, invokeSqlTbl, mapping);
stack.push(sqlExpr);
return sqlExpr;
} else if (tbl instanceof JoinTable) {
if (invokeSqlExpr.getJavaTypeMapping() instanceof EmbeddedMapping) {
// Table containing an embedded element/key/value so assume we have a column in the join table
EmbeddedMapping embMapping = (EmbeddedMapping) invokeSqlExpr.getJavaTypeMapping();
JavaTypeMapping mapping = embMapping.getJavaTypeMapping(expr.getId());
if (mapping == null) {
throw new NucleusUserException("Dont currently support evaluating " + expr.getId() + " on " + invokeSqlExpr + ". The field " + expr.getId() + " doesnt exist in table " + tbl);
}
sqlExpr = exprFactory.newExpression(stmt, invokeSqlTbl, mapping);
stack.push(sqlExpr);
return sqlExpr;
}
}
throw new NucleusUserException("Dont currently support evaluating " + expr.getId() + " on " + invokeSqlExpr + " with invoke having table of " + tbl);
} else {
throw new NucleusUserException("Dont currently support PrimaryExpression with 'left' of " + expr.getLeft());
}
}
// Real primary expression ("field.field", "alias.field.field" etc)
SQLTableMapping sqlMapping = getSQLTableMappingForPrimaryExpression(stmt, null, expr, null);
if (sqlMapping == null) {
throw new NucleusException("PrimaryExpression " + expr.getId() + " is not yet supported");
}
sqlExpr = exprFactory.newExpression(stmt, sqlMapping.table, sqlMapping.mapping);
if (sqlMapping.mmd != null && sqlExpr instanceof MapExpression) {
// This sqlMapping is for something joined in a FROM clause, so set the alias on the returned MapExpression to avoid doing the same joins
String alias = getAliasForSQLTableMapping(sqlMapping);
if (alias == null && parentMapper != null) {
alias = parentMapper.getAliasForSQLTableMapping(sqlMapping);
}
((MapExpression) sqlExpr).setAliasForMapTable(alias);
}
stack.push(sqlExpr);
return sqlExpr;
}
use of org.datanucleus.store.query.expression.DyadicExpression in project datanucleus-rdbms by datanucleus.
the class QueryToSQLMapper method getInvokedSqlExpressionForInvokeExpression.
protected SQLExpression getInvokedSqlExpressionForInvokeExpression(InvokeExpression expr) {
Expression invokedExpr = expr.getLeft();
SQLExpression invokedSqlExpr = null;
if (invokedExpr == null) {
// Static method
} else if (invokedExpr instanceof PrimaryExpression) {
processPrimaryExpression((PrimaryExpression) invokedExpr);
invokedSqlExpr = stack.pop();
} else if (invokedExpr instanceof Literal) {
processLiteral((Literal) invokedExpr);
invokedSqlExpr = stack.pop();
} else if (invokedExpr instanceof ParameterExpression) {
// TODO May be needed to set the second parameter to "false" here and then if the method
// being invoked needs the parameters as a normal SQLLiteral then allow it to convert it itself
processParameterExpression((ParameterExpression) invokedExpr, true);
invokedSqlExpr = stack.pop();
} else if (invokedExpr instanceof InvokeExpression) {
processInvokeExpression((InvokeExpression) invokedExpr);
invokedSqlExpr = stack.pop();
} else if (invokedExpr instanceof VariableExpression) {
processVariableExpression((VariableExpression) invokedExpr);
invokedSqlExpr = stack.pop();
} else if (invokedExpr instanceof ArrayExpression) {
ArrayExpression arrExpr = (ArrayExpression) invokedExpr;
SQLExpression[] arrSqlExprs = new SQLExpression[arrExpr.getArraySize()];
for (int i = 0; i < arrExpr.getArraySize(); i++) {
Expression arrElemExpr = arrExpr.getElement(i);
arrElemExpr.evaluate(this);
arrSqlExprs[i] = stack.pop();
}
JavaTypeMapping m = exprFactory.getMappingForType(Object[].class, false);
invokedSqlExpr = new org.datanucleus.store.rdbms.sql.expression.ArrayExpression(stmt, m, arrSqlExprs);
} else if (invokedExpr instanceof DyadicExpression) {
DyadicExpression dyExpr = (DyadicExpression) invokedExpr;
dyExpr.evaluate(this);
invokedSqlExpr = stack.pop();
} else {
throw new NucleusException("Dont currently support invoke expression " + invokedExpr);
}
return invokedSqlExpr;
}
use of org.datanucleus.store.query.expression.DyadicExpression in project datanucleus-rdbms by datanucleus.
the class QueryToSQLMapper method compileUpdate.
/**
* Method to compile the result clause of the query into the SQLStatement.
* @param stmt UPDATE statement
*/
protected void compileUpdate(UpdateStatement stmt) {
if (compilation.getExprUpdate() != null) {
// Update statement, so generate update expression(s)
compileComponent = CompilationComponent.UPDATE;
Expression[] updateExprs = compilation.getExprUpdate();
SQLExpression[] updateSqlExprs = new SQLExpression[updateExprs.length];
// TODO If the field being set is in a different table omit it
boolean performingUpdate = false;
for (int i = 0; i < updateExprs.length; i++) {
// "field = value"
DyadicExpression updateExpr = (DyadicExpression) updateExprs[i];
// Left-side has to be PrimaryExpression
SQLExpression leftSqlExpr = null;
if (updateExpr.getLeft() instanceof PrimaryExpression) {
processPrimaryExpression((PrimaryExpression) updateExpr.getLeft());
leftSqlExpr = stack.pop();
if (leftSqlExpr.getSQLTable() != stmt.getPrimaryTable()) {
// Set left to null to signify that it is not applicable to the table of this UPDATE statement
leftSqlExpr = null;
}
} else {
throw new NucleusException("Dont currently support update clause containing left expression of type " + updateExpr.getLeft());
}
if (leftSqlExpr != null) {
if (!stmt.getDatastoreAdapter().supportsOption(DatastoreAdapter.UPDATE_STATEMENT_ALLOW_TABLE_ALIAS_IN_SET_CLAUSE)) {
// This datastore doesn't allow table alias in UPDATE SET clause, so just use column name
for (int j = 0; j < leftSqlExpr.getNumberOfSubExpressions(); j++) {
ColumnExpression colExpr = leftSqlExpr.getSubExpression(j);
colExpr.setOmitTableFromString(true);
}
}
performingUpdate = true;
SQLExpression rightSqlExpr = null;
if (updateExpr.getRight() instanceof Literal) {
processLiteral((Literal) updateExpr.getRight());
rightSqlExpr = stack.pop();
} else if (updateExpr.getRight() instanceof ParameterExpression) {
ParameterExpression paramExpr = (ParameterExpression) updateExpr.getRight();
paramMappingForName.put(paramExpr.getId(), leftSqlExpr.getJavaTypeMapping());
processParameterExpression(paramExpr);
rightSqlExpr = stack.pop();
} else if (updateExpr.getRight() instanceof PrimaryExpression) {
processPrimaryExpression((PrimaryExpression) updateExpr.getRight());
rightSqlExpr = stack.pop();
} else if (updateExpr.getRight() instanceof DyadicExpression) {
updateExpr.getRight().evaluate(this);
rightSqlExpr = stack.pop();
} else if (updateExpr.getRight() instanceof CaseExpression) {
CaseExpression caseExpr = (CaseExpression) updateExpr.getRight();
processCaseExpression(caseExpr, leftSqlExpr);
rightSqlExpr = stack.pop();
} else if (updateExpr.getRight() instanceof VariableExpression) {
// Subquery?
processVariableExpression((VariableExpression) updateExpr.getRight());
rightSqlExpr = stack.pop();
if (rightSqlExpr instanceof UnboundExpression) {
// TODO Support whatever this is
throw new NucleusException("Found UnboundExpression in UPDATE clause!");
}
} else {
throw new NucleusException("Dont currently support update clause containing right expression of type " + updateExpr.getRight());
}
if (rightSqlExpr != null) {
updateSqlExprs[i] = leftSqlExpr.eq(rightSqlExpr);
}
}
}
if (candidateCmd.isVersioned() && options.contains(OPTION_BULK_UPDATE_VERSION)) {
SQLExpression updateSqlExpr = null;
ClassTable table = (ClassTable) stmt.getPrimaryTable().getTable();
JavaTypeMapping verMapping = table.getSurrogateMapping(SurrogateColumnType.VERSION, true);
ClassTable verTable = table.getTableManagingMapping(verMapping);
if (verTable == stmt.getPrimaryTable().getTable()) {
VersionMetaData vermd = candidateCmd.getVersionMetaDataForClass();
if (vermd.getStrategy() == VersionStrategy.VERSION_NUMBER) {
// Increment the version
SQLTable verSqlTbl = stmt.getTable(verTable, stmt.getPrimaryTable().getGroupName());
SQLExpression verExpr = new NumericExpression(stmt, verSqlTbl, verMapping);
SQLExpression incrExpr = verExpr.add(new IntegerLiteral(stmt, exprFactory.getMappingForType(Integer.class, false), Integer.valueOf(1), null));
updateSqlExpr = verExpr.eq(incrExpr);
SQLExpression[] oldArray = updateSqlExprs;
updateSqlExprs = new SQLExpression[oldArray.length + 1];
System.arraycopy(oldArray, 0, updateSqlExprs, 0, oldArray.length);
updateSqlExprs[oldArray.length] = updateSqlExpr;
performingUpdate = true;
} else if (vermd.getStrategy() == VersionStrategy.DATE_TIME) {
// Set version to the time of update
SQLTable verSqlTbl = stmt.getTable(verTable, stmt.getPrimaryTable().getGroupName());
SQLExpression verExpr = new NumericExpression(stmt, verSqlTbl, verMapping);
Object newVersion = ec.getLockManager().getNextVersion(vermd, null);
JavaTypeMapping valMapping = exprFactory.getMappingForType(newVersion.getClass(), false);
SQLExpression valExpr = new TemporalLiteral(stmt, valMapping, newVersion, null);
updateSqlExpr = verExpr.eq(valExpr);
SQLExpression[] oldArray = updateSqlExprs;
updateSqlExprs = new SQLExpression[oldArray.length + 1];
System.arraycopy(oldArray, 0, updateSqlExprs, 0, oldArray.length);
updateSqlExprs[oldArray.length] = updateSqlExpr;
performingUpdate = true;
}
}
}
if (performingUpdate) {
// Only set the updates component of the SQLStatement if anything to update in this table
stmt.setUpdates(updateSqlExprs);
}
}
compileComponent = null;
}
use of org.datanucleus.store.query.expression.DyadicExpression in project datanucleus-rdbms by datanucleus.
the class QueryToSQLMapper method compileFromClassExpression.
/**
* Method to take a ClassExpression (in a FROM clause) and process the candidate and any
* linked JoinExpression(s), adding joins to the SQLStatement as required.
* @param clsExpr The ClassExpression
*/
protected void compileFromClassExpression(ClassExpression clsExpr) {
Symbol clsExprSym = clsExpr.getSymbol();
Class baseCls = (clsExprSym != null ? clsExprSym.getValueType() : null);
SQLTable candSqlTbl = stmt.getPrimaryTable();
MetaDataManager mmgr = storeMgr.getMetaDataManager();
AbstractClassMetaData cmd = mmgr.getMetaDataForClass(baseCls, clr);
if (baseCls != null && !candidateAlias.equals(clsExpr.getAlias())) {
// Not candidate class so must be cross join (JPA spec 4.4.5)
DatastoreClass candTbl = storeMgr.getDatastoreClass(baseCls.getName(), clr);
candSqlTbl = stmt.join(JoinType.CROSS_JOIN, null, null, null, candTbl, clsExpr.getAlias(), null, null, null, null, true, null);
SQLTableMapping tblMapping = new SQLTableMapping(candSqlTbl, cmd, candTbl.getIdMapping());
setSQLTableMappingForAlias(clsExpr.getAlias(), tblMapping);
}
if (clsExpr.getCandidateExpression() != null && parentMapper != null) {
// User defined the candidate of the subquery as an implied join to the outer query
// e.g SELECT c FROM Customer c WHERE EXISTS (SELECT o FROM c.orders o ...)
// so add the join(s) to the outer query
processFromClauseSubquery(clsExpr, candSqlTbl, mmgr);
}
// Process all linked JoinExpression(s) for this ClassExpression
Expression rightExpr = clsExpr.getRight();
SQLTable sqlTbl = candSqlTbl;
JavaTypeMapping previousMapping = null;
while (rightExpr != null) {
if (rightExpr instanceof JoinExpression) {
JoinExpression joinExpr = (JoinExpression) rightExpr;
JoinExpression.JoinType exprJoinType = joinExpr.getType();
JoinType joinType = org.datanucleus.store.rdbms.sql.SQLJoin.getJoinTypeForJoinExpressionType(exprJoinType);
Expression joinedExpr = joinExpr.getJoinedExpression();
Expression joinOnExpr = joinExpr.getOnExpression();
String joinAlias = joinExpr.getAlias();
PrimaryExpression joinPrimExpr = null;
Class castCls = null;
if (joinedExpr instanceof PrimaryExpression) {
joinPrimExpr = (PrimaryExpression) joinedExpr;
} else if (joinedExpr instanceof DyadicExpression && joinedExpr.getOperator() == Expression.OP_CAST) {
// TREAT this join as a particular type. Cast type is processed below where we add the joins
joinPrimExpr = (PrimaryExpression) joinedExpr.getLeft();
String castClassName = (String) ((Literal) joinedExpr.getRight()).getLiteral();
castCls = clr.classForName(castClassName);
} else {
throw new NucleusException("We do not currently support JOIN to " + joinedExpr);
}
Iterator<String> iter = joinPrimExpr.getTuples().iterator();
String rootId = iter.next();
if (joinPrimExpr.getTuples().size() == 1 && !rootId.endsWith("#KEY") && !rootId.endsWith("#VALUE")) {
// DN Extension : Join to (new) root element? We need an ON expression to be supplied in this case
if (joinOnExpr == null) {
throw new NucleusUserException("Query has join to " + joinPrimExpr.getId() + " yet this is a root component and there is no ON expression");
}
// Add the basic join first with no condition since this root will be referenced in the "on" condition
baseCls = resolveClass(joinPrimExpr.getId());
DatastoreClass baseTbl = storeMgr.getDatastoreClass(baseCls.getName(), clr);
sqlTbl = stmt.join(joinType, candSqlTbl, baseTbl, joinAlias, null, null, true);
cmd = mmgr.getMetaDataForClass(baseCls, clr);
SQLTableMapping tblMapping = new SQLTableMapping(sqlTbl, cmd, baseTbl.getIdMapping());
setSQLTableMappingForAlias(joinAlias, tblMapping);
// Convert the ON expression to a BooleanExpression and add to the join
processingOnClause = true;
joinOnExpr.evaluate(this);
BooleanExpression joinOnSqlExpr = (BooleanExpression) stack.pop();
processingOnClause = false;
stmt.addAndConditionToJoinForTable(sqlTbl, joinOnSqlExpr, true);
// Move on to next join in the chain
rightExpr = rightExpr.getRight();
continue;
}
String joinTableGroupName = null;
SQLTable tblMappingSqlTbl = null;
JavaTypeMapping tblIdMapping = null;
AbstractMemberMetaData tblMmd = null;
boolean mapKey = false;
boolean mapValue = false;
String rootComponent = rootId;
if (rootComponent.endsWith("#KEY")) {
mapKey = true;
rootComponent = rootComponent.substring(0, rootComponent.length() - 4);
} else if (rootComponent.endsWith("#VALUE")) {
mapValue = true;
rootComponent = rootComponent.substring(0, rootComponent.length() - 6);
}
if (rootComponent.equalsIgnoreCase(candidateAlias)) {
// Join relative to the candidate
// Name table group of joined-to as per the relation
// Note : this will only work for one level out from the candidate TODO Extend this
cmd = candidateCmd;
joinTableGroupName = joinPrimExpr.getId();
sqlTbl = candSqlTbl;
} else {
// Join relative to some other alias
SQLTableMapping sqlTblMapping = getSQLTableMappingForAlias(rootComponent);
if (sqlTblMapping != null) {
if (sqlTblMapping.mmd != null && (mapKey || mapValue)) {
// First component is Map-related (i.e m#KEY, m#VALUE), so add any necessary join(s)
MapMetaData mapmd = sqlTblMapping.mmd.getMap();
cmd = mapKey ? mapmd.getKeyClassMetaData(clr) : mapmd.getValueClassMetaData(clr);
// Find the table forming the Map. This may be a join table, or the key or value depending on the type
// TODO Use OPTION_CASE_INSENSITIVE
sqlTbl = stmt.getTable(rootComponent + "_MAP");
if (sqlTbl == null) {
sqlTbl = stmt.getTable((rootComponent + "_MAP").toUpperCase());
if (sqlTbl == null) {
sqlTbl = stmt.getTable((rootComponent + "_MAP").toLowerCase());
}
}
String aliasForJoin = (iter.hasNext()) ? null : joinAlias;
boolean embedded = mapKey ? (mapmd.isEmbeddedKey() || mapmd.isSerializedKey()) : (mapmd.isEmbeddedValue() || mapmd.isSerializedValue());
if (mapmd.getMapType() == MapType.MAP_TYPE_JOIN) {
// Join from join table to KEY/VALUE as required
if (!embedded) {
if (mapKey) {
DatastoreClass keyTable = storeMgr.getDatastoreClass(mapmd.getKeyType(), clr);
sqlTbl = stmt.join(joinType, sqlTbl, ((MapTable) sqlTbl.getTable()).getKeyMapping(), keyTable, aliasForJoin, keyTable.getIdMapping(), null, joinTableGroupName, true);
} else {
DatastoreClass valueTable = storeMgr.getDatastoreClass(mapmd.getValueType(), clr);
sqlTbl = stmt.join(joinType, sqlTbl, ((MapTable) sqlTbl.getTable()).getValueMapping(), valueTable, aliasForJoin, valueTable.getIdMapping(), null, joinTableGroupName, true);
}
tblMappingSqlTbl = sqlTbl;
tblIdMapping = tblMappingSqlTbl.getTable().getIdMapping();
}
} else if (mapmd.getMapType() == MapType.MAP_TYPE_KEY_IN_VALUE) {
// TODO Cater for this type
} else if (mapmd.getMapType() == MapType.MAP_TYPE_VALUE_IN_KEY) {
// TODO Cater for this type
}
} else {
cmd = sqlTblMapping.cmd;
sqlTbl = sqlTblMapping.table;
}
joinTableGroupName = sqlTbl.getGroupName() + joinPrimExpr.getId().substring(rootComponent.length());
} else {
throw new NucleusUserException("Query has " + joinPrimExpr.getId() + " yet the first component " + rootComponent + " is unknown!");
}
}
while (iter.hasNext()) {
String id = iter.next();
String[] ids = id.contains(".") ? StringUtils.split(id, ".") : new String[] { id };
for (int k = 0; k < ids.length; k++) {
if (cmd == null) {
throw new NucleusUserException("Error in JOIN clause. id=" + id + " but component prior to " + ids[k] + " has no metadata");
}
boolean lastComponent = (k == ids.length - 1);
String thisComponent = ids[k];
mapKey = false;
mapValue = false;
if (thisComponent.endsWith("#KEY")) {
thisComponent = thisComponent.substring(0, thisComponent.length() - 4);
mapKey = true;
} else if (thisComponent.endsWith("#VALUE")) {
thisComponent = thisComponent.substring(0, thisComponent.length() - 6);
mapValue = true;
}
AbstractMemberMetaData mmd = cmd.getMetaDataForMember(thisComponent);
if (mmd == null) {
if (exprJoinType == JoinExpression.JoinType.JOIN_LEFT_OUTER || exprJoinType == JoinExpression.JoinType.JOIN_LEFT_OUTER_FETCH) {
// Polymorphic join, where the field exists in a subclass (doable since we have outer join)
String[] subclasses = mmgr.getSubclassesForClass(cmd.getFullClassName(), true);
for (int l = 0; l < subclasses.length; l++) {
AbstractClassMetaData subCmd = mmgr.getMetaDataForClass(subclasses[l], clr);
if (subCmd != null) {
mmd = subCmd.getMetaDataForMember(thisComponent);
if (mmd != null) {
cmd = subCmd;
break;
}
}
}
}
if (mmd == null) {
throw new NucleusUserException("Query has " + joinPrimExpr.getId() + " yet " + thisComponent + " is not found. Fix your input");
}
}
tblMmd = null;
String aliasForJoin = null;
if (k == (ids.length - 1) && !iter.hasNext()) {
aliasForJoin = joinAlias;
}
RelationType relationType = mmd.getRelationType(clr);
DatastoreClass relTable = null;
AbstractMemberMetaData relMmd = null;
if (relationType != RelationType.NONE) {
if (JoinExpression.JoinType.isFetch(exprJoinType)) {
// Add field to FetchPlan since marked for FETCH
String fgName = "QUERY_FETCH_" + mmd.getFullFieldName();
FetchGroupManager fetchGrpMgr = storeMgr.getNucleusContext().getFetchGroupManager();
if (fetchGrpMgr.getFetchGroupsWithName(fgName) == null) {
FetchGroup grp = new FetchGroup(storeMgr.getNucleusContext(), fgName, clr.classForName(cmd.getFullClassName()));
grp.addMember(mmd.getName());
fetchGrpMgr.addFetchGroup(grp);
}
fetchPlan.addGroup(fgName);
}
}
if (relationType == RelationType.ONE_TO_ONE_UNI) {
JavaTypeMapping otherMapping = null;
Object[] castDiscrimValues = null;
if (castCls != null && lastComponent) {
cmd = mmgr.getMetaDataForClass(castCls, clr);
if (cmd.hasDiscriminatorStrategy()) {
// Restrict discriminator on cast type to be the type+subclasses
castDiscrimValues = getDiscriminatorValuesForCastClass(cmd);
}
} else {
cmd = mmgr.getMetaDataForClass(mmd.getType(), clr);
}
if (mmd.isEmbedded()) {
// Embedded into the same table as before, so no join needed
otherMapping = sqlTbl.getTable().getMemberMapping(mmd);
} else {
if (sqlTbl.getTable() instanceof CollectionTable) {
// Currently in a join table, so work from the element and this being an embedded member
CollectionTable collTbl = (CollectionTable) sqlTbl.getTable();
JavaTypeMapping elemMapping = collTbl.getElementMapping();
if (elemMapping instanceof EmbeddedMapping) {
otherMapping = ((EmbeddedMapping) elemMapping).getJavaTypeMapping(mmd.getName());
}
} else {
otherMapping = sqlTbl.getTable().getMemberMapping(mmd);
}
relTable = storeMgr.getDatastoreClass(mmd.getTypeName(), clr);
if (otherMapping == null && previousMapping != null) {
if (previousMapping instanceof EmbeddedMapping) {
// Part of an embedded 1-1 object, so find the relevant member mapping
EmbeddedMapping embMapping = (EmbeddedMapping) previousMapping;
otherMapping = embMapping.getJavaTypeMapping(mmd.getName());
}
}
if (otherMapping == null) {
// Polymorphic join? : cannot find this member in the candidate of the main statement, so need to pick which UNION
String tblGroupName = sqlTbl.getGroupName();
SQLTableGroup grp = stmt.getTableGroup(tblGroupName);
SQLTable nextSqlTbl = null;
// Try to find subtable in the same group that has a mapping for this member (and join from that)
SQLTable[] grpTbls = grp.getTables();
for (SQLTable grpTbl : grpTbls) {
if (grpTbl.getTable().getMemberMapping(mmd) != null) {
otherMapping = grpTbl.getTable().getMemberMapping(mmd);
break;
}
}
SQLTable newSqlTbl = stmt.join(joinType, sqlTbl, otherMapping, relTable, aliasForJoin, relTable.getIdMapping(), null, joinTableGroupName, false);
if (newSqlTbl != null) {
nextSqlTbl = newSqlTbl;
}
if (stmt instanceof SelectStatement) {
List<SelectStatement> unionStmts = ((SelectStatement) stmt).getUnions();
if (unionStmts != null) {
for (SQLStatement unionStmt : unionStmts) {
// Repeat the process for any unioned statements, find a subtable in the same group (and join from that)
otherMapping = null;
grp = unionStmt.getTableGroup(tblGroupName);
SQLTable[] unionGrpTbls = grp.getTables();
for (SQLTable grpTbl : unionGrpTbls) {
if (grpTbl.getTable().getMemberMapping(mmd) != null) {
otherMapping = grpTbl.getTable().getMemberMapping(mmd);
break;
}
}
newSqlTbl = unionStmt.join(joinType, sqlTbl, otherMapping, relTable, aliasForJoin, relTable.getIdMapping(), castDiscrimValues, joinTableGroupName, false);
if (newSqlTbl != null) {
nextSqlTbl = newSqlTbl;
}
}
}
}
sqlTbl = nextSqlTbl;
} else {
sqlTbl = stmt.join(joinType, sqlTbl, otherMapping, relTable, aliasForJoin, relTable.getIdMapping(), castDiscrimValues, joinTableGroupName, true);
}
}
previousMapping = otherMapping;
tblIdMapping = sqlTbl.getTable().getIdMapping();
tblMappingSqlTbl = sqlTbl;
} else if (relationType == RelationType.ONE_TO_ONE_BI) {
JavaTypeMapping otherMapping = null;
Object[] castDiscrimValues = null;
if (castCls != null && lastComponent) {
cmd = mmgr.getMetaDataForClass(castCls, clr);
if (cmd.hasDiscriminatorStrategy()) {
// Restrict discriminator on cast type to be the type+subclasses
castDiscrimValues = getDiscriminatorValuesForCastClass(cmd);
}
} else {
cmd = mmgr.getMetaDataForClass(mmd.getType(), clr);
}
if (mmd.isEmbedded()) {
// Embedded into the same table as before, so no join needed
otherMapping = sqlTbl.getTable().getMemberMapping(mmd);
} else {
relTable = storeMgr.getDatastoreClass(mmd.getTypeName(), clr);
if (mmd.getMappedBy() != null) {
relMmd = mmd.getRelatedMemberMetaData(clr)[0];
JavaTypeMapping relMapping = relTable.getMemberMapping(relMmd);
sqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), relTable, aliasForJoin, relMapping, castDiscrimValues, joinTableGroupName, true);
} else {
if (sqlTbl.getTable() instanceof CollectionTable) {
// Currently in a join table, so work from the element and this being an embedded member
CollectionTable collTbl = (CollectionTable) sqlTbl.getTable();
JavaTypeMapping elemMapping = collTbl.getElementMapping();
if (elemMapping instanceof EmbeddedMapping) {
otherMapping = ((EmbeddedMapping) elemMapping).getJavaTypeMapping(mmd.getName());
}
} else {
otherMapping = sqlTbl.getTable().getMemberMapping(mmd);
}
if (otherMapping == null && previousMapping != null) {
if (previousMapping instanceof EmbeddedMapping) {
// Part of an embedded 1-1 object, so find the relevant member mapping
EmbeddedMapping embMapping = (EmbeddedMapping) previousMapping;
otherMapping = embMapping.getJavaTypeMapping(mmd.getName());
}
}
sqlTbl = stmt.join(joinType, sqlTbl, otherMapping, relTable, aliasForJoin, relTable.getIdMapping(), castDiscrimValues, joinTableGroupName, true);
}
}
previousMapping = otherMapping;
tblIdMapping = sqlTbl.getTable().getIdMapping();
tblMappingSqlTbl = sqlTbl;
} else if (relationType == RelationType.ONE_TO_MANY_BI) {
previousMapping = null;
if (mmd.hasCollection()) {
// Join across COLLECTION relation
cmd = mmd.getCollection().getElementClassMetaData(clr);
if (mmd.getCollection().isEmbeddedElement() && mmd.getJoinMetaData() != null) {
// Embedded element stored in (collection) join table
CollectionTable relEmbTable = (CollectionTable) storeMgr.getTable(mmd);
JavaTypeMapping relOwnerMapping = relEmbTable.getOwnerMapping();
sqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), relEmbTable, aliasForJoin, relOwnerMapping, null, joinTableGroupName, true);
tblMappingSqlTbl = sqlTbl;
tblIdMapping = relEmbTable.getElementMapping();
} else {
relTable = storeMgr.getDatastoreClass(mmd.getCollection().getElementType(), clr);
relMmd = mmd.getRelatedMemberMetaData(clr)[0];
if (mmd.getJoinMetaData() != null || relMmd.getJoinMetaData() != null) {
// Join to join table, then to related table
ElementContainerTable joinTbl = (ElementContainerTable) storeMgr.getTable(mmd);
SQLTable joinSqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), joinTbl, null, joinTbl.getOwnerMapping(), null, null, true);
sqlTbl = stmt.join(joinType, joinSqlTbl, joinTbl.getElementMapping(), relTable, aliasForJoin, relTable.getIdMapping(), null, joinTableGroupName, true);
} else {
// Join to related table FK
sqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), relTable, aliasForJoin, relTable.getMemberMapping(relMmd), null, joinTableGroupName, true);
}
tblIdMapping = sqlTbl.getTable().getIdMapping();
tblMappingSqlTbl = sqlTbl;
}
} else if (mmd.hasMap()) {
// Join across MAP relation
MapMetaData mapmd = mmd.getMap();
cmd = mapmd.getValueClassMetaData(clr);
tblMmd = mmd;
boolean embedded = mapKey ? (mapmd.isEmbeddedKey() || mapmd.isSerializedKey()) : (mapmd.isEmbeddedValue() || mapmd.isSerializedValue());
if (mapmd.getMapType() == MapType.MAP_TYPE_JOIN) {
// Add join to join table, then to related table (value)
MapTable joinTbl = (MapTable) storeMgr.getTable(mmd);
String aliasForMap = embedded ? aliasForJoin : (aliasForJoin + "_MAP");
sqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), joinTbl, aliasForMap, joinTbl.getOwnerMapping(), null, null, true);
if (embedded) {
tblMappingSqlTbl = sqlTbl;
tblIdMapping = mapKey ? joinTbl.getKeyMapping() : joinTbl.getValueMapping();
} else {
if (mapKey) {
// Join to key table and use that
relTable = storeMgr.getDatastoreClass(mapmd.getKeyType(), clr);
sqlTbl = stmt.join(joinType, sqlTbl, joinTbl.getKeyMapping(), relTable, aliasForJoin, relTable.getIdMapping(), null, joinTableGroupName, true);
// TODO if there is an ON clause it needs to go on the correct join See [rdbms-177]
} else {
// Join to value table and use that
relTable = storeMgr.getDatastoreClass(mapmd.getValueType(), clr);
sqlTbl = stmt.join(joinType, sqlTbl, joinTbl.getValueMapping(), relTable, aliasForJoin, relTable.getIdMapping(), null, joinTableGroupName, true);
// TODO if there is an ON clause it needs to go on the correct join See [rdbms-177]
}
tblMappingSqlTbl = sqlTbl;
tblIdMapping = tblMappingSqlTbl.getTable().getIdMapping();
}
} else if (mapmd.getMapType() == MapType.MAP_TYPE_KEY_IN_VALUE) {
// Join to value table
DatastoreClass valTable = storeMgr.getDatastoreClass(mapmd.getValueType(), clr);
JavaTypeMapping mapTblOwnerMapping;
if (mmd.getMappedBy() != null) {
mapTblOwnerMapping = valTable.getMemberMapping(mapmd.getValueClassMetaData(clr).getMetaDataForMember(mmd.getMappedBy()));
} else {
mapTblOwnerMapping = valTable.getExternalMapping(mmd, MappingType.EXTERNAL_FK);
}
String aliasForMap = (embedded || !mapKey) ? aliasForJoin : (aliasForJoin + "_MAP");
sqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), valTable, aliasForMap, mapTblOwnerMapping, null, null, true);
if (!embedded) {
if (mapKey) {
// Join to key table
JavaTypeMapping keyMapping = valTable.getMemberMapping(mmd.getKeyMetaData().getMappedBy());
relTable = storeMgr.getDatastoreClass(mapmd.getKeyType(), clr);
sqlTbl = stmt.join(joinType, sqlTbl, keyMapping, relTable, aliasForJoin, relTable.getIdMapping(), null, joinTableGroupName, true);
// TODO if there is an ON clause it needs to go on the correct join See [rdbms-177]
}
}
tblMappingSqlTbl = sqlTbl;
tblIdMapping = tblMappingSqlTbl.getTable().getIdMapping();
} else if (mapmd.getMapType() == MapType.MAP_TYPE_VALUE_IN_KEY) {
// Join to key table, and then to value table
DatastoreClass keyTable = storeMgr.getDatastoreClass(mapmd.getKeyType(), clr);
JavaTypeMapping mapTblOwnerMapping;
if (mmd.getMappedBy() != null) {
mapTblOwnerMapping = keyTable.getMemberMapping(mapmd.getKeyClassMetaData(clr).getMetaDataForMember(mmd.getMappedBy()));
} else {
mapTblOwnerMapping = keyTable.getExternalMapping(mmd, MappingType.EXTERNAL_FK);
}
String aliasForMap = (embedded || mapKey) ? aliasForJoin : (aliasForJoin + "_MAP");
sqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), keyTable, aliasForMap, mapTblOwnerMapping, null, null, true);
if (!embedded) {
if (!mapKey) {
// Join to value table
JavaTypeMapping valueMapping = keyTable.getMemberMapping(mmd.getValueMetaData().getMappedBy());
relTable = storeMgr.getDatastoreClass(mapmd.getValueType(), clr);
sqlTbl = stmt.join(joinType, sqlTbl, valueMapping, relTable, aliasForJoin, relTable.getIdMapping(), null, joinTableGroupName, true);
// TODO if there is an ON clause it needs to go on the correct join See [rdbms-177]
}
}
tblMappingSqlTbl = sqlTbl;
tblIdMapping = tblMappingSqlTbl.getTable().getIdMapping();
}
} else if (mmd.hasArray()) {
// Join across ARRAY relation
cmd = mmd.getArray().getElementClassMetaData(clr);
if (mmd.getArray().isEmbeddedElement() && mmd.getJoinMetaData() != null) {
// Embedded element stored in (array) join table
ArrayTable relEmbTable = (ArrayTable) storeMgr.getTable(mmd);
JavaTypeMapping relOwnerMapping = relEmbTable.getOwnerMapping();
sqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), relEmbTable, aliasForJoin, relOwnerMapping, null, joinTableGroupName, true);
tblMappingSqlTbl = sqlTbl;
tblIdMapping = relEmbTable.getElementMapping();
} else {
relTable = storeMgr.getDatastoreClass(mmd.getArray().getElementType(), clr);
relMmd = mmd.getRelatedMemberMetaData(clr)[0];
if (mmd.getJoinMetaData() != null || relMmd.getJoinMetaData() != null) {
// Join to join table, then to related table
ElementContainerTable joinTbl = (ElementContainerTable) storeMgr.getTable(mmd);
SQLTable joinSqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), joinTbl, null, joinTbl.getOwnerMapping(), null, null, true);
sqlTbl = stmt.join(joinType, joinSqlTbl, joinTbl.getElementMapping(), relTable, aliasForJoin, relTable.getIdMapping(), null, joinTableGroupName, true);
// TODO if there is an ON clause it needs to go on the correct join See [rdbms-177]
} else {
// Join to related table FK
sqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), relTable, aliasForJoin, relTable.getMemberMapping(relMmd), null, joinTableGroupName, true);
}
tblIdMapping = sqlTbl.getTable().getIdMapping();
tblMappingSqlTbl = sqlTbl;
}
}
} else if (relationType == RelationType.ONE_TO_MANY_UNI) {
previousMapping = null;
if (mmd.hasCollection()) {
// Join across COLLECTION relation
cmd = mmd.getCollection().getElementClassMetaData(clr);
if (mmd.getCollection().isEmbeddedElement() && mmd.getJoinMetaData() != null) {
// Embedded element stored in (collection) join table
CollectionTable relEmbTable = (CollectionTable) storeMgr.getTable(mmd);
JavaTypeMapping relOwnerMapping = relEmbTable.getOwnerMapping();
sqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), relEmbTable, aliasForJoin, relOwnerMapping, null, joinTableGroupName, true);
tblMappingSqlTbl = sqlTbl;
tblIdMapping = relEmbTable.getElementMapping();
} else {
relTable = storeMgr.getDatastoreClass(mmd.getCollection().getElementType(), clr);
if (mmd.getJoinMetaData() != null) {
// Join to join table, then to related table
ElementContainerTable joinTbl = (ElementContainerTable) storeMgr.getTable(mmd);
SQLTable joinSqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), joinTbl, null, joinTbl.getOwnerMapping(), null, null, true);
sqlTbl = stmt.join(joinType, joinSqlTbl, joinTbl.getElementMapping(), relTable, aliasForJoin, relTable.getIdMapping(), null, joinTableGroupName, true);
// TODO if there is an ON clause it needs to go on the correct join See [rdbms-177]
} else {
// Join to related table FK
JavaTypeMapping relMapping = relTable.getExternalMapping(mmd, MappingType.EXTERNAL_FK);
sqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), relTable, aliasForJoin, relMapping, null, joinTableGroupName, true);
}
tblMappingSqlTbl = sqlTbl;
tblIdMapping = tblMappingSqlTbl.getTable().getIdMapping();
}
} else if (mmd.hasMap()) {
// Join across MAP relation
MapMetaData mapmd = mmd.getMap();
cmd = mapmd.getValueClassMetaData(clr);
tblMmd = mmd;
boolean embedded = mapKey ? (mapmd.isEmbeddedKey() || mapmd.isSerializedKey()) : (mapmd.isEmbeddedValue() || mapmd.isSerializedValue());
if (mapmd.getMapType() == MapType.MAP_TYPE_JOIN) {
// Add join to join table, then to related table (value)
MapTable joinTbl = (MapTable) storeMgr.getTable(mmd);
String aliasForMap = (embedded || mapKey) ? aliasForJoin : (aliasForJoin + "_MAP");
sqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), joinTbl, aliasForMap, joinTbl.getOwnerMapping(), null, null, true);
if (embedded) {
tblMappingSqlTbl = sqlTbl;
tblIdMapping = mapKey ? joinTbl.getKeyMapping() : joinTbl.getValueMapping();
} else {
if (mapKey) {
// Join to key table and use that
relTable = storeMgr.getDatastoreClass(mapmd.getKeyType(), clr);
sqlTbl = stmt.join(joinType, sqlTbl, joinTbl.getKeyMapping(), relTable, aliasForJoin, relTable.getIdMapping(), null, joinTableGroupName, true);
// TODO if there is an ON clause it needs to go on the correct join See [rdbms-177]
} else {
// Join to value table and use that
relTable = storeMgr.getDatastoreClass(mapmd.getValueType(), clr);
sqlTbl = stmt.join(joinType, sqlTbl, joinTbl.getValueMapping(), relTable, aliasForJoin, relTable.getIdMapping(), null, joinTableGroupName, true);
// TODO if there is an ON clause it needs to go on the correct join See [rdbms-177]
}
tblMappingSqlTbl = sqlTbl;
tblIdMapping = tblMappingSqlTbl.getTable().getIdMapping();
}
} else if (mapmd.getMapType() == MapType.MAP_TYPE_KEY_IN_VALUE) {
// Join to value table
DatastoreClass valTable = storeMgr.getDatastoreClass(mapmd.getValueType(), clr);
JavaTypeMapping mapTblOwnerMapping;
if (mmd.getMappedBy() != null) {
mapTblOwnerMapping = valTable.getMemberMapping(mapmd.getValueClassMetaData(clr).getMetaDataForMember(mmd.getMappedBy()));
} else {
mapTblOwnerMapping = valTable.getExternalMapping(mmd, MappingType.EXTERNAL_FK);
}
String aliasForMap = (embedded || !mapKey) ? aliasForJoin : (aliasForJoin + "_MAP");
sqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), valTable, aliasForMap, mapTblOwnerMapping, null, null, true);
if (!embedded) {
if (mapKey) {
// Join to key table
JavaTypeMapping keyMapping = valTable.getMemberMapping(mmd.getKeyMetaData().getMappedBy());
relTable = storeMgr.getDatastoreClass(mapmd.getKeyType(), clr);
sqlTbl = stmt.join(joinType, sqlTbl, keyMapping, relTable, aliasForJoin, relTable.getIdMapping(), null, joinTableGroupName, true);
// TODO if there is an ON clause it needs to go on the correct join See [rdbms-177]
}
}
tblMappingSqlTbl = sqlTbl;
tblIdMapping = tblMappingSqlTbl.getTable().getIdMapping();
} else if (mapmd.getMapType() == MapType.MAP_TYPE_VALUE_IN_KEY) {
// Join to key table, and then to value table
DatastoreClass keyTable = storeMgr.getDatastoreClass(mapmd.getKeyType(), clr);
JavaTypeMapping mapTblOwnerMapping;
if (mmd.getMappedBy() != null) {
mapTblOwnerMapping = keyTable.getMemberMapping(mapmd.getKeyClassMetaData(clr).getMetaDataForMember(mmd.getMappedBy()));
} else {
mapTblOwnerMapping = keyTable.getExternalMapping(mmd, MappingType.EXTERNAL_FK);
}
String aliasForMap = (embedded || mapKey) ? aliasForJoin : (aliasForJoin + "_MAP");
sqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), keyTable, aliasForMap, mapTblOwnerMapping, null, null, true);
if (!embedded) {
if (!mapKey) {
// Join to value table
JavaTypeMapping valueMapping = keyTable.getMemberMapping(mmd.getValueMetaData().getMappedBy());
relTable = storeMgr.getDatastoreClass(mapmd.getValueType(), clr);
sqlTbl = stmt.join(joinType, sqlTbl, valueMapping, relTable, aliasForJoin, relTable.getIdMapping(), null, joinTableGroupName, true);
// TODO if there is an ON clause it needs to go on the correct join See [rdbms-177]
}
}
tblMappingSqlTbl = sqlTbl;
tblIdMapping = tblMappingSqlTbl.getTable().getIdMapping();
}
} else if (mmd.hasArray()) {
// Join across ARRAY relation
cmd = mmd.getArray().getElementClassMetaData(clr);
if (mmd.getArray().isEmbeddedElement() && mmd.getJoinMetaData() != null) {
// Embedded element stored in (array) join table
ArrayTable relEmbTable = (ArrayTable) storeMgr.getTable(mmd);
JavaTypeMapping relOwnerMapping = relEmbTable.getOwnerMapping();
sqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), relEmbTable, aliasForJoin, relOwnerMapping, null, joinTableGroupName, true);
tblMappingSqlTbl = sqlTbl;
tblIdMapping = relEmbTable.getElementMapping();
} else {
relTable = storeMgr.getDatastoreClass(mmd.getArray().getElementType(), clr);
if (mmd.getJoinMetaData() != null) {
// Join to join table, then to related table
ElementContainerTable joinTbl = (ElementContainerTable) storeMgr.getTable(mmd);
SQLTable joinSqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), joinTbl, null, joinTbl.getOwnerMapping(), null, null, true);
sqlTbl = stmt.join(joinType, joinSqlTbl, joinTbl.getElementMapping(), relTable, aliasForJoin, relTable.getIdMapping(), null, joinTableGroupName, true);
} else {
// Join to related table FK
JavaTypeMapping relMapping = relTable.getExternalMapping(mmd, MappingType.EXTERNAL_FK);
sqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), relTable, aliasForJoin, relMapping, null, joinTableGroupName, true);
}
tblMappingSqlTbl = sqlTbl;
tblIdMapping = tblMappingSqlTbl.getTable().getIdMapping();
}
}
} else if (relationType == RelationType.MANY_TO_MANY_BI) {
previousMapping = null;
relTable = storeMgr.getDatastoreClass(mmd.getCollection().getElementType(), clr);
cmd = mmd.getCollection().getElementClassMetaData(clr);
relMmd = mmd.getRelatedMemberMetaData(clr)[0];
// Join to join table, then to related table
if (mmd.hasCollection()) {
CollectionTable joinTbl = (CollectionTable) storeMgr.getTable(mmd);
SQLTable joinSqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), joinTbl, null, joinTbl.getOwnerMapping(), null, null, true);
sqlTbl = stmt.join(joinType, joinSqlTbl, joinTbl.getElementMapping(), relTable, aliasForJoin, relTable.getIdMapping(), null, joinTableGroupName, true);
} else if (mmd.hasMap()) {
NucleusLogger.QUERY.warn("We do not support joining across a M-N MAP field : " + mmd.getFullFieldName());
} else if (mmd.hasArray()) {
NucleusLogger.QUERY.warn("We do not support joining across a M-N ARRAY field : " + mmd.getFullFieldName());
}
tblMappingSqlTbl = sqlTbl;
tblIdMapping = tblMappingSqlTbl.getTable().getIdMapping();
} else if (relationType == RelationType.MANY_TO_ONE_BI) {
previousMapping = null;
relTable = storeMgr.getDatastoreClass(mmd.getTypeName(), clr);
Object[] castDiscrimValues = null;
if (castCls != null && lastComponent) {
cmd = mmgr.getMetaDataForClass(castCls, clr);
if (cmd.hasDiscriminatorStrategy()) {
// Restrict discriminator on cast type to be the type+subclasses
castDiscrimValues = getDiscriminatorValuesForCastClass(cmd);
}
} else {
cmd = mmgr.getMetaDataForClass(mmd.getType(), clr);
}
relMmd = mmd.getRelatedMemberMetaData(clr)[0];
if (mmd.getJoinMetaData() != null || relMmd.getJoinMetaData() != null) {
// Join to join table, then to related table
if (mmd.hasCollection()) {
CollectionTable joinTbl = (CollectionTable) storeMgr.getTable(relMmd);
SQLTable joinSqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), joinTbl, null, joinTbl.getElementMapping(), null, null, true);
sqlTbl = stmt.join(joinType, joinSqlTbl, joinTbl.getOwnerMapping(), relTable, aliasForJoin, relTable.getIdMapping(), castDiscrimValues, joinTableGroupName, true);
} else if (mmd.hasMap()) {
NucleusLogger.QUERY.warn("We do not support joining across a N-1 MAP field : " + mmd.getFullFieldName());
} else if (mmd.hasArray()) {
NucleusLogger.QUERY.warn("We do not support joining across a N-1 ARRAY field : " + mmd.getFullFieldName());
}
} else {
// Join to owner table
JavaTypeMapping fkMapping = sqlTbl.getTable().getMemberMapping(mmd);
sqlTbl = stmt.join(joinType, sqlTbl, fkMapping, relTable, aliasForJoin, relTable.getIdMapping(), castDiscrimValues, joinTableGroupName, true);
}
tblMappingSqlTbl = sqlTbl;
tblIdMapping = tblMappingSqlTbl.getTable().getIdMapping();
} else {
// NO RELATION, but cater for join table cases
previousMapping = null;
if (mmd.hasCollection()) {
cmd = null;
if (mmd.getJoinMetaData() != null) {
// Join to join table
CollectionTable joinTbl = (CollectionTable) storeMgr.getTable(mmd);
sqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), joinTbl, aliasForJoin, joinTbl.getOwnerMapping(), null, null, true);
tblMappingSqlTbl = sqlTbl;
tblIdMapping = joinTbl.getElementMapping();
} else {
throw new NucleusUserException("FROM clause contains join to Collection field at " + mmd.getFullFieldName() + " yet this has no join table");
}
} else if (mmd.hasMap()) {
MapMetaData mapmd = mmd.getMap();
cmd = mapmd.getValueClassMetaData(clr);
tblMmd = mmd;
if (// Should be the only type when relationType is NONE
mapmd.getMapType() == MapType.MAP_TYPE_JOIN) {
// Add join to join table
MapTable joinTbl = (MapTable) storeMgr.getTable(mmd);
String aliasForMap = aliasForJoin;
sqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), joinTbl, aliasForMap, joinTbl.getOwnerMapping(), null, null, true);
tblMappingSqlTbl = sqlTbl;
tblIdMapping = joinTbl.getValueMapping();
} else {
throw new NucleusUserException("FROM clause contains join to Map field at " + mmd.getFullFieldName() + " yet this has no join table");
}
} else if (mmd.hasArray()) {
cmd = null;
if (mmd.getJoinMetaData() != null) {
// Join to join table
ArrayTable joinTbl = (ArrayTable) storeMgr.getTable(mmd);
sqlTbl = stmt.join(joinType, sqlTbl, sqlTbl.getTable().getIdMapping(), joinTbl, aliasForJoin, joinTbl.getOwnerMapping(), null, null, true);
tblMappingSqlTbl = sqlTbl;
tblIdMapping = joinTbl.getElementMapping();
} else {
throw new NucleusUserException("FROM clause contains join to array field at " + mmd.getFullFieldName() + " yet this has no join table");
}
}
}
}
}
if (joinAlias != null) {
if (explicitJoinPrimaryByAlias == null) {
explicitJoinPrimaryByAlias = new HashMap<>();
}
explicitJoinPrimaryByAlias.put(joinAlias, joinPrimExpr.getId());
SQLTableMapping tblMapping = null;
if (tblMmd != null) {
// Maps store the member so we can more easily navigate to the key/value
tblMapping = new SQLTableMapping(tblMappingSqlTbl, cmd, tblMmd, tblIdMapping);
} else {
tblMapping = new SQLTableMapping(tblMappingSqlTbl, cmd, tblIdMapping);
}
setSQLTableMappingForAlias(joinAlias, tblMapping);
}
if (joinOnExpr != null) {
// Convert the ON expression to a BooleanExpression
processingOnClause = true;
joinOnExpr.evaluate(this);
BooleanExpression joinOnSqlExpr = (BooleanExpression) stack.pop();
processingOnClause = false;
// Add the ON expression to the most recent SQLTable at the end of this chain
// TODO Allow for SQL JOIN "grouping" [rdbms-177]. This applies to all cases where we join to a join table then to an element/value table and
// need to apply the ON clause across both
SQLJoin join = stmt.getJoinForTable(sqlTbl);
join.addAndCondition(joinOnSqlExpr);
}
} else {
previousMapping = null;
}
// Move on to next join in the chain
rightExpr = rightExpr.getRight();
}
}
use of org.datanucleus.store.query.expression.DyadicExpression in project datanucleus-rdbms by datanucleus.
the class QueryToSQLMapper method compileResult.
/**
* Method to compile the result clause of the query into the SQLStatement.
* Note that this also compiles queries of the candidate (no specified result), selecting the candidate field(s).
* @param stmt SELECT statement
*/
protected void compileResult(SelectStatement stmt) {
compileComponent = CompilationComponent.RESULT;
boolean unionsPresent = stmt.getNumberOfUnions() > 0;
// TODO Cater for more expression types where we have UNIONs and select each UNION separately
if (compilation.getExprResult() != null) {
// Select any result expressions
Expression[] resultExprs = compilation.getExprResult();
for (int i = 0; i < resultExprs.length; i++) {
String alias = resultExprs[i].getAlias();
if (alias != null && resultAliases == null) {
resultAliases = new HashSet<>();
}
if (resultExprs[i] instanceof InvokeExpression || resultExprs[i] instanceof ParameterExpression || resultExprs[i] instanceof Literal) {
// Process expressions that need no special treatment
if (resultExprs[i] instanceof InvokeExpression) {
processInvokeExpression((InvokeExpression) resultExprs[i]);
} else if (resultExprs[i] instanceof ParameterExpression) {
// Second argument : parameters are literals in result
processParameterExpression((ParameterExpression) resultExprs[i], true);
} else {
processLiteral((Literal) resultExprs[i]);
}
SQLExpression sqlExpr = stack.pop();
validateExpressionForResult(sqlExpr);
int[] cols = stmt.select(sqlExpr, alias);
StatementMappingIndex idx = new StatementMappingIndex(sqlExpr.getJavaTypeMapping());
idx.setColumnPositions(cols);
if (alias != null) {
resultAliases.add(alias);
idx.setColumnAlias(alias);
}
resultDefinition.addMappingForResultExpression(i, idx);
} else if (resultExprs[i] instanceof PrimaryExpression) {
PrimaryExpression primExpr = (PrimaryExpression) resultExprs[i];
if (primExpr.getId().equals(candidateAlias)) {
// "this", so select fetch plan fields
if (unionsPresent) {
// Process the first union separately (in case they have TYPE/instanceof) and then handle remaining UNIONs below
stmt.setAllowUnions(false);
}
StatementClassMapping map = new StatementClassMapping(candidateCmd.getFullClassName(), null);
SQLStatementHelper.selectFetchPlanOfCandidateInStatement(stmt, map, candidateCmd, fetchPlan, 1);
resultDefinition.addMappingForResultExpression(i, map);
if (unionsPresent) {
// Process remaining UNIONs. Assumed that we have the same result mapping as the first UNION otherwise SQL wouldn't work anyway
stmt.setAllowUnions(true);
List<SelectStatement> unionStmts = stmt.getUnions();
SelectStatement originalStmt = stmt;
for (SelectStatement unionStmt : unionStmts) {
this.stmt = unionStmt;
unionStmt.setQueryGenerator(this);
unionStmt.setAllowUnions(false);
StatementClassMapping dummyClsMapping = new StatementClassMapping(candidateCmd.getFullClassName(), null);
SQLStatementHelper.selectFetchPlanOfCandidateInStatement(unionStmt, dummyClsMapping, candidateCmd, fetchPlan, 1);
unionStmt.setQueryGenerator(null);
unionStmt.setAllowUnions(true);
}
this.stmt = originalStmt;
}
} else {
processPrimaryExpression(primExpr);
SQLExpression sqlExpr = stack.pop();
validateExpressionForResult(sqlExpr);
if (primExpr.getId().endsWith("#KEY") || primExpr.getId().endsWith("#VALUE")) {
// JPQL KEY(map) or VALUE(map), so select FetchPlan fields where persistable
if (sqlExpr.getJavaTypeMapping() instanceof PersistableMapping) {
// Method returns persistable object, so select the FetchPlan
String selectedType = ((PersistableMapping) sqlExpr.getJavaTypeMapping()).getType();
AbstractClassMetaData selectedCmd = ec.getMetaDataManager().getMetaDataForClass(selectedType, clr);
FetchPlanForClass fpForCmd = fetchPlan.getFetchPlanForClass(selectedCmd);
int[] membersToSelect = fpForCmd.getMemberNumbers();
ClassTable selectedTable = (ClassTable) sqlExpr.getSQLTable().getTable();
StatementClassMapping map = new StatementClassMapping(selectedCmd.getFullClassName(), null);
if (selectedCmd.getIdentityType() == IdentityType.DATASTORE) {
int[] cols = stmt.select(sqlExpr.getSQLTable(), selectedTable.getSurrogateMapping(SurrogateColumnType.DATASTORE_ID, false), alias);
StatementMappingIndex idx = new StatementMappingIndex(selectedTable.getSurrogateMapping(SurrogateColumnType.DATASTORE_ID, false));
idx.setColumnPositions(cols);
map.addMappingForMember(SurrogateColumnType.DATASTORE_ID.getFieldNumber(), idx);
}
// Select the FetchPlan members
for (int memberToSelect : membersToSelect) {
AbstractMemberMetaData selMmd = selectedCmd.getMetaDataForManagedMemberAtAbsolutePosition(memberToSelect);
// TODO Arbitrary penultimate argument
SQLStatementHelper.selectMemberOfSourceInStatement(stmt, map, fetchPlan, sqlExpr.getSQLTable(), selMmd, clr, 1, null);
}
resultDefinition.addMappingForResultExpression(i, map);
continue;
} else if (sqlExpr.getJavaTypeMapping() instanceof EmbeddedMapping) {
// Method returns embedded object, so select the FetchPlan
EmbeddedMapping embMapping = (EmbeddedMapping) sqlExpr.getJavaTypeMapping();
AbstractClassMetaData selectedCmd = ec.getMetaDataManager().getMetaDataForClass(embMapping.getType(), clr);
// Select the FetchPlan members
StatementClassMapping map = new StatementClassMapping(selectedCmd.getFullClassName(), null);
int[] membersToSelect = fetchPlan.getFetchPlanForClass(selectedCmd).getMemberNumbers();
for (int memberToSelect : membersToSelect) {
AbstractMemberMetaData selMmd = selectedCmd.getMetaDataForManagedMemberAtAbsolutePosition(memberToSelect);
JavaTypeMapping selMapping = embMapping.getJavaTypeMapping(selMmd.getName());
if (selMapping.includeInFetchStatement()) {
int[] cols = stmt.select(sqlExpr.getSQLTable(), selMapping, alias);
StatementMappingIndex idx = new StatementMappingIndex(selMapping);
idx.setColumnPositions(cols);
map.addMappingForMember(memberToSelect, idx);
}
}
resultDefinition.addMappingForResultExpression(i, map);
continue;
}
}
// TODO If the user selects an alias here that is joined, should maybe respect FetchPlan for that (like above for candidate)
// TODO Cater for use of UNIONs (e.g "complete-table" inheritance) where mapping is different in other UNION
// The difficulty here is that processPrimaryExpression makes use of the sqlTableByPrimary lookup, which is based on the primary statement, so
// it still doesn't find the right table/mapping for the UNIONed statements.
int[] cols = null;
if (sqlExpr instanceof SQLLiteral) {
cols = stmt.select(sqlExpr, alias);
} else {
cols = stmt.select(sqlExpr.getSQLTable(), sqlExpr.getJavaTypeMapping(), alias);
}
StatementMappingIndex idx = new StatementMappingIndex(sqlExpr.getJavaTypeMapping());
idx.setColumnPositions(cols);
if (alias != null) {
resultAliases.add(alias);
idx.setColumnAlias(alias);
}
resultDefinition.addMappingForResultExpression(i, idx);
}
} else if (resultExprs[i] instanceof VariableExpression) {
// Subquery?
processVariableExpression((VariableExpression) resultExprs[i]);
SQLExpression sqlExpr = stack.pop();
validateExpressionForResult(sqlExpr);
if (sqlExpr instanceof UnboundExpression) {
// Variable wasn't bound in the compilation so far, so handle as cross-join
processUnboundExpression((UnboundExpression) sqlExpr);
sqlExpr = stack.pop();
NucleusLogger.QUERY.debug("QueryToSQL.exprResult variable was still unbound, so binding via cross-join");
}
int[] cols = stmt.select(sqlExpr, alias);
StatementMappingIndex idx = new StatementMappingIndex(sqlExpr.getJavaTypeMapping());
idx.setColumnPositions(cols);
if (alias != null) {
resultAliases.add(alias);
idx.setColumnAlias(alias);
}
resultDefinition.addMappingForResultExpression(i, idx);
} else if (resultExprs[i] instanceof TypeExpression) {
// TYPE(identification_variable | single_valued_path_expr | input_parameter)
TypeExpression typeExpr = (TypeExpression) resultExprs[i];
Expression containedExpr = typeExpr.getContainedExpression();
if (containedExpr instanceof PrimaryExpression) {
processPrimaryExpression((PrimaryExpression) containedExpr);
SQLExpression sqlExpr = stack.pop();
JavaTypeMapping discrimMapping = sqlExpr.getSQLTable().getTable().getSurrogateMapping(SurrogateColumnType.DISCRIMINATOR, true);
if (discrimMapping == null) {
// TODO If we have a UNIONED primary expression then it would be possible to just select the DN_TYPE from the particular union
throw new NucleusException("Result has call to " + typeExpr + " but contained expression has no discriminator. Not supported");
}
int[] cols = stmt.select(sqlExpr.getSQLTable(), discrimMapping, null, true);
StatementMappingIndex idx = new StatementMappingIndex(discrimMapping);
idx.setColumnPositions(cols);
resultDefinition.addMappingForResultExpression(i, idx);
} else {
throw new NucleusException("Result has call to " + typeExpr + " but contained expression not supported");
}
} else if (resultExprs[i] instanceof CreatorExpression) {
processCreatorExpression((CreatorExpression) resultExprs[i]);
NewObjectExpression sqlExpr = (NewObjectExpression) stack.pop();
StatementNewObjectMapping stmtMap = getStatementMappingForNewObjectExpression(sqlExpr, stmt);
resultDefinition.addMappingForResultExpression(i, stmtMap);
} else if (resultExprs[i] instanceof DyadicExpression || resultExprs[i] instanceof CaseExpression) {
if (unionsPresent) {
// Process the first union separately (in case they have TYPE/instanceof) and then handle remaining UNIONs below
stmt.setAllowUnions(false);
}
// TODO If we have something like "DISTINCT this" then maybe could actually select the FetchPlan like if it was a basic PrimaryExpression(this)
resultExprs[i].evaluate(this);
SQLExpression sqlExpr = stack.pop();
int[] cols = stmt.select(sqlExpr, alias);
StatementMappingIndex idx = new StatementMappingIndex(sqlExpr.getJavaTypeMapping());
idx.setColumnPositions(cols);
if (alias != null) {
resultAliases.add(alias);
idx.setColumnAlias(alias);
}
resultDefinition.addMappingForResultExpression(i, idx);
if (unionsPresent) {
// Process remaining UNIONs. Assumed that we have the same result mapping as the first UNION otherwise SQL wouldn't work anyway
stmt.setAllowUnions(true);
List<SelectStatement> unionStmts = stmt.getUnions();
SelectStatement originalStmt = stmt;
for (SelectStatement unionStmt : unionStmts) {
this.stmt = unionStmt;
unionStmt.setQueryGenerator(this);
unionStmt.setAllowUnions(false);
resultExprs[i].evaluate(this);
sqlExpr = stack.pop();
unionStmt.select(sqlExpr, alias);
unionStmt.setQueryGenerator(null);
unionStmt.setAllowUnions(true);
}
this.stmt = originalStmt;
}
} else {
throw new NucleusException("Dont currently support result clause containing expression of type " + resultExprs[i]);
}
}
if (stmt.getNumberOfSelects() == 0) {
// Nothing selected so likely the user had some "new MyClass()" expression, so select "1"
stmt.select(exprFactory.newLiteral(stmt, storeMgr.getMappingManager().getMapping(Integer.class), 1), null);
}
} else {
// Select of the candidate (no result)
if (candidateCmd.getIdentityType() == IdentityType.NONDURABLE) {
// Nondurable identity cases have no "id" for later fetching so get all fields now
if (NucleusLogger.QUERY.isDebugEnabled()) {
NucleusLogger.QUERY.debug(Localiser.msg("052520", candidateCmd.getFullClassName()));
}
fetchPlan.setGroup("all");
}
if (subclasses) {
// TODO Check for special case of candidate+subclasses stores in same table with discriminator, so select FetchGroup of subclasses also
}
int maxFetchDepth = fetchPlan.getMaxFetchDepth();
if (extensionsByName != null && extensionsByName.containsKey(PropertyNames.PROPERTY_MAX_FETCH_DEPTH)) {
maxFetchDepth = (Integer) extensionsByName.get(PropertyNames.PROPERTY_MAX_FETCH_DEPTH);
}
if (maxFetchDepth < 0) {
NucleusLogger.QUERY.debug("No limit specified on query fetch so limiting to 3 levels from candidate. " + "Specify the '" + PropertyNames.PROPERTY_MAX_FETCH_DEPTH + "' to override this");
// TODO Arbitrary
maxFetchDepth = 3;
}
// This means that we then cater for a UNION using a different column name than the primary statement
if (unionsPresent) {
// Process the first union separately (in case they have TYPE/instanceof) and then handle remaining UNIONs below
stmt.setAllowUnions(false);
}
selectFetchPlanForCandidate(stmt, resultDefinitionForClass, maxFetchDepth);
if (unionsPresent) {
// Process remaining UNIONs. Assumed that we have the same result mapping as the first UNION otherwise SQL wouldn't work anyway
stmt.setAllowUnions(true);
List<SelectStatement> unionStmts = stmt.getUnions();
SelectStatement originalStmt = stmt;
for (SelectStatement unionStmt : unionStmts) {
this.stmt = unionStmt;
unionStmt.setQueryGenerator(this);
unionStmt.setAllowUnions(false);
// We don't want to overwrite anything in the root StatementClassMapping
StatementClassMapping dummyResClsMapping = new StatementClassMapping();
selectFetchPlanForCandidate(unionStmt, dummyResClsMapping, maxFetchDepth);
unionStmt.setQueryGenerator(null);
unionStmt.setAllowUnions(true);
}
this.stmt = originalStmt;
}
}
compileComponent = null;
}
Aggregations