Search in sources :

Example 1 with Symbol

use of org.datanucleus.store.query.compiler.Symbol 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;
}
Also used : SQLExpression(org.datanucleus.store.rdbms.sql.expression.SQLExpression) PrimaryExpression(org.datanucleus.store.query.expression.PrimaryExpression) JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) Symbol(org.datanucleus.store.query.compiler.Symbol) SQLLiteral(org.datanucleus.store.rdbms.sql.expression.SQLLiteral) UnboundExpression(org.datanucleus.store.rdbms.sql.expression.UnboundExpression) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) BooleanExpression(org.datanucleus.store.rdbms.sql.expression.BooleanExpression) TemporalLiteral(org.datanucleus.store.rdbms.sql.expression.TemporalLiteral) Literal(org.datanucleus.store.query.expression.Literal) SQLLiteral(org.datanucleus.store.rdbms.sql.expression.SQLLiteral) ParameterLiteral(org.datanucleus.store.rdbms.sql.expression.ParameterLiteral) CollectionLiteral(org.datanucleus.store.rdbms.sql.expression.CollectionLiteral) BooleanLiteral(org.datanucleus.store.rdbms.sql.expression.BooleanLiteral) IntegerLiteral(org.datanucleus.store.rdbms.sql.expression.IntegerLiteral) NullLiteral(org.datanucleus.store.rdbms.sql.expression.NullLiteral) SQLTable(org.datanucleus.store.rdbms.sql.SQLTable) ListIterator(java.util.ListIterator) Iterator(java.util.Iterator) ArrayList(java.util.ArrayList) List(java.util.List) MapExpression(org.datanucleus.store.rdbms.sql.expression.MapExpression) InvokeExpression(org.datanucleus.store.query.expression.InvokeExpression) Table(org.datanucleus.store.rdbms.table.Table) JoinTable(org.datanucleus.store.rdbms.table.JoinTable) ClassTable(org.datanucleus.store.rdbms.table.ClassTable) ElementContainerTable(org.datanucleus.store.rdbms.table.ElementContainerTable) MapTable(org.datanucleus.store.rdbms.table.MapTable) SQLTable(org.datanucleus.store.rdbms.sql.SQLTable) ArrayTable(org.datanucleus.store.rdbms.table.ArrayTable) CollectionTable(org.datanucleus.store.rdbms.table.CollectionTable) NucleusUserException(org.datanucleus.exceptions.NucleusUserException) VariableExpression(org.datanucleus.store.query.expression.VariableExpression) DyadicExpression(org.datanucleus.store.query.expression.DyadicExpression) OptionalMapping(org.datanucleus.store.rdbms.mapping.java.OptionalMapping) PersistableMapping(org.datanucleus.store.rdbms.mapping.java.PersistableMapping) EmbeddedMapping(org.datanucleus.store.rdbms.mapping.java.EmbeddedMapping) ParameterExpression(org.datanucleus.store.query.expression.ParameterExpression) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) FetchPlanForClass(org.datanucleus.FetchPlanForClass) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) NucleusException(org.datanucleus.exceptions.NucleusException) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData) JoinTable(org.datanucleus.store.rdbms.table.JoinTable)

Example 2 with Symbol

use of org.datanucleus.store.query.compiler.Symbol in project datanucleus-rdbms by datanucleus.

the class QueryToSQLMapper method bindVariable.

/**
 * Method to bind the specified unbound variable (as cross join) on the assumption that the type is a persistable class.
 * @param expr Unbound expression
 * @param type The type to bind as
 */
public SQLExpression bindVariable(UnboundExpression expr, Class type) {
    String varName = expr.getVariableName();
    Symbol varSym = compilation.getSymbolTable().getSymbol(varName);
    if (varSym.getValueType() == null) {
        varSym.setValueType(type);
    }
    AbstractClassMetaData cmd = ec.getMetaDataManager().getMetaDataForClass(type, clr);
    if (cmd != null) {
        // Variable is persistent type, so add cross join (may need changing later on in compilation)
        DatastoreClass varTable = storeMgr.getDatastoreClass(varSym.getValueType().getName(), clr);
        SQLTable varSqlTbl = stmt.join(JoinType.CROSS_JOIN, null, null, null, varTable, "VAR_" + varName, null, null, null, null, true, null);
        SQLTableMapping varSqlTblMapping = new SQLTableMapping(varSqlTbl, cmd, varTable.getIdMapping());
        setSQLTableMappingForAlias(varName, varSqlTblMapping);
        return exprFactory.newExpression(stmt, varSqlTbl, varTable.getIdMapping());
    }
    return null;
}
Also used : Symbol(org.datanucleus.store.query.compiler.Symbol) SQLTable(org.datanucleus.store.rdbms.sql.SQLTable) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData)

Example 3 with Symbol

use of org.datanucleus.store.query.compiler.Symbol in project datanucleus-rdbms by datanucleus.

the class QueryToSQLMapper method processVariableExpression.

/* (non-Javadoc)
     * @see org.datanucleus.query.evaluator.AbstractExpressionEvaluator#processVariableExpression(org.datanucleus.query.expression.VariableExpression)
     */
@Override
protected Object processVariableExpression(VariableExpression expr) {
    String varName = expr.getId();
    Symbol varSym = expr.getSymbol();
    if (varSym != null) {
        // Use name from symbol if possible
        varName = varSym.getQualifiedName();
    }
    if (hasSQLTableMappingForAlias(varName)) {
        // Variable already found
        SQLTableMapping tblMapping = getSQLTableMappingForAlias(varName);
        SQLExpression sqlExpr = exprFactory.newExpression(tblMapping.table.getSQLStatement(), tblMapping.table, tblMapping.mapping);
        stack.push(sqlExpr);
        return sqlExpr;
    } else if (compilation.getCompilationForSubquery(varName) != null) {
        // Subquery variable
        QueryCompilation subCompilation = compilation.getCompilationForSubquery(varName);
        AbstractClassMetaData subCmd = ec.getMetaDataManager().getMetaDataForClass(subCompilation.getCandidateClass(), ec.getClassLoaderResolver());
        // Create subquery statement, using any provided alias if possible
        String subAlias = null;
        if (subCompilation.getCandidateAlias() != null && !subCompilation.getCandidateAlias().equals(candidateAlias)) {
            subAlias = subCompilation.getCandidateAlias();
        }
        StatementResultMapping subqueryResultMapping = new StatementResultMapping();
        // TODO Fix "avg(something)" arg - not essential but is a hack right now
        SQLStatement subStmt = RDBMSQueryUtils.getStatementForCandidates(storeMgr, stmt, subCmd, null, ec, subCompilation.getCandidateClass(), true, "avg(something)", subAlias, null, null);
        QueryToSQLMapper sqlMapper = new QueryToSQLMapper(subStmt, subCompilation, parameters, null, subqueryResultMapping, subCmd, true, fetchPlan, ec, importsDefinition, options, extensionsByName);
        sqlMapper.setDefaultJoinType(defaultJoinType);
        sqlMapper.setDefaultJoinTypeFilter(defaultJoinTypeFilter);
        sqlMapper.setParentMapper(this);
        sqlMapper.compile();
        if (subqueryResultMapping.getNumberOfResultExpressions() > 1) {
            throw new NucleusUserException("Number of result expressions in subquery should be 1");
        }
        SQLExpression subExpr = null;
        // TODO Cater for subquery select of its own candidate
        if (subqueryResultMapping.getNumberOfResultExpressions() == 0) {
            subExpr = new org.datanucleus.store.rdbms.sql.expression.SubqueryExpression(stmt, subStmt);
        } else {
            JavaTypeMapping subMapping = ((StatementMappingIndex) subqueryResultMapping.getMappingForResultExpression(0)).getMapping();
            if (subMapping instanceof TemporalMapping) {
                subExpr = new TemporalSubqueryExpression(stmt, subStmt);
            } else if (subMapping instanceof StringMapping) {
                subExpr = new StringSubqueryExpression(stmt, subStmt);
            } else {
                subExpr = new NumericSubqueryExpression(stmt, subStmt);
            }
            if (subExpr.getJavaTypeMapping() == null) {
                subExpr.setJavaTypeMapping(subMapping);
            }
        }
        stack.push(subExpr);
        return subExpr;
    } else if (stmt.getParentStatement() != null && parentMapper != null && parentMapper.candidateAlias != null && parentMapper.candidateAlias.equals(varName)) {
        // Variable in subquery linking back to parent query
        SQLExpression varExpr = exprFactory.newExpression(stmt.getParentStatement(), stmt.getParentStatement().getPrimaryTable(), stmt.getParentStatement().getPrimaryTable().getTable().getIdMapping());
        stack.push(varExpr);
        return varExpr;
    } else {
        // Variable never met before, so return as UnboundExpression - process later if needing binding
        NucleusLogger.QUERY.debug("QueryToSQL.processVariable (unbound) variable=" + varName + " is not yet bound so returning UnboundExpression");
        UnboundExpression unbExpr = new UnboundExpression(stmt, varName);
        stack.push(unbExpr);
        return unbExpr;
    }
}
Also used : SQLExpression(org.datanucleus.store.rdbms.sql.expression.SQLExpression) JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) Symbol(org.datanucleus.store.query.compiler.Symbol) NucleusUserException(org.datanucleus.exceptions.NucleusUserException) UnboundExpression(org.datanucleus.store.rdbms.sql.expression.UnboundExpression) StringMapping(org.datanucleus.store.rdbms.mapping.java.StringMapping) SQLStatement(org.datanucleus.store.rdbms.sql.SQLStatement) NumericSubqueryExpression(org.datanucleus.store.rdbms.sql.expression.NumericSubqueryExpression) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) BooleanSubqueryExpression(org.datanucleus.store.rdbms.sql.expression.BooleanSubqueryExpression) NumericSubqueryExpression(org.datanucleus.store.rdbms.sql.expression.NumericSubqueryExpression) StringSubqueryExpression(org.datanucleus.store.rdbms.sql.expression.StringSubqueryExpression) TemporalSubqueryExpression(org.datanucleus.store.rdbms.sql.expression.TemporalSubqueryExpression) SubqueryExpression(org.datanucleus.store.query.expression.SubqueryExpression) TemporalSubqueryExpression(org.datanucleus.store.rdbms.sql.expression.TemporalSubqueryExpression) StringSubqueryExpression(org.datanucleus.store.rdbms.sql.expression.StringSubqueryExpression) QueryCompilation(org.datanucleus.store.query.compiler.QueryCompilation) TemporalMapping(org.datanucleus.store.rdbms.mapping.java.TemporalMapping)

Example 4 with Symbol

use of org.datanucleus.store.query.compiler.Symbol in project datanucleus-rdbms by datanucleus.

the class JDOQLQuery method compileInternal.

/**
 * Method to compile the JDOQL query.
 * Uses the superclass to compile the generic query populating the "compilation", and then generates
 * the datastore-specific "datastoreCompilation".
 * @param parameterValues Map of param values keyed by param name (if available at compile time)
 */
protected synchronized void compileInternal(Map parameterValues) {
    if (isCompiled()) {
        return;
    }
    if (getExtension(EXTENSION_INCLUDE_SOFT_DELETES) != null) {
        // If using an extension that can change the datastore query then evict any existing compilation
        QueryManager qm = getQueryManager();
        qm.removeQueryCompilation(Query.LANGUAGE_JDOQL, getQueryCacheKey());
    }
    // Compile the generic query expressions
    super.compileInternal(parameterValues);
    boolean inMemory = evaluateInMemory();
    if (candidateCollection != null && inMemory) {
        // TODO Maybe apply the result class checks ?
        return;
    }
    // Create the SQL statement, and its result/parameter definitions
    RDBMSStoreManager storeMgr = (RDBMSStoreManager) getStoreManager();
    if (candidateClass == null) {
        throw new NucleusUserException(Localiser.msg("021009", candidateClassName));
    }
    // Make sure any persistence info is loaded
    ec.hasPersistenceInformationForClass(candidateClass);
    if (parameterValues != null) {
        // Check for null values on primitive parameters
        Set paramNames = parameterValues.entrySet();
        Iterator<Map.Entry> iter = paramNames.iterator();
        while (iter.hasNext()) {
            Map.Entry entry = iter.next();
            Object paramName = entry.getKey();
            if (paramName instanceof String) {
                Symbol sym = compilation.getSymbolTable().getSymbol((String) paramName);
                Object value = entry.getValue();
                if (value == null) {
                    // so omit the check for that case
                    if (sym != null && sym.getValueType() != null && sym.getValueType().isPrimitive()) {
                        throw new NucleusUserException(Localiser.msg("021117", paramName, sym.getValueType().getName()));
                    }
                }
            }
        }
    }
    QueryManager qm = getQueryManager();
    String datastoreKey = storeMgr.getQueryCacheKey();
    String queryCacheKey = getQueryCacheKey();
    if (useCaching() && queryCacheKey != null) {
        // Check if we have any parameters set to null, since this can invalidate a datastore compilation
        // e.g " field == :val" can be "COL IS NULL" or "COL = <val>"
        boolean nullParameter = false;
        if (parameterValues != null) {
            Iterator iter = parameterValues.values().iterator();
            while (iter.hasNext()) {
                Object val = iter.next();
                if (val == null) {
                    nullParameter = true;
                    break;
                }
            }
        }
        if (!nullParameter) {
            // Allowing caching so try to find compiled (datastore) query
            datastoreCompilation = (RDBMSQueryCompilation) qm.getDatastoreQueryCompilation(datastoreKey, getLanguage(), queryCacheKey);
            if (datastoreCompilation != null) {
                // Cached compilation exists for this datastore so reuse it
                setResultDistinct(compilation.getResultDistinct());
                return;
            }
        }
    }
    // Compile the query for the datastore since not cached
    AbstractClassMetaData acmd = getCandidateClassMetaData();
    if (type == QueryType.BULK_UPDATE) {
        datastoreCompilation = new RDBMSQueryCompilation();
        compileQueryUpdate(parameterValues, acmd);
    } else if (type == QueryType.BULK_DELETE) {
        datastoreCompilation = new RDBMSQueryCompilation();
        compileQueryDelete(parameterValues, acmd);
    } else {
        datastoreCompilation = new RDBMSQueryCompilation();
        synchronized (datastoreCompilation) {
            if (inMemory) {
                // Generate statement to just retrieve all candidate objects for later processing
                compileQueryToRetrieveCandidates(parameterValues, acmd);
            } else {
                // Generate statement to perform the full query in the datastore
                compileQueryFull(parameterValues, acmd);
                if (result != null) {
                    // Check existence of invalid selections in the result
                    StatementResultMapping resultMapping = datastoreCompilation.getResultDefinition();
                    for (int i = 0; i < resultMapping.getNumberOfResultExpressions(); i++) {
                        Object stmtMap = resultMapping.getMappingForResultExpression(i);
                        if (stmtMap instanceof StatementMappingIndex) {
                            StatementMappingIndex idx = (StatementMappingIndex) stmtMap;
                            AbstractMemberMetaData mmd = idx.getMapping().getMemberMetaData();
                            if (mmd != null) {
                                if (idx.getMapping() instanceof AbstractContainerMapping && idx.getMapping().getNumberOfColumnMappings() != 1) {
                                    throw new NucleusUserException(Localiser.msg("021213"));
                                }
                            }
                        }
                    }
                    if (resultClass != null) {
                        if (// Don't apply checks when user has specified the default (no result class)
                        !resultClass.getName().equals(Object[].class.getName())) {
                            // Check validity of resultClass for the result (PrivilegedAction since uses reflection)
                            AccessController.doPrivileged(new PrivilegedAction() {

                                public Object run() {
                                    // Check that this class has the necessary constructor/setters/fields to be used
                                    StatementResultMapping resultMapping = datastoreCompilation.getResultDefinition();
                                    if (QueryUtils.resultClassIsSimple(resultClass.getName())) {
                                        if (resultMapping.getNumberOfResultExpressions() > 1) {
                                            // Invalid number of result expressions
                                            throw new NucleusUserException(Localiser.msg("021201", resultClass.getName()));
                                        }
                                        Object stmtMap = resultMapping.getMappingForResultExpression(0);
                                        if (stmtMap instanceof StatementMappingIndex) {
                                            StatementMappingIndex idx = (StatementMappingIndex) stmtMap;
                                            Class exprType = idx.getMapping().getJavaType();
                                            boolean typeConsistent = false;
                                            if (exprType == resultClass) {
                                                typeConsistent = true;
                                            } else if (exprType.isPrimitive()) {
                                                Class resultClassPrimitive = ClassUtils.getPrimitiveTypeForType(resultClass);
                                                if (resultClassPrimitive == exprType) {
                                                    typeConsistent = true;
                                                }
                                            }
                                            if (!typeConsistent) {
                                                // Inconsistent expression type not matching the result class type
                                                throw new NucleusUserException(Localiser.msg("021202", resultClass.getName(), exprType));
                                            }
                                        } else {
                                            // TODO Handle StatementNewObjectMapping
                                            throw new NucleusUserException("Don't support result clause of " + result + " with resultClass of " + resultClass.getName());
                                        }
                                    } else if (QueryUtils.resultClassIsUserType(resultClass.getName())) {
                                        // Check for valid constructor (either using param types, or using default ctr)
                                        Class[] ctrTypes = new Class[resultMapping.getNumberOfResultExpressions()];
                                        for (int i = 0; i < ctrTypes.length; i++) {
                                            Object stmtMap = resultMapping.getMappingForResultExpression(i);
                                            if (stmtMap instanceof StatementMappingIndex) {
                                                ctrTypes[i] = ((StatementMappingIndex) stmtMap).getMapping().getJavaType();
                                            } else if (stmtMap instanceof StatementNewObjectMapping) {
                                            // TODO Handle this
                                            }
                                        }
                                        Constructor ctr = ClassUtils.getConstructorWithArguments(resultClass, ctrTypes);
                                        if (ctr == null && !ClassUtils.hasDefaultConstructor(resultClass)) {
                                            // No valid constructor found!
                                            throw new NucleusUserException(Localiser.msg("021205", resultClass.getName()));
                                        } else if (ctr == null) {
                                            // We are using default constructor, so check the types of the result expressions for means of input
                                            for (int i = 0; i < resultMapping.getNumberOfResultExpressions(); i++) {
                                                Object stmtMap = resultMapping.getMappingForResultExpression(i);
                                                if (stmtMap instanceof StatementMappingIndex) {
                                                    StatementMappingIndex mapIdx = (StatementMappingIndex) stmtMap;
                                                    AbstractMemberMetaData mmd = mapIdx.getMapping().getMemberMetaData();
                                                    String fieldName = mapIdx.getColumnAlias();
                                                    Class fieldType = mapIdx.getMapping().getJavaType();
                                                    if (fieldName == null && mmd != null) {
                                                        fieldName = mmd.getName();
                                                    }
                                                    if (fieldName != null) {
                                                        // Check for the field of that name in the result class
                                                        Class resultFieldType = null;
                                                        boolean publicField = true;
                                                        try {
                                                            Field fld = resultClass.getDeclaredField(fieldName);
                                                            resultFieldType = fld.getType();
                                                            // Check the type of the field
                                                            if (!ClassUtils.typesAreCompatible(fieldType, resultFieldType) && !ClassUtils.typesAreCompatible(resultFieldType, fieldType)) {
                                                                throw new NucleusUserException(Localiser.msg("021211", fieldName, fieldType.getName(), resultFieldType.getName()));
                                                            }
                                                            if (!Modifier.isPublic(fld.getModifiers())) {
                                                                publicField = false;
                                                            }
                                                        } catch (NoSuchFieldException nsfe) {
                                                            publicField = false;
                                                        }
                                                        // Check for a public set method
                                                        if (!publicField) {
                                                            Method setMethod = QueryUtils.getPublicSetMethodForFieldOfResultClass(resultClass, fieldName, resultFieldType);
                                                            if (setMethod == null) {
                                                                // No setter, so check for a public put(Object, Object) method
                                                                Method putMethod = QueryUtils.getPublicPutMethodForResultClass(resultClass);
                                                                if (putMethod == null) {
                                                                    throw new NucleusUserException(Localiser.msg("021212", resultClass.getName(), fieldName));
                                                                }
                                                            }
                                                        }
                                                    }
                                                } else if (stmtMap instanceof StatementNewObjectMapping) {
                                                // TODO Handle this
                                                }
                                            }
                                        }
                                    }
                                    return null;
                                }
                            });
                        }
                    }
                }
            }
            boolean hasParams = false;
            if (explicitParameters != null) {
                hasParams = true;
            } else if (parameterValues != null && parameterValues.size() > 0) {
                hasParams = true;
            }
            if (!statementReturnsEmpty && queryCacheKey != null && useCaching()) {
                // TODO Allow caching of queries with subqueries
                if (!datastoreCompilation.isPrecompilable() || (datastoreCompilation.getSQL().indexOf('?') < 0 && hasParams)) {
                    // Some parameters had their clauses evaluated during compilation so the query didn't gain any parameters, so don't cache it
                    NucleusLogger.QUERY.debug(Localiser.msg("021075"));
                } else {
                    qm.addDatastoreQueryCompilation(datastoreKey, getLanguage(), queryCacheKey, datastoreCompilation);
                }
            }
        }
    }
}
Also used : ResultSet(java.sql.ResultSet) Set(java.util.Set) HashSet(java.util.HashSet) Symbol(org.datanucleus.store.query.compiler.Symbol) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) Field(java.lang.reflect.Field) PrivilegedAction(java.security.PrivilegedAction) Iterator(java.util.Iterator) NucleusUserException(org.datanucleus.exceptions.NucleusUserException) Constructor(java.lang.reflect.Constructor) Method(java.lang.reflect.Method) RDBMSStoreManager(org.datanucleus.store.rdbms.RDBMSStoreManager) AbstractContainerMapping(org.datanucleus.store.rdbms.mapping.java.AbstractContainerMapping) QueryManager(org.datanucleus.store.query.QueryManager) FetchPlanForClass(org.datanucleus.FetchPlanForClass) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) Map(java.util.Map) HashMap(java.util.HashMap) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData)

Example 5 with Symbol

use of org.datanucleus.store.query.compiler.Symbol 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();
    }
}
Also used : FetchGroupManager(org.datanucleus.FetchGroupManager) PrimaryExpression(org.datanucleus.store.query.expression.PrimaryExpression) JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) Symbol(org.datanucleus.store.query.compiler.Symbol) SQLJoin(org.datanucleus.store.rdbms.sql.SQLJoin) SQLStatement(org.datanucleus.store.rdbms.sql.SQLStatement) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) MapTable(org.datanucleus.store.rdbms.table.MapTable) SelectStatement(org.datanucleus.store.rdbms.sql.SelectStatement) BooleanExpression(org.datanucleus.store.rdbms.sql.expression.BooleanExpression) CollectionTable(org.datanucleus.store.rdbms.table.CollectionTable) SQLTable(org.datanucleus.store.rdbms.sql.SQLTable) TemporalLiteral(org.datanucleus.store.rdbms.sql.expression.TemporalLiteral) Literal(org.datanucleus.store.query.expression.Literal) SQLLiteral(org.datanucleus.store.rdbms.sql.expression.SQLLiteral) ParameterLiteral(org.datanucleus.store.rdbms.sql.expression.ParameterLiteral) CollectionLiteral(org.datanucleus.store.rdbms.sql.expression.CollectionLiteral) BooleanLiteral(org.datanucleus.store.rdbms.sql.expression.BooleanLiteral) IntegerLiteral(org.datanucleus.store.rdbms.sql.expression.IntegerLiteral) NullLiteral(org.datanucleus.store.rdbms.sql.expression.NullLiteral) RelationType(org.datanucleus.metadata.RelationType) JoinExpression(org.datanucleus.store.query.expression.JoinExpression) ElementContainerTable(org.datanucleus.store.rdbms.table.ElementContainerTable) NucleusUserException(org.datanucleus.exceptions.NucleusUserException) MetaDataManager(org.datanucleus.metadata.MetaDataManager) JoinType(org.datanucleus.store.rdbms.sql.SQLJoin.JoinType) MapMetaData(org.datanucleus.metadata.MapMetaData) DyadicExpression(org.datanucleus.store.query.expression.DyadicExpression) PrimaryExpression(org.datanucleus.store.query.expression.PrimaryExpression) BooleanSubqueryExpression(org.datanucleus.store.rdbms.sql.expression.BooleanSubqueryExpression) TypeConverterExpression(org.datanucleus.store.rdbms.sql.expression.TypeConverterExpression) StringExpression(org.datanucleus.store.rdbms.sql.expression.StringExpression) NumericSubqueryExpression(org.datanucleus.store.rdbms.sql.expression.NumericSubqueryExpression) StringSubqueryExpression(org.datanucleus.store.rdbms.sql.expression.StringSubqueryExpression) MapExpression(org.datanucleus.store.rdbms.sql.expression.MapExpression) ArrayExpression(org.datanucleus.store.query.expression.ArrayExpression) CaseExpression(org.datanucleus.store.query.expression.CaseExpression) CreatorExpression(org.datanucleus.store.query.expression.CreatorExpression) NewObjectExpression(org.datanucleus.store.rdbms.sql.expression.NewObjectExpression) OrderExpression(org.datanucleus.store.query.expression.OrderExpression) TemporalSubqueryExpression(org.datanucleus.store.rdbms.sql.expression.TemporalSubqueryExpression) ClassExpression(org.datanucleus.store.query.expression.ClassExpression) BooleanExpression(org.datanucleus.store.rdbms.sql.expression.BooleanExpression) Expression(org.datanucleus.store.query.expression.Expression) SQLExpression(org.datanucleus.store.rdbms.sql.expression.SQLExpression) ParameterExpression(org.datanucleus.store.query.expression.ParameterExpression) UnboundExpression(org.datanucleus.store.rdbms.sql.expression.UnboundExpression) TemporalExpression(org.datanucleus.store.rdbms.sql.expression.TemporalExpression) SubqueryExpression(org.datanucleus.store.query.expression.SubqueryExpression) ResultAliasExpression(org.datanucleus.store.rdbms.sql.expression.ResultAliasExpression) VariableExpression(org.datanucleus.store.query.expression.VariableExpression) TypeExpression(org.datanucleus.store.query.expression.TypeExpression) NumericExpression(org.datanucleus.store.rdbms.sql.expression.NumericExpression) CollectionExpression(org.datanucleus.store.rdbms.sql.expression.CollectionExpression) DyadicExpression(org.datanucleus.store.query.expression.DyadicExpression) ColumnExpression(org.datanucleus.store.rdbms.sql.expression.ColumnExpression) JoinExpression(org.datanucleus.store.query.expression.JoinExpression) InvokeExpression(org.datanucleus.store.query.expression.InvokeExpression) EmbeddedMapping(org.datanucleus.store.rdbms.mapping.java.EmbeddedMapping) SQLTableGroup(org.datanucleus.store.rdbms.sql.SQLTableGroup) FetchGroup(org.datanucleus.FetchGroup) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) FetchPlanForClass(org.datanucleus.FetchPlanForClass) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) NucleusException(org.datanucleus.exceptions.NucleusException) ArrayTable(org.datanucleus.store.rdbms.table.ArrayTable) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData)

Aggregations

Symbol (org.datanucleus.store.query.compiler.Symbol)7 NucleusUserException (org.datanucleus.exceptions.NucleusUserException)5 AbstractClassMetaData (org.datanucleus.metadata.AbstractClassMetaData)5 SQLExpression (org.datanucleus.store.rdbms.sql.expression.SQLExpression)4 FetchPlanForClass (org.datanucleus.FetchPlanForClass)3 AbstractMemberMetaData (org.datanucleus.metadata.AbstractMemberMetaData)3 JavaTypeMapping (org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping)3 SQLTable (org.datanucleus.store.rdbms.sql.SQLTable)3 DatastoreClass (org.datanucleus.store.rdbms.table.DatastoreClass)3 HashMap (java.util.HashMap)2 Iterator (java.util.Iterator)2 Map (java.util.Map)2 NucleusException (org.datanucleus.exceptions.NucleusException)2 DyadicExpression (org.datanucleus.store.query.expression.DyadicExpression)2 InvokeExpression (org.datanucleus.store.query.expression.InvokeExpression)2 Literal (org.datanucleus.store.query.expression.Literal)2 ParameterExpression (org.datanucleus.store.query.expression.ParameterExpression)2 PrimaryExpression (org.datanucleus.store.query.expression.PrimaryExpression)2 SubqueryExpression (org.datanucleus.store.query.expression.SubqueryExpression)2 VariableExpression (org.datanucleus.store.query.expression.VariableExpression)2