Search in sources :

Example 1 with EmbeddedMapping

use of org.datanucleus.store.rdbms.mapping.java.EmbeddedMapping 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.query.expression.PrimaryExpression) JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) Symbol(org.datanucleus.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) SQLLiteral(org.datanucleus.store.rdbms.sql.expression.SQLLiteral) ParameterLiteral(org.datanucleus.store.rdbms.sql.expression.ParameterLiteral) BooleanLiteral(org.datanucleus.store.rdbms.sql.expression.BooleanLiteral) IntegerLiteral(org.datanucleus.store.rdbms.sql.expression.IntegerLiteral) Literal(org.datanucleus.query.expression.Literal) 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.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.query.expression.VariableExpression) DyadicExpression(org.datanucleus.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.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 EmbeddedMapping

use of org.datanucleus.store.rdbms.mapping.java.EmbeddedMapping in project datanucleus-rdbms by datanucleus.

the class QueryToSQLMapper method getSQLTableMappingForPrimaryExpression.

/**
 * Method to take in a PrimaryExpression and return the SQLTable mapping info that it signifies.
 * If the primary expression implies joining to other objects then adds the joins to the statement.
 * Only adds joins if necessary; so if there is a further component after the required join, or if
 * the "forceJoin" flag is set.
 * @param theStmt SQLStatement to use when looking for tables etc
 * @param exprName Name for an expression that this primary is relative to (optional)
 *                 If not specified then the tuples are relative to the candidate.
 *                 If specified then should have an entry in sqlTableByPrimary under this name.
 * @param primExpr The primary expression
 * @param forceJoin Whether to force a join if a relation member (or null if leaving to this method to decide)
 * @return The SQL table mapping information for the specified primary
 */
private SQLTableMapping getSQLTableMappingForPrimaryExpression(SQLStatement theStmt, String exprName, PrimaryExpression primExpr, Boolean forceJoin) {
    if (forceJoin == null && primExpr.getParent() != null) {
        if (primExpr.getParent().getOperator() == Expression.OP_IS || primExpr.getParent().getOperator() == Expression.OP_ISNOT) {
            // "instanceOf" needs to be in the table of the primary expression
            forceJoin = Boolean.TRUE;
        }
    }
    SQLTableMapping sqlMapping = null;
    List<String> tuples = primExpr.getTuples();
    // Find source object
    ListIterator<String> iter = tuples.listIterator();
    String first = tuples.get(0);
    boolean mapKey = false;
    boolean mapValue = false;
    if (first.endsWith("#KEY")) {
        first = first.substring(0, first.length() - 4);
        mapKey = true;
    } else if (first.endsWith("#VALUE")) {
        first = first.substring(0, first.length() - 6);
        mapValue = true;
    }
    String primaryName = null;
    if (exprName != null) {
        // Primary relative to some object etc
        sqlMapping = getSQLTableMappingForAlias(exprName);
        primaryName = exprName;
    } else {
        if (hasSQLTableMappingForAlias(first)) {
            // Start from a candidate (e.g JPQL alias)
            sqlMapping = getSQLTableMappingForAlias(first);
            primaryName = first;
            // Skip first tuple
            iter.next();
        }
        if (sqlMapping != null && first.equals(candidateAlias) && candidateCmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.COMPLETE_TABLE) {
            // Special case of using COMPLETE_TABLE for candidate and picked wrong table
            // TODO Use OPTION_CASE_INSENSITIVE
            SQLTable firstSqlTbl = stmt.getTable(first.toUpperCase());
            if (firstSqlTbl != null && firstSqlTbl.getTable() != sqlMapping.table.getTable()) {
                // Cached the SQLTableMapping for one of the other inherited classes, so create our own
                sqlMapping = new SQLTableMapping(firstSqlTbl, sqlMapping.cmd, firstSqlTbl.getTable().getIdMapping());
            }
        }
        if (sqlMapping == null) {
            if (parentMapper != null) {
                QueryToSQLMapper theParentMapper = parentMapper;
                while (theParentMapper != null) {
                    if (theParentMapper.hasSQLTableMappingForAlias(first)) {
                        // Try parent query
                        sqlMapping = theParentMapper.getSQLTableMappingForAlias(first);
                        primaryName = first;
                        // Skip first tuple
                        iter.next();
                        // This expression is for the parent statement so any joins need to go on that statement
                        theStmt = sqlMapping.table.getSQLStatement();
                        break;
                    }
                    theParentMapper = theParentMapper.parentMapper;
                }
            }
        }
        if (sqlMapping == null) {
            // Field of candidate, so use candidate
            sqlMapping = getSQLTableMappingForAlias(candidateAlias);
            primaryName = candidateAlias;
        }
    }
    AbstractClassMetaData cmd = sqlMapping.cmd;
    JavaTypeMapping mapping = sqlMapping.mapping;
    if (sqlMapping.mmd != null && (mapKey || mapValue)) {
        // Special case of MAP#KEY or MAP#VALUE, so navigate from the Map "table" to the key or value
        SQLTable sqlTbl = sqlMapping.table;
        AbstractMemberMetaData mmd = sqlMapping.mmd;
        MapMetaData mapmd = mmd.getMap();
        // Find the table forming the Map. This may be a join table, or the key or value depending on the type
        if (mapKey) {
            // Cater for all case possibilities of table name/alias
            // TODO Use OPTION_CASE_INSENSITIVE
            SQLTable mapSqlTbl = stmt.getTable(first + "_MAP");
            if (mapSqlTbl == null) {
                mapSqlTbl = stmt.getTable((first + "_MAP").toUpperCase());
                if (mapSqlTbl == null) {
                    mapSqlTbl = stmt.getTable((first + "_MAP").toLowerCase());
                }
            }
            if (mapSqlTbl != null) {
                sqlTbl = mapSqlTbl;
            }
        }
        if (mapmd.getMapType() == MapType.MAP_TYPE_JOIN) {
            if (sqlTbl.getTable() instanceof MapTable) {
                MapTable mapTable = (MapTable) sqlTbl.getTable();
                if (mapKey) {
                    cmd = mapmd.getKeyClassMetaData(clr);
                    if (!mapmd.isEmbeddedKey() && !mapmd.isSerializedKey()) {
                        // Join to key table
                        DatastoreClass keyTable = storeMgr.getDatastoreClass(mapmd.getKeyType(), clr);
                        sqlTbl = stmt.join(getDefaultJoinTypeForNavigation(), sqlMapping.table, mapTable.getKeyMapping(), keyTable, null, keyTable.getIdMapping(), null, null, true);
                        mapping = keyTable.getIdMapping();
                    } else {
                        mapping = mapTable.getKeyMapping();
                    }
                } else {
                    cmd = mapmd.getValueClassMetaData(clr);
                    if (!mapmd.isEmbeddedValue() && !mapmd.isSerializedValue()) {
                        // Join to value table
                        DatastoreClass valueTable = storeMgr.getDatastoreClass(mapmd.getValueType(), clr);
                        sqlTbl = stmt.join(getDefaultJoinTypeForNavigation(), sqlMapping.table, mapTable.getValueMapping(), valueTable, null, valueTable.getIdMapping(), null, null, true);
                        mapping = valueTable.getIdMapping();
                    } else {
                        mapping = mapTable.getValueMapping();
                    }
                }
            } else {
                // TODO Document exactly which situation this is
                if (!mapmd.isEmbeddedValue() && !mapmd.isSerializedValue()) {
                    mapping = sqlTbl.getTable().getIdMapping();
                }
            }
        } else if (mapmd.getMapType() == MapType.MAP_TYPE_KEY_IN_VALUE) {
            if (mapKey) {
                AbstractClassMetaData keyCmd = mapmd.getKeyClassMetaData(clr);
                String keyMappedBy = mmd.getKeyMetaData().getMappedBy();
                mapping = ((DatastoreClass) sqlTbl.getTable()).getMemberMapping(keyMappedBy);
                if (keyCmd != null) {
                    // Join to key table
                    DatastoreClass keyTable = storeMgr.getDatastoreClass(mapmd.getKeyType(), clr);
                    sqlTbl = stmt.join(getDefaultJoinTypeForNavigation(), sqlMapping.table, mapping, keyTable, null, keyTable.getIdMapping(), null, null, true);
                    mapping = keyTable.getIdMapping();
                }
            } else {
            }
        } else if (mapmd.getMapType() == MapType.MAP_TYPE_VALUE_IN_KEY) {
            // TODO We maybe already have the VALUE TABLE from the original join
            if (!mapKey) {
                AbstractClassMetaData valCmd = mapmd.getValueClassMetaData(clr);
                String valMappedBy = mmd.getValueMetaData().getMappedBy();
                mapping = ((DatastoreClass) sqlTbl.getTable()).getMemberMapping(valMappedBy);
                if (valCmd != null) {
                    // Join to value table
                    DatastoreClass valueTable = storeMgr.getDatastoreClass(mapmd.getValueType(), clr);
                    sqlTbl = stmt.join(getDefaultJoinTypeForNavigation(), sqlMapping.table, mapping, valueTable, null, valueTable.getIdMapping(), null, null, true);
                    mapping = valueTable.getIdMapping();
                }
            }
        }
        sqlMapping = new SQLTableMapping(sqlTbl, cmd, mapping);
    }
    while (iter.hasNext()) {
        String component = iter.next();
        // fully-qualified primary name
        primaryName += "." + component;
        // Derive SQLTableMapping for this component
        SQLTableMapping sqlMappingNew = getSQLTableMappingForAlias(primaryName);
        if (sqlMappingNew == null) {
            // Table not present for this primary
            AbstractMemberMetaData mmd = cmd.getMetaDataForMember(component);
            if (mmd == null) {
                // Not valid member name
                throw new NucleusUserException(Localiser.msg("021062", component, cmd.getFullClassName()));
            } else if (mmd.getPersistenceModifier() != FieldPersistenceModifier.PERSISTENT) {
                throw new NucleusUserException("Field " + mmd.getFullFieldName() + " is not marked as persistent so cannot be queried");
            }
            RelationType relationType = mmd.getRelationType(clr);
            // Find the table and the mapping for this field in the table
            SQLTable sqlTbl = null;
            if (mapping instanceof EmbeddedMapping) {
                // Embedded into the current table
                sqlTbl = sqlMapping.table;
                mapping = ((EmbeddedMapping) mapping).getJavaTypeMapping(component);
            } else if (mapping instanceof PersistableMapping && cmd.isEmbeddedOnly()) {
                // JPA EmbeddedId into current table
                sqlTbl = sqlMapping.table;
                JavaTypeMapping[] subMappings = ((PersistableMapping) mapping).getJavaTypeMapping();
                if (subMappings.length == 1 && subMappings[0] instanceof EmbeddedPCMapping) {
                    mapping = ((EmbeddedPCMapping) subMappings[0]).getJavaTypeMapping(component);
                } else {
                // TODO What situation is this?
                }
            } else {
                DatastoreClass table = storeMgr.getDatastoreClass(cmd.getFullClassName(), clr);
                if (table == null) {
                    if (cmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.COMPLETE_TABLE && candidateCmd.getFullClassName().equals(cmd.getFullClassName())) {
                        // Special case of a candidate having no table of its own and using COMPLETE_TABLE, so we use the candidate class for this statement (or UNION)
                        table = storeMgr.getDatastoreClass(stmt.getCandidateClassName(), clr);
                    }
                }
                if (table == null) {
                    AbstractClassMetaData[] subCmds = storeMgr.getClassesManagingTableForClass(cmd, clr);
                    if (subCmds.length == 1) {
                        table = storeMgr.getDatastoreClass(subCmds[0].getFullClassName(), clr);
                    } else {
                        // all of UNIONs, and this primary expression refers to a mapping in each of subclass tables
                        throw new NucleusUserException("Unable to find table for primary " + primaryName + " since the class " + cmd.getFullClassName() + " is managed in multiple tables");
                    }
                }
                if (table == null) {
                    throw new NucleusUserException("Unable to find table for primary " + primaryName + ". Table for class=" + cmd.getFullClassName() + " is null : is the field correct? or using some inheritance pattern?");
                }
                mapping = table.getMemberMapping(mmd);
                sqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(theStmt, sqlMapping.table, mapping);
            }
            if (relationType == RelationType.NONE) {
                sqlMappingNew = new SQLTableMapping(sqlTbl, cmd, mapping);
                cmd = sqlMappingNew.cmd;
                setSQLTableMappingForAlias(primaryName, sqlMappingNew);
            } else if (relationType == RelationType.ONE_TO_ONE_UNI || relationType == RelationType.ONE_TO_ONE_BI) {
                if (mmd.getMappedBy() != null) {
                    // FK in other table so join to that first
                    AbstractMemberMetaData relMmd = mmd.getRelatedMemberMetaData(clr)[0];
                    if (relMmd.getAbstractClassMetaData().isEmbeddedOnly()) {
                        // Member is embedded, so keep same SQL table mapping
                        sqlMappingNew = sqlMapping;
                        cmd = relMmd.getAbstractClassMetaData();
                    } else {
                        // Member is in own table, so move to that SQL table mapping
                        DatastoreClass relTable = storeMgr.getDatastoreClass(mmd.getTypeName(), clr);
                        JavaTypeMapping relMapping = relTable.getMemberMapping(relMmd);
                        // Join to related table unless we already have the join in place
                        sqlTbl = theStmt.getTable(relTable, primaryName);
                        if (sqlTbl == null) {
                            sqlTbl = SQLStatementHelper.addJoinForOneToOneRelation(theStmt, sqlMapping.table.getTable().getIdMapping(), sqlMapping.table, relMapping, relTable, null, null, primaryName, getDefaultJoinTypeForNavigation());
                        }
                        if (iter.hasNext()) {
                            sqlMappingNew = new SQLTableMapping(sqlTbl, relMmd.getAbstractClassMetaData(), relTable.getIdMapping());
                            cmd = sqlMappingNew.cmd;
                        } else {
                            sqlMappingNew = new SQLTableMapping(sqlTbl, cmd, relTable.getIdMapping());
                            cmd = sqlMappingNew.cmd;
                        }
                    }
                } else {
                    // FK is at this side
                    if (forceJoin == null) {
                        if (!iter.hasNext()) {
                            // Further component provided, so check if we should force a join to the other side
                            if (primExpr.getParent() != null && primExpr.getParent().getOperator() == Expression.OP_CAST) {
                                // Cast and not an interface field, so do a join to the table of the persistable object
                                if (mapping instanceof ReferenceMapping) {
                                // Don't join with interface field since represents multiple implementations
                                // and the cast will be a restrict on which implementation to join to
                                } else {
                                    AbstractClassMetaData relCmd = ec.getMetaDataManager().getMetaDataForClass(mmd.getType(), clr);
                                    if (relCmd != null && !relCmd.isEmbeddedOnly()) {
                                        DatastoreClass relTable = storeMgr.getDatastoreClass(relCmd.getFullClassName(), clr);
                                        if (relTable == null) {
                                        } else {
                                            forceJoin = Boolean.TRUE;
                                        }
                                    } else {
                                        forceJoin = Boolean.TRUE;
                                    }
                                }
                            }
                        } else {
                            // TODO Add optimisation to omit join if the FK is at this side and only selecting PK of the related object
                            if (iter.hasNext()) {
                                // Peek ahead to see if just selecting "id" of the related (i.e candidate.related.id with related FK in candidate table, so don't join)
                                String next = iter.next();
                                if (!iter.hasNext()) {
                                    AbstractClassMetaData relCmd = storeMgr.getMetaDataManager().getMetaDataForClass(mmd.getType(), clr);
                                    if (relCmd != null) {
                                        AbstractMemberMetaData mmdOfRelCmd = relCmd.getMetaDataForMember(next);
                                        if (mmdOfRelCmd != null && mmdOfRelCmd.isPrimaryKey() && relCmd.getNoOfPrimaryKeyMembers() == 1 && !storeMgr.getMetaDataManager().isClassPersistable(mmdOfRelCmd.getTypeName())) {
                                            // We have something like "a.b.id" and have the FK to the "B" table in the "A" table, so just refer to A.FK rather than joining and using B.ID
                                            NucleusLogger.QUERY.debug("Found implicit join to member=" + mmdOfRelCmd.getFullFieldName() + " which is PK of the other type but FK is in this table so avoiding the join");
                                            JavaTypeMapping subMapping = ((PersistableMapping) mapping).getJavaTypeMapping()[0];
                                            // Component mappings of a PersistableMapping sometimes don't have table set, so fix it
                                            subMapping.setTable(mapping.getTable());
                                            return new SQLTableMapping(sqlMapping.table, relCmd, subMapping);
                                        }
                                    }
                                }
                                iter.previous();
                            }
                        }
                    }
                    if (iter.hasNext() || Boolean.TRUE.equals(forceJoin)) {
                        AbstractClassMetaData relCmd = null;
                        JavaTypeMapping relMapping = null;
                        DatastoreClass relTable = null;
                        if (relationType == RelationType.ONE_TO_ONE_BI) {
                            AbstractMemberMetaData relMmd = mmd.getRelatedMemberMetaData(clr)[0];
                            relCmd = relMmd.getAbstractClassMetaData();
                        } else {
                            String typeName = mmd.isSingleCollection() ? mmd.getCollection().getElementType() : mmd.getTypeName();
                            relCmd = ec.getMetaDataManager().getMetaDataForClass(typeName, clr);
                        }
                        if (relCmd != null && relCmd.isEmbeddedOnly()) {
                            // Member is embedded so use same table but embedded mapping
                            sqlMappingNew = new SQLTableMapping(sqlTbl, relCmd, mapping);
                            cmd = relCmd;
                        } else {
                            // Member is in own table, so move to that SQL table mapping
                            relTable = storeMgr.getDatastoreClass(relCmd.getFullClassName(), clr);
                            if (relTable == null) {
                                // No table for the related type (subclass-table), so see if this class has a single subclass with its own table
                                Collection<String> relSubclassNames = storeMgr.getSubClassesForClass(relCmd.getFullClassName(), false, clr);
                                if (relSubclassNames != null && relSubclassNames.size() == 1) {
                                    String relSubclassName = relSubclassNames.iterator().next();
                                    relTable = storeMgr.getDatastoreClass(relSubclassName, clr);
                                    // TODO Cater for this having no table and next level yes etc
                                    if (relTable != null) {
                                        relCmd = ec.getMetaDataManager().getMetaDataForClass(relSubclassName, clr);
                                    }
                                }
                                if (relTable == null) {
                                    // No table as such, so likely using subclass-table at other side and we don't know where to join to
                                    throw new NucleusUserException("Reference to PrimaryExpression " + primExpr + " yet this needs to join relation " + mmd.getFullFieldName() + " and the other type has no table (subclass-table?). Maybe use a CAST to the appropriate subclass?");
                                }
                            }
                            relMapping = relTable.getIdMapping();
                            // Join to other table unless we already have the join in place
                            sqlTbl = theStmt.getTable(relTable, primaryName);
                            if (sqlTbl == null) {
                                sqlTbl = SQLStatementHelper.addJoinForOneToOneRelation(theStmt, mapping, sqlMapping.table, relMapping, relTable, null, null, primaryName, getDefaultJoinTypeForNavigation());
                            }
                            sqlMappingNew = new SQLTableMapping(sqlTbl, relCmd, relMapping);
                            cmd = sqlMappingNew.cmd;
                            setSQLTableMappingForAlias(primaryName, sqlMappingNew);
                        }
                    } else {
                        sqlMappingNew = new SQLTableMapping(sqlTbl, cmd, mapping);
                        cmd = sqlMappingNew.cmd;
                    // Don't register the SQLTableMapping for this alias since only using FK
                    }
                }
            } else if (relationType == RelationType.MANY_TO_ONE_BI) {
                AbstractMemberMetaData relMmd = mmd.getRelatedMemberMetaData(clr)[0];
                DatastoreClass relTable = storeMgr.getDatastoreClass(mmd.getTypeName(), clr);
                if (mmd.getJoinMetaData() != null || relMmd.getJoinMetaData() != null) {
                    // Has join table so use that
                    sqlTbl = theStmt.getTable(relTable, primaryName);
                    if (sqlTbl == null) {
                        // Join to the join table
                        CollectionTable joinTbl = (CollectionTable) storeMgr.getTable(relMmd);
                        JoinType defJoinType = getDefaultJoinTypeForNavigation();
                        if (defJoinType == JoinType.INNER_JOIN) {
                            SQLTable joinSqlTbl = theStmt.join(JoinType.INNER_JOIN, sqlMapping.table, sqlMapping.table.getTable().getIdMapping(), joinTbl, null, joinTbl.getElementMapping(), null, null, true);
                            sqlTbl = theStmt.join(JoinType.INNER_JOIN, joinSqlTbl, joinTbl.getOwnerMapping(), relTable, null, relTable.getIdMapping(), null, primaryName, true);
                        } else if (defJoinType == JoinType.LEFT_OUTER_JOIN || defJoinType == null) {
                            SQLTable joinSqlTbl = theStmt.join(JoinType.LEFT_OUTER_JOIN, sqlMapping.table, sqlMapping.table.getTable().getIdMapping(), joinTbl, null, joinTbl.getElementMapping(), null, null, true);
                            sqlTbl = theStmt.join(JoinType.LEFT_OUTER_JOIN, joinSqlTbl, joinTbl.getOwnerMapping(), relTable, null, relTable.getIdMapping(), null, primaryName, true);
                        }
                    }
                    sqlMappingNew = new SQLTableMapping(sqlTbl, relMmd.getAbstractClassMetaData(), relTable.getIdMapping());
                    cmd = sqlMappingNew.cmd;
                    setSQLTableMappingForAlias(primaryName, sqlMappingNew);
                } else {
                    // FK in this table
                    sqlTbl = theStmt.getTable(relTable, primaryName);
                    if (sqlTbl == null) {
                        if (mmd.getMappedBy() == null) {
                            // FK at this side so check for optimisations
                            if (iter.hasNext()) {
                                // Peek ahead to see if just selecting "id" of the related (i.e candidate.related.id with related FK in candidate table, so don't join)
                                String next = iter.next();
                                if (!iter.hasNext()) {
                                    AbstractClassMetaData relCmd = relMmd.getAbstractClassMetaData();
                                    AbstractMemberMetaData mmdOfRelCmd = relCmd.getMetaDataForMember(next);
                                    if (mmdOfRelCmd != null && mmdOfRelCmd.isPrimaryKey() && relCmd.getNoOfPrimaryKeyMembers() == 1 && !storeMgr.getMetaDataManager().isClassPersistable(mmdOfRelCmd.getTypeName())) {
                                        // We have something like "a.b.id" and have the FK to the "B" table in the "A" table, so just refer to A.FK rather than joining and using B.ID
                                        NucleusLogger.QUERY.debug("Found implicit join to member=" + mmdOfRelCmd.getFullFieldName() + " which is PK of the other type but FK is in this table so avoiding the join");
                                        JavaTypeMapping subMapping = ((PersistableMapping) mapping).getJavaTypeMapping()[0];
                                        // Component mappings of a PersistableMapping sometimes don't have table set, so fix it
                                        subMapping.setTable(mapping.getTable());
                                        return new SQLTableMapping(sqlMapping.table, relCmd, subMapping);
                                    }
                                }
                                iter.previous();
                            }
                        }
                        Operator op = (primExpr.getParent() != null ? primExpr.getParent().getOperator() : null);
                        if (!iter.hasNext() && (op == Expression.OP_EQ || op == Expression.OP_GT || op == Expression.OP_LT || op == Expression.OP_GTEQ || op == Expression.OP_LTEQ || op == Expression.OP_NOTEQ)) {
                            // Just return the FK mapping since in a "a.b == c.d" type expression and not needing to go further than the FK
                            sqlMappingNew = new SQLTableMapping(sqlMapping.table, relMmd.getAbstractClassMetaData(), mapping);
                        } else {
                            // Join to the related table
                            JoinType defJoinType = getDefaultJoinTypeForNavigation();
                            if (defJoinType == JoinType.INNER_JOIN) {
                                sqlTbl = theStmt.join(JoinType.INNER_JOIN, sqlMapping.table, mapping, relTable, null, relTable.getIdMapping(), null, primaryName, true);
                            } else if (defJoinType == JoinType.LEFT_OUTER_JOIN || defJoinType == null) {
                                sqlTbl = theStmt.join(JoinType.LEFT_OUTER_JOIN, sqlMapping.table, mapping, relTable, null, relTable.getIdMapping(), null, primaryName, true);
                            }
                            sqlMappingNew = new SQLTableMapping(sqlTbl, relMmd.getAbstractClassMetaData(), relTable.getIdMapping());
                            cmd = sqlMappingNew.cmd;
                            setSQLTableMappingForAlias(primaryName, sqlMappingNew);
                        }
                    } else {
                        sqlMappingNew = new SQLTableMapping(sqlTbl, relMmd.getAbstractClassMetaData(), relTable.getIdMapping());
                        cmd = sqlMappingNew.cmd;
                        setSQLTableMappingForAlias(primaryName, sqlMappingNew);
                    }
                }
            } else if (RelationType.isRelationMultiValued(relationType)) {
                // Can't reference further than a collection/map so just return its mapping here
                sqlMappingNew = new SQLTableMapping(sqlTbl, cmd, mapping);
                cmd = sqlMappingNew.cmd;
                setSQLTableMappingForAlias(primaryName, sqlMappingNew);
            }
        } else {
            cmd = sqlMappingNew.cmd;
        }
        sqlMapping = sqlMappingNew;
    }
    return sqlMapping;
}
Also used : Operator(org.datanucleus.query.expression.Expression.Operator) JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) NucleusUserException(org.datanucleus.exceptions.NucleusUserException) EmbeddedPCMapping(org.datanucleus.store.rdbms.mapping.java.EmbeddedPCMapping) JoinType(org.datanucleus.store.rdbms.sql.SQLJoin.JoinType) MapMetaData(org.datanucleus.metadata.MapMetaData) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) MapTable(org.datanucleus.store.rdbms.table.MapTable) PersistableMapping(org.datanucleus.store.rdbms.mapping.java.PersistableMapping) ReferenceMapping(org.datanucleus.store.rdbms.mapping.java.ReferenceMapping) CollectionTable(org.datanucleus.store.rdbms.table.CollectionTable) EmbeddedMapping(org.datanucleus.store.rdbms.mapping.java.EmbeddedMapping) SQLTable(org.datanucleus.store.rdbms.sql.SQLTable) RelationType(org.datanucleus.metadata.RelationType) Collection(java.util.Collection) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData)

Example 3 with EmbeddedMapping

use of org.datanucleus.store.rdbms.mapping.java.EmbeddedMapping in project datanucleus-rdbms by datanucleus.

the class ObjectExpression method is.

/**
 * An "is" (instanceOf) expression, providing a BooleanExpression whether this expression is an instanceof the provided type.
 * @param expr The expression representing the type
 * @param not Whether the operator is "!instanceof"
 * @return Whether this expression is an instance of the provided type
 */
public BooleanExpression is(SQLExpression expr, boolean not) {
    RDBMSStoreManager storeMgr = stmt.getRDBMSManager();
    ClassLoaderResolver clr = stmt.getClassLoaderResolver();
    // Extract instanceOf type
    String instanceofClassName = null;
    SQLExpression classExpr = expr;
    if (expr instanceof TypeConverterLiteral) {
        // For some reason the user has input "TYPE(field) = :param" and param is a type-converted value
        classExpr = ((TypeConverterLiteral) expr).getDelegate();
    }
    if (classExpr instanceof StringLiteral) {
        instanceofClassName = (String) ((StringLiteral) classExpr).getValue();
    } else if (classExpr instanceof CollectionLiteral) {
        // "a instanceof (b1,b2,b3)"
        CollectionLiteral typesLiteral = (CollectionLiteral) classExpr;
        Collection values = (Collection) typesLiteral.getValue();
        if (values.size() == 1) {
            Object value = values.iterator().next();
            String valueStr = null;
            if (value instanceof Class) {
                valueStr = ((Class) value).getCanonicalName();
            } else if (value instanceof String) {
                valueStr = (String) value;
            } else {
                throw new NucleusUserException("Do not support CollectionLiteral of element type " + value.getClass().getName());
            }
            return is(new StringLiteral(stmt, typesLiteral.getJavaTypeMapping(), valueStr, typesLiteral.getParameterName()), not);
        }
        List<BooleanExpression> listExp = new LinkedList<>();
        for (Object value : values) {
            String valueStr = null;
            if (value instanceof Class) {
                valueStr = ((Class) value).getCanonicalName();
            } else if (value instanceof String) {
                valueStr = (String) value;
            } else {
                throw new NucleusUserException("Do not support CollectionLiteral of element type " + value.getClass().getName());
            }
            listExp.add(is(new StringLiteral(stmt, typesLiteral.getJavaTypeMapping(), valueStr, typesLiteral.getParameterName()), false));
        }
        BooleanExpression result = null;
        for (BooleanExpression sqlExpression : listExp) {
            result = result == null ? sqlExpression : result.ior(sqlExpression);
        }
        if (result != null) {
            return not ? result.not() : result;
        }
    } else {
        throw new NucleusUserException("Do not currently support `instanceof` with class expression of type " + classExpr);
    }
    Class type = null;
    try {
        type = stmt.getQueryGenerator().resolveClass(instanceofClassName);
    } catch (ClassNotResolvedException cnre) {
        type = null;
    }
    if (type == null) {
        throw new NucleusUserException(Localiser.msg("037016", instanceofClassName));
    }
    // Extract type of member and check obvious conditions
    SQLExpressionFactory exprFactory = stmt.getSQLExpressionFactory();
    Class memberType = clr.classForName(mapping.getType());
    if (!memberType.isAssignableFrom(type) && !type.isAssignableFrom(memberType)) {
        // Member type and instanceof type are totally incompatible, so just return false
        JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
        return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, not));
    } else if (memberType == type) {
        // instanceof type is the same as the member type therefore must comply (can't store supertypes)
        JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
        return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, !not));
    }
    if (mapping instanceof EmbeddedMapping) {
        // Don't support embedded instanceof expressions
        AbstractClassMetaData fieldCmd = storeMgr.getMetaDataManager().getMetaDataForClass(mapping.getType(), clr);
        if (fieldCmd.hasDiscriminatorStrategy()) {
            // Embedded field with inheritance so add discriminator restriction
            JavaTypeMapping discMapping = ((EmbeddedMapping) mapping).getDiscriminatorMapping();
            AbstractClassMetaData typeCmd = storeMgr.getMetaDataManager().getMetaDataForClass(type, clr);
            SQLExpression discExpr = stmt.getSQLExpressionFactory().newExpression(stmt, table, discMapping);
            SQLExpression discValExpr = stmt.getSQLExpressionFactory().newLiteral(stmt, discMapping, typeCmd.getDiscriminatorValue());
            BooleanExpression typeExpr = (not ? discExpr.ne(discValExpr) : discExpr.eq(discValExpr));
            Iterator<String> subclassIter = storeMgr.getSubClassesForClass(type.getName(), true, clr).iterator();
            while (subclassIter.hasNext()) {
                String subclassName = subclassIter.next();
                AbstractClassMetaData subtypeCmd = storeMgr.getMetaDataManager().getMetaDataForClass(subclassName, clr);
                Object subtypeDiscVal = subtypeCmd.getDiscriminatorValue();
                discValExpr = stmt.getSQLExpressionFactory().newLiteral(stmt, discMapping, subtypeDiscVal);
                BooleanExpression subtypeExpr = (not ? discExpr.ne(discValExpr) : discExpr.eq(discValExpr));
                if (not) {
                    typeExpr = typeExpr.and(subtypeExpr);
                } else {
                    typeExpr = typeExpr.ior(subtypeExpr);
                }
            }
            return typeExpr;
        }
        JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
        return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, not));
    } else if (mapping instanceof PersistableMapping || mapping instanceof ReferenceMapping) {
        // Field has its own table, so join to it
        AbstractClassMetaData memberCmd = storeMgr.getMetaDataManager().getMetaDataForClass(mapping.getType(), clr);
        DatastoreClass memberTable = null;
        if (memberCmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUBCLASS_TABLE) {
            // Field is a PC class that uses "subclass-table" inheritance strategy (and so has multiple possible tables to join to)
            AbstractClassMetaData[] cmds = storeMgr.getClassesManagingTableForClass(memberCmd, clr);
            if (cmds != null) {
                // TODO Allow for all possible tables. Can we do an OR of the tables ? How ?
                if (cmds.length > 1) {
                    NucleusLogger.QUERY.warn(Localiser.msg("037006", mapping.getMemberMetaData().getFullFieldName(), cmds[0].getFullClassName()));
                }
                memberTable = storeMgr.getDatastoreClass(cmds[0].getFullClassName(), clr);
            } else {
                // No subclasses with tables to join to, so throw a user error
                throw new NucleusUserException(Localiser.msg("037005", mapping.getMemberMetaData().getFullFieldName()));
            }
        } else {
            // Class of the field will have its own table
            memberTable = storeMgr.getDatastoreClass(mapping.getType(), clr);
        }
        DiscriminatorMetaData dismd = memberTable.getDiscriminatorMetaData();
        DiscriminatorMapping discMapping = (DiscriminatorMapping) memberTable.getSurrogateMapping(SurrogateColumnType.DISCRIMINATOR, false);
        if (discMapping != null) {
            SQLTable targetSqlTbl = null;
            if (mapping.getTable() != memberTable) {
                // FK is on source table so inner join to target table (holding the discriminator)
                targetSqlTbl = stmt.getTable(memberTable, null);
                if (targetSqlTbl == null) {
                    targetSqlTbl = stmt.join(JoinType.INNER_JOIN, getSQLTable(), mapping, memberTable, null, memberTable.getIdMapping(), null, null);
                }
            } else {
                // FK is on target side and already joined
                targetSqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(stmt, getSQLTable(), discMapping);
            }
            // Add restrict to discriminator for the instanceOf type and subclasses
            SQLTable discSqlTbl = targetSqlTbl;
            BooleanExpression discExpr = null;
            if (!Modifier.isAbstract(type.getModifiers())) {
                discExpr = SQLStatementHelper.getExpressionForDiscriminatorForClass(stmt, type.getName(), dismd, discMapping, discSqlTbl, clr);
            }
            Iterator<String> subclassIter = storeMgr.getSubClassesForClass(type.getName(), true, clr).iterator();
            boolean multiplePossibles = false;
            while (subclassIter.hasNext()) {
                String subclassName = subclassIter.next();
                Class subclass = clr.classForName(subclassName);
                if (Modifier.isAbstract(subclass.getModifiers())) {
                    continue;
                }
                BooleanExpression discExprSub = SQLStatementHelper.getExpressionForDiscriminatorForClass(stmt, subclassName, dismd, discMapping, discSqlTbl, clr);
                if (discExpr != null) {
                    multiplePossibles = true;
                    discExpr = discExpr.ior(discExprSub);
                } else {
                    discExpr = discExprSub;
                }
            }
            if (multiplePossibles && discExpr != null) {
                discExpr.encloseInParentheses();
            }
            return ((not && discExpr != null) ? discExpr.not() : discExpr);
        }
        // No discriminator, so the following is likely incomplete.
        // Join to member table
        DatastoreClass table = null;
        if (memberCmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUBCLASS_TABLE) {
            // Field is a PC class that uses "subclass-table" inheritance strategy (and so has multiple possible tables to join to)
            AbstractClassMetaData[] cmds = storeMgr.getClassesManagingTableForClass(memberCmd, clr);
            if (cmds != null) {
                // TODO Allow for all possible tables. Can we do an OR of the tables ? How ?
                if (cmds.length > 1) {
                    NucleusLogger.QUERY.warn(Localiser.msg("037006", mapping.getMemberMetaData().getFullFieldName(), cmds[0].getFullClassName()));
                }
                table = storeMgr.getDatastoreClass(cmds[0].getFullClassName(), clr);
            } else {
                // No subclasses with tables to join to, so throw a user error
                throw new NucleusUserException(Localiser.msg("037005", mapping.getMemberMetaData().getFullFieldName()));
            }
        } else {
            // Class of the field will have its own table
            table = storeMgr.getDatastoreClass(mapping.getType(), clr);
        }
        if (table.managesClass(type.getName())) {
            // This type is managed in this table so must be an instance TODO Is this correct, what if member is using discrim?
            JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
            return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, !not));
        }
        if (table == stmt.getPrimaryTable().getTable()) {
            // This is member table, so just need to restrict to the instanceof type now
            JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
            if (stmt instanceof SelectStatement) {
                SelectStatement selectStmt = (SelectStatement) stmt;
                if (selectStmt.getNumberOfUnions() == 0) {
                    // No UNIONs so just check the main statement and return according to whether it is allowed
                    Class mainCandidateCls = clr.classForName(stmt.getCandidateClassName());
                    if (type.isAssignableFrom(mainCandidateCls) == not) {
                        SQLExpression returnExpr = exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, false));
                        return (BooleanExpression) returnExpr;
                    }
                    SQLExpression returnExpr = exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, true));
                    return (BooleanExpression) returnExpr;
                }
                NucleusLogger.QUERY.warn("TYPE/INSTANCEOF operator for class=" + memberCmd.getFullClassName() + " on table=" + memberTable + " for type=" + instanceofClassName + " but there is no discriminator and using UNIONs. Any subsequent handling is likely incorrect TODO");
                // a). we have unions for the member, so restrict to just the applicable unions
                // Note that this is only really valid is wanting "a instanceof SUB1".
                // It fails when we want to do "a instanceof SUB1 || a instanceof SUB2"
                // TODO What if this "OP_IS" is in the SELECT clause??? Need to update QueryToSQLMapper.compileResult
                Class mainCandidateCls = clr.classForName(stmt.getCandidateClassName());
                if (type.isAssignableFrom(mainCandidateCls) == not) {
                    SQLExpression unionClauseExpr = exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, false));
                    stmt.whereAnd((BooleanExpression) unionClauseExpr, false);
                }
                List<SelectStatement> unionStmts = selectStmt.getUnions();
                for (SelectStatement unionStmt : unionStmts) {
                    Class unionCandidateCls = clr.classForName(unionStmt.getCandidateClassName());
                    if (type.isAssignableFrom(unionCandidateCls) == not) {
                        SQLExpression unionClauseExpr = exprFactory.newLiteral(unionStmt, m, true).eq(exprFactory.newLiteral(unionStmt, m, false));
                        // TODO Avoid using whereAnd
                        unionStmt.whereAnd((BooleanExpression) unionClauseExpr, false);
                    }
                }
                // Just return true since we applied the condition direct to the unions
                SQLExpression returnExpr = exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, true));
                return (BooleanExpression) returnExpr;
            }
            // b). The member table doesn't manage the instanceof type, so do inner join to
            // the table of the instanceof to impose the instanceof condition
            DatastoreClass instanceofTable = storeMgr.getDatastoreClass(type.getName(), clr);
            stmt.join(JoinType.INNER_JOIN, this.table, this.table.getTable().getIdMapping(), instanceofTable, null, instanceofTable.getIdMapping(), null, this.table.getGroupName());
            return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, !not));
        }
        // Do inner join to this table to impose the instanceOf
        DatastoreClass instanceofTable = storeMgr.getDatastoreClass(type.getName(), clr);
        stmt.join(JoinType.INNER_JOIN, this.table, this.table.getTable().getIdMapping(), instanceofTable, null, instanceofTable.getIdMapping(), null, this.table.getGroupName());
        JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
        return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, !not));
    } else {
        // TODO Implement instanceof for other types
        throw new NucleusException("Dont currently support " + this + " instanceof " + type.getName());
    }
}
Also used : JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) SelectStatement(org.datanucleus.store.rdbms.sql.SelectStatement) ReferenceMapping(org.datanucleus.store.rdbms.mapping.java.ReferenceMapping) SQLTable(org.datanucleus.store.rdbms.sql.SQLTable) Iterator(java.util.Iterator) LinkedList(java.util.LinkedList) List(java.util.List) DiscriminatorMapping(org.datanucleus.store.rdbms.mapping.java.DiscriminatorMapping) NucleusUserException(org.datanucleus.exceptions.NucleusUserException) ClassLoaderResolver(org.datanucleus.ClassLoaderResolver) ClassNotResolvedException(org.datanucleus.exceptions.ClassNotResolvedException) RDBMSStoreManager(org.datanucleus.store.rdbms.RDBMSStoreManager) DiscriminatorMetaData(org.datanucleus.metadata.DiscriminatorMetaData) PersistableMapping(org.datanucleus.store.rdbms.mapping.java.PersistableMapping) EmbeddedMapping(org.datanucleus.store.rdbms.mapping.java.EmbeddedMapping) Collection(java.util.Collection) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) NucleusException(org.datanucleus.exceptions.NucleusException)

Example 4 with EmbeddedMapping

use of org.datanucleus.store.rdbms.mapping.java.EmbeddedMapping in project datanucleus-rdbms by datanucleus.

the class ObjectExpression method cast.

/**
 * Cast operator. Called when the query contains "(type)obj" where "obj" is this object.
 * @param expr Expression representing the type to cast to
 * @return Scalar expression representing the cast object.
 */
public SQLExpression cast(SQLExpression expr) {
    RDBMSStoreManager storeMgr = stmt.getRDBMSManager();
    ClassLoaderResolver clr = stmt.getClassLoaderResolver();
    // Extract cast type
    String castClassName = (String) ((StringLiteral) expr).getValue();
    Class type = null;
    try {
        type = stmt.getQueryGenerator().resolveClass(castClassName);
    } catch (ClassNotResolvedException cnre) {
        type = null;
    }
    if (type == null) {
        throw new NucleusUserException(Localiser.msg("037017", castClassName));
    }
    // Extract type of this object and check obvious conditions
    SQLExpressionFactory exprFactory = stmt.getSQLExpressionFactory();
    Class memberType = clr.classForName(mapping.getType());
    if (!memberType.isAssignableFrom(type) && !type.isAssignableFrom(memberType)) {
        // object type and cast type are totally incompatible, so just return false
        JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
        return exprFactory.newLiteral(stmt, m, false).eq(exprFactory.newLiteral(stmt, m, true));
    } else if (memberType == type) {
        // Just return this expression since it is already castable
        return this;
    }
    if (mapping instanceof EmbeddedMapping) {
        // Don't support embedded casts
        JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
        return exprFactory.newLiteral(stmt, m, false).eq(exprFactory.newLiteral(stmt, m, true));
    } else if (mapping instanceof ReferenceMapping) {
        // This expression will be for the table containing the reference so need to join now
        ReferenceMapping refMapping = (ReferenceMapping) mapping;
        if (refMapping.getMappingStrategy() != ReferenceMapping.PER_IMPLEMENTATION_MAPPING) {
            throw new NucleusUserException("Impossible to do cast of interface to " + type.getName() + " since interface is persisted as embedded String." + " Use per-implementation mapping to allow this query");
        }
        JavaTypeMapping[] implMappings = refMapping.getJavaTypeMapping();
        for (int i = 0; i < implMappings.length; i++) {
            Class implType = clr.classForName(implMappings[i].getType());
            if (type.isAssignableFrom(implType)) {
                DatastoreClass castTable = storeMgr.getDatastoreClass(type.getName(), clr);
                SQLTable castSqlTbl = stmt.join(JoinType.LEFT_OUTER_JOIN, table, implMappings[i], refMapping, castTable, null, castTable.getIdMapping(), null, null, null, true, null);
                return exprFactory.newExpression(stmt, castSqlTbl, castTable.getIdMapping());
            }
        }
        // No implementation matching this cast type, so return false
        NucleusLogger.QUERY.warn("Unable to process cast of interface field to " + type.getName() + " since it has no implementations that match that type");
        JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
        return exprFactory.newLiteral(stmt, m, false).eq(exprFactory.newLiteral(stmt, m, true));
    } else if (mapping instanceof PersistableMapping) {
        // Check if there is already the cast table in the current table group
        DatastoreClass castTable = storeMgr.getDatastoreClass(type.getName(), clr);
        SQLTable castSqlTbl = stmt.getTable(castTable, table.getGroupName());
        if (castSqlTbl == null) {
            // Join not present, so join to the cast table
            castSqlTbl = stmt.join(JoinType.LEFT_OUTER_JOIN, table, table.getTable().getIdMapping(), castTable, null, castTable.getIdMapping(), null, table.getGroupName());
        }
        if (castSqlTbl == table) {
            AbstractClassMetaData castCmd = storeMgr.getMetaDataManager().getMetaDataForClass(type, clr);
            if (castCmd.hasDiscriminatorStrategy()) {
                // TODO How do we handle this? If this is part of the filter then need to hang a BooleanExpression off the ObjectExpression and apply later.
                // If this is part of the result, do we just return null when this is not the right type?
                NucleusLogger.QUERY.warn(">> Currently do not support adding restriction on discriminator for table=" + table + " to " + type);
            }
        }
        // Return an expression based on the cast table
        return exprFactory.newExpression(stmt, castSqlTbl, castTable.getIdMapping());
    } else {
    // TODO Handle other casts
    }
    // TODO Implement cast (left outer join to table of type, then return new ObjectExpression)
    throw new NucleusUserException("Dont currently support ObjectExpression.cast(" + type + ")");
}
Also used : JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) NucleusUserException(org.datanucleus.exceptions.NucleusUserException) ClassLoaderResolver(org.datanucleus.ClassLoaderResolver) ClassNotResolvedException(org.datanucleus.exceptions.ClassNotResolvedException) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) RDBMSStoreManager(org.datanucleus.store.rdbms.RDBMSStoreManager) PersistableMapping(org.datanucleus.store.rdbms.mapping.java.PersistableMapping) ReferenceMapping(org.datanucleus.store.rdbms.mapping.java.ReferenceMapping) EmbeddedMapping(org.datanucleus.store.rdbms.mapping.java.EmbeddedMapping) SQLTable(org.datanucleus.store.rdbms.sql.SQLTable) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass)

Example 5 with EmbeddedMapping

use of org.datanucleus.store.rdbms.mapping.java.EmbeddedMapping in project datanucleus-rdbms by datanucleus.

the class QueryToSQLMapper method compileResult.

/**
 * Method to compile the result clause of the query into the SQLStatement.
 * Note that this also compiles queries of the candidate (no specified result), selecting the candidate field(s).
 * @param stmt SELECT statement
 */
protected void compileResult(SelectStatement stmt) {
    compileComponent = CompilationComponent.RESULT;
    boolean unionsPresent = stmt.getNumberOfUnions() > 0;
    // TODO Cater for more expression types where we have UNIONs and select each UNION separately
    if (compilation.getExprResult() != null) {
        // Select any result expressions
        Expression[] resultExprs = compilation.getExprResult();
        for (int i = 0; i < resultExprs.length; i++) {
            String alias = resultExprs[i].getAlias();
            if (alias != null && resultAliases == null) {
                resultAliases = new HashSet<>();
            }
            if (resultExprs[i] instanceof InvokeExpression || resultExprs[i] instanceof ParameterExpression || resultExprs[i] instanceof Literal) {
                // Process expressions that need no special treatment
                if (resultExprs[i] instanceof InvokeExpression) {
                    processInvokeExpression((InvokeExpression) resultExprs[i]);
                } else if (resultExprs[i] instanceof ParameterExpression) {
                    // Second argument : parameters are literals in result
                    processParameterExpression((ParameterExpression) resultExprs[i], true);
                } else {
                    processLiteral((Literal) resultExprs[i]);
                }
                SQLExpression sqlExpr = stack.pop();
                validateExpressionForResult(sqlExpr);
                int[] cols = stmt.select(sqlExpr, alias);
                StatementMappingIndex idx = new StatementMappingIndex(sqlExpr.getJavaTypeMapping());
                idx.setColumnPositions(cols);
                if (alias != null) {
                    resultAliases.add(alias);
                    idx.setColumnAlias(alias);
                }
                resultDefinition.addMappingForResultExpression(i, idx);
            } else if (resultExprs[i] instanceof PrimaryExpression) {
                PrimaryExpression primExpr = (PrimaryExpression) resultExprs[i];
                if (primExpr.getId().equals(candidateAlias)) {
                    // "this", so select fetch plan fields
                    if (unionsPresent) {
                        // Process the first union separately (in case they have TYPE/instanceof) and then handle remaining UNIONs below
                        stmt.setAllowUnions(false);
                    }
                    StatementClassMapping map = new StatementClassMapping(candidateCmd.getFullClassName(), null);
                    SQLStatementHelper.selectFetchPlanOfCandidateInStatement(stmt, map, candidateCmd, fetchPlan, 1);
                    resultDefinition.addMappingForResultExpression(i, map);
                    if (unionsPresent) {
                        // Process remaining UNIONs. Assumed that we have the same result mapping as the first UNION otherwise SQL wouldn't work anyway
                        stmt.setAllowUnions(true);
                        List<SelectStatement> unionStmts = stmt.getUnions();
                        SelectStatement originalStmt = stmt;
                        for (SelectStatement unionStmt : unionStmts) {
                            this.stmt = unionStmt;
                            unionStmt.setQueryGenerator(this);
                            unionStmt.setAllowUnions(false);
                            StatementClassMapping dummyClsMapping = new StatementClassMapping(candidateCmd.getFullClassName(), null);
                            SQLStatementHelper.selectFetchPlanOfCandidateInStatement(unionStmt, dummyClsMapping, candidateCmd, fetchPlan, 1);
                            unionStmt.setQueryGenerator(null);
                            unionStmt.setAllowUnions(true);
                        }
                        this.stmt = originalStmt;
                    }
                } else {
                    processPrimaryExpression(primExpr);
                    SQLExpression sqlExpr = stack.pop();
                    validateExpressionForResult(sqlExpr);
                    if (primExpr.getId().endsWith("#KEY") || primExpr.getId().endsWith("#VALUE")) {
                        // JPQL KEY(map) or VALUE(map), so select FetchPlan fields where persistable
                        if (sqlExpr.getJavaTypeMapping() instanceof PersistableMapping) {
                            // Method returns persistable object, so select the FetchPlan
                            String selectedType = ((PersistableMapping) sqlExpr.getJavaTypeMapping()).getType();
                            AbstractClassMetaData selectedCmd = ec.getMetaDataManager().getMetaDataForClass(selectedType, clr);
                            FetchPlanForClass fpForCmd = fetchPlan.getFetchPlanForClass(selectedCmd);
                            int[] membersToSelect = fpForCmd.getMemberNumbers();
                            ClassTable selectedTable = (ClassTable) sqlExpr.getSQLTable().getTable();
                            StatementClassMapping map = new StatementClassMapping(selectedCmd.getFullClassName(), null);
                            if (selectedCmd.getIdentityType() == IdentityType.DATASTORE) {
                                int[] cols = stmt.select(sqlExpr.getSQLTable(), selectedTable.getSurrogateMapping(SurrogateColumnType.DATASTORE_ID, false), alias);
                                StatementMappingIndex idx = new StatementMappingIndex(selectedTable.getSurrogateMapping(SurrogateColumnType.DATASTORE_ID, false));
                                idx.setColumnPositions(cols);
                                map.addMappingForMember(SurrogateColumnType.DATASTORE_ID.getFieldNumber(), idx);
                            }
                            // Select the FetchPlan members
                            for (int memberToSelect : membersToSelect) {
                                AbstractMemberMetaData selMmd = selectedCmd.getMetaDataForManagedMemberAtAbsolutePosition(memberToSelect);
                                // TODO Arbitrary penultimate argument
                                SQLStatementHelper.selectMemberOfSourceInStatement(stmt, map, fetchPlan, sqlExpr.getSQLTable(), selMmd, clr, 1, null);
                            }
                            resultDefinition.addMappingForResultExpression(i, map);
                            continue;
                        } else if (sqlExpr.getJavaTypeMapping() instanceof EmbeddedMapping) {
                            // Method returns embedded object, so select the FetchPlan
                            EmbeddedMapping embMapping = (EmbeddedMapping) sqlExpr.getJavaTypeMapping();
                            AbstractClassMetaData selectedCmd = ec.getMetaDataManager().getMetaDataForClass(embMapping.getType(), clr);
                            // Select the FetchPlan members
                            StatementClassMapping map = new StatementClassMapping(selectedCmd.getFullClassName(), null);
                            int[] membersToSelect = fetchPlan.getFetchPlanForClass(selectedCmd).getMemberNumbers();
                            for (int memberToSelect : membersToSelect) {
                                AbstractMemberMetaData selMmd = selectedCmd.getMetaDataForManagedMemberAtAbsolutePosition(memberToSelect);
                                JavaTypeMapping selMapping = embMapping.getJavaTypeMapping(selMmd.getName());
                                if (selMapping.includeInFetchStatement()) {
                                    int[] cols = stmt.select(sqlExpr.getSQLTable(), selMapping, alias);
                                    StatementMappingIndex idx = new StatementMappingIndex(selMapping);
                                    idx.setColumnPositions(cols);
                                    map.addMappingForMember(memberToSelect, idx);
                                }
                            }
                            resultDefinition.addMappingForResultExpression(i, map);
                            continue;
                        }
                    }
                    // TODO If the user selects an alias here that is joined, should maybe respect FetchPlan for that (like above for candidate)
                    // TODO Cater for use of UNIONs (e.g "complete-table" inheritance) where mapping is different in other UNION
                    // The difficulty here is that processPrimaryExpression makes use of the sqlTableByPrimary lookup, which is based on the primary statement, so
                    // it still doesn't find the right table/mapping for the UNIONed statements.
                    int[] cols = null;
                    if (sqlExpr instanceof SQLLiteral) {
                        cols = stmt.select(sqlExpr, alias);
                    } else {
                        cols = stmt.select(sqlExpr.getSQLTable(), sqlExpr.getJavaTypeMapping(), alias);
                    }
                    StatementMappingIndex idx = new StatementMappingIndex(sqlExpr.getJavaTypeMapping());
                    idx.setColumnPositions(cols);
                    if (alias != null) {
                        resultAliases.add(alias);
                        idx.setColumnAlias(alias);
                    }
                    resultDefinition.addMappingForResultExpression(i, idx);
                }
            } else if (resultExprs[i] instanceof VariableExpression) {
                // Subquery?
                processVariableExpression((VariableExpression) resultExprs[i]);
                SQLExpression sqlExpr = stack.pop();
                validateExpressionForResult(sqlExpr);
                if (sqlExpr instanceof UnboundExpression) {
                    // Variable wasn't bound in the compilation so far, so handle as cross-join
                    processUnboundExpression((UnboundExpression) sqlExpr);
                    sqlExpr = stack.pop();
                    NucleusLogger.QUERY.debug("QueryToSQL.exprResult variable was still unbound, so binding via cross-join");
                }
                int[] cols = stmt.select(sqlExpr, alias);
                StatementMappingIndex idx = new StatementMappingIndex(sqlExpr.getJavaTypeMapping());
                idx.setColumnPositions(cols);
                if (alias != null) {
                    resultAliases.add(alias);
                    idx.setColumnAlias(alias);
                }
                resultDefinition.addMappingForResultExpression(i, idx);
            } else if (resultExprs[i] instanceof TypeExpression) {
                // TYPE(identification_variable | single_valued_path_expr | input_parameter)
                TypeExpression typeExpr = (TypeExpression) resultExprs[i];
                Expression containedExpr = typeExpr.getContainedExpression();
                if (containedExpr instanceof PrimaryExpression) {
                    processPrimaryExpression((PrimaryExpression) containedExpr);
                    SQLExpression sqlExpr = stack.pop();
                    JavaTypeMapping discrimMapping = sqlExpr.getSQLTable().getTable().getSurrogateMapping(SurrogateColumnType.DISCRIMINATOR, true);
                    if (discrimMapping == null) {
                        // TODO If we have a UNIONED primary expression then it would be possible to just select the DN_TYPE from the particular union
                        throw new NucleusException("Result has call to " + typeExpr + " but contained expression has no discriminator. Not supported");
                    }
                    int[] cols = stmt.select(sqlExpr.getSQLTable(), discrimMapping, null, true);
                    StatementMappingIndex idx = new StatementMappingIndex(discrimMapping);
                    idx.setColumnPositions(cols);
                    resultDefinition.addMappingForResultExpression(i, idx);
                } else {
                    throw new NucleusException("Result has call to " + typeExpr + " but contained expression not supported");
                }
            } else if (resultExprs[i] instanceof CreatorExpression) {
                processCreatorExpression((CreatorExpression) resultExprs[i]);
                NewObjectExpression sqlExpr = (NewObjectExpression) stack.pop();
                StatementNewObjectMapping stmtMap = getStatementMappingForNewObjectExpression(sqlExpr, stmt);
                resultDefinition.addMappingForResultExpression(i, stmtMap);
            } else if (resultExprs[i] instanceof DyadicExpression || resultExprs[i] instanceof CaseExpression) {
                if (unionsPresent) {
                    // Process the first union separately (in case they have TYPE/instanceof) and then handle remaining UNIONs below
                    stmt.setAllowUnions(false);
                }
                resultExprs[i].evaluate(this);
                SQLExpression sqlExpr = stack.pop();
                int[] cols = stmt.select(sqlExpr, alias);
                StatementMappingIndex idx = new StatementMappingIndex(sqlExpr.getJavaTypeMapping());
                idx.setColumnPositions(cols);
                if (alias != null) {
                    resultAliases.add(alias);
                    idx.setColumnAlias(alias);
                }
                resultDefinition.addMappingForResultExpression(i, idx);
                if (unionsPresent) {
                    // Process remaining UNIONs. Assumed that we have the same result mapping as the first UNION otherwise SQL wouldn't work anyway
                    stmt.setAllowUnions(true);
                    List<SelectStatement> unionStmts = stmt.getUnions();
                    SelectStatement originalStmt = stmt;
                    for (SelectStatement unionStmt : unionStmts) {
                        this.stmt = unionStmt;
                        unionStmt.setQueryGenerator(this);
                        unionStmt.setAllowUnions(false);
                        resultExprs[i].evaluate(this);
                        sqlExpr = stack.pop();
                        unionStmt.select(sqlExpr, alias);
                        unionStmt.setQueryGenerator(null);
                        unionStmt.setAllowUnions(true);
                    }
                    this.stmt = originalStmt;
                }
            } else {
                throw new NucleusException("Dont currently support result clause containing expression of type " + resultExprs[i]);
            }
        }
        if (stmt.getNumberOfSelects() == 0) {
            // Nothing selected so likely the user had some "new MyClass()" expression, so select "1"
            stmt.select(exprFactory.newLiteral(stmt, storeMgr.getMappingManager().getMapping(Integer.class), 1), null);
        }
    } else {
        // Select of the candidate (no result)
        if (candidateCmd.getIdentityType() == IdentityType.NONDURABLE) {
            // Nondurable identity cases have no "id" for later fetching so get all fields now
            if (NucleusLogger.QUERY.isDebugEnabled()) {
                NucleusLogger.QUERY.debug(Localiser.msg("052520", candidateCmd.getFullClassName()));
            }
            fetchPlan.setGroup("all");
        }
        if (subclasses) {
        // TODO Check for special case of candidate+subclasses stores in same table with discriminator, so select FetchGroup of subclasses also
        }
        int maxFetchDepth = fetchPlan.getMaxFetchDepth();
        if (maxFetchDepth < 0) {
            NucleusLogger.QUERY.debug("No limit specified on query fetch so limiting to 3 levels from candidate. " + "Specify the '" + PropertyNames.PROPERTY_MAX_FETCH_DEPTH + "' to override this");
            // TODO Arbitrary
            maxFetchDepth = 3;
        }
        // This means that we then cater for a UNION using a different column name than the primary statement
        if (unionsPresent) {
            // Process the first union separately (in case they have TYPE/instanceof) and then handle remaining UNIONs below
            stmt.setAllowUnions(false);
        }
        selectFetchPlanForCandidate(stmt, resultDefinitionForClass, maxFetchDepth);
        if (unionsPresent) {
            // Process remaining UNIONs. Assumed that we have the same result mapping as the first UNION otherwise SQL wouldn't work anyway
            stmt.setAllowUnions(true);
            List<SelectStatement> unionStmts = stmt.getUnions();
            SelectStatement originalStmt = stmt;
            for (SelectStatement unionStmt : unionStmts) {
                this.stmt = unionStmt;
                unionStmt.setQueryGenerator(this);
                unionStmt.setAllowUnions(false);
                // We don't want to overwrite anything in the root StatementClassMapping
                StatementClassMapping dummyResClsMapping = new StatementClassMapping();
                selectFetchPlanForCandidate(unionStmt, dummyResClsMapping, maxFetchDepth);
                unionStmt.setQueryGenerator(null);
                unionStmt.setAllowUnions(true);
            }
            this.stmt = originalStmt;
        }
    }
    compileComponent = null;
}
Also used : SQLExpression(org.datanucleus.store.rdbms.sql.expression.SQLExpression) PrimaryExpression(org.datanucleus.query.expression.PrimaryExpression) FetchPlanForClass(org.datanucleus.FetchPlanForClass) JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) SQLLiteral(org.datanucleus.store.rdbms.sql.expression.SQLLiteral) UnboundExpression(org.datanucleus.store.rdbms.sql.expression.UnboundExpression) CreatorExpression(org.datanucleus.query.expression.CreatorExpression) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) CaseExpression(org.datanucleus.query.expression.CaseExpression) NewObjectExpression(org.datanucleus.store.rdbms.sql.expression.NewObjectExpression) SelectStatement(org.datanucleus.store.rdbms.sql.SelectStatement) TemporalLiteral(org.datanucleus.store.rdbms.sql.expression.TemporalLiteral) SQLLiteral(org.datanucleus.store.rdbms.sql.expression.SQLLiteral) ParameterLiteral(org.datanucleus.store.rdbms.sql.expression.ParameterLiteral) BooleanLiteral(org.datanucleus.store.rdbms.sql.expression.BooleanLiteral) IntegerLiteral(org.datanucleus.store.rdbms.sql.expression.IntegerLiteral) Literal(org.datanucleus.query.expression.Literal) NullLiteral(org.datanucleus.store.rdbms.sql.expression.NullLiteral) ArrayList(java.util.ArrayList) List(java.util.List) InvokeExpression(org.datanucleus.query.expression.InvokeExpression) TypeExpression(org.datanucleus.query.expression.TypeExpression) VariableExpression(org.datanucleus.query.expression.VariableExpression) DyadicExpression(org.datanucleus.query.expression.DyadicExpression) PersistableMapping(org.datanucleus.store.rdbms.mapping.java.PersistableMapping) CaseExpression(org.datanucleus.query.expression.CaseExpression) BooleanSubqueryExpression(org.datanucleus.store.rdbms.sql.expression.BooleanSubqueryExpression) StringExpression(org.datanucleus.store.rdbms.sql.expression.StringExpression) JoinExpression(org.datanucleus.query.expression.JoinExpression) NumericSubqueryExpression(org.datanucleus.store.rdbms.sql.expression.NumericSubqueryExpression) StringSubqueryExpression(org.datanucleus.store.rdbms.sql.expression.StringSubqueryExpression) ClassExpression(org.datanucleus.query.expression.ClassExpression) InvokeExpression(org.datanucleus.query.expression.InvokeExpression) MapExpression(org.datanucleus.store.rdbms.sql.expression.MapExpression) SubqueryExpression(org.datanucleus.query.expression.SubqueryExpression) NewObjectExpression(org.datanucleus.store.rdbms.sql.expression.NewObjectExpression) TemporalSubqueryExpression(org.datanucleus.store.rdbms.sql.expression.TemporalSubqueryExpression) BooleanExpression(org.datanucleus.store.rdbms.sql.expression.BooleanExpression) OrderExpression(org.datanucleus.query.expression.OrderExpression) PrimaryExpression(org.datanucleus.query.expression.PrimaryExpression) SQLExpression(org.datanucleus.store.rdbms.sql.expression.SQLExpression) UnboundExpression(org.datanucleus.store.rdbms.sql.expression.UnboundExpression) TemporalExpression(org.datanucleus.store.rdbms.sql.expression.TemporalExpression) ArrayExpression(org.datanucleus.query.expression.ArrayExpression) ResultAliasExpression(org.datanucleus.store.rdbms.sql.expression.ResultAliasExpression) CreatorExpression(org.datanucleus.query.expression.CreatorExpression) Expression(org.datanucleus.query.expression.Expression) TypeExpression(org.datanucleus.query.expression.TypeExpression) NumericExpression(org.datanucleus.store.rdbms.sql.expression.NumericExpression) CollectionExpression(org.datanucleus.store.rdbms.sql.expression.CollectionExpression) DyadicExpression(org.datanucleus.query.expression.DyadicExpression) ParameterExpression(org.datanucleus.query.expression.ParameterExpression) ColumnExpression(org.datanucleus.store.rdbms.sql.expression.ColumnExpression) VariableExpression(org.datanucleus.query.expression.VariableExpression) EmbeddedMapping(org.datanucleus.store.rdbms.mapping.java.EmbeddedMapping) ClassTable(org.datanucleus.store.rdbms.table.ClassTable) ParameterExpression(org.datanucleus.query.expression.ParameterExpression) NucleusException(org.datanucleus.exceptions.NucleusException) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData)

Aggregations

AbstractClassMetaData (org.datanucleus.metadata.AbstractClassMetaData)7 EmbeddedMapping (org.datanucleus.store.rdbms.mapping.java.EmbeddedMapping)7 JavaTypeMapping (org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping)7 PersistableMapping (org.datanucleus.store.rdbms.mapping.java.PersistableMapping)6 AbstractMemberMetaData (org.datanucleus.metadata.AbstractMemberMetaData)5 NucleusException (org.datanucleus.exceptions.NucleusException)4 NucleusUserException (org.datanucleus.exceptions.NucleusUserException)4 SQLTable (org.datanucleus.store.rdbms.sql.SQLTable)4 DatastoreClass (org.datanucleus.store.rdbms.table.DatastoreClass)4 FetchPlanForClass (org.datanucleus.FetchPlanForClass)3 DyadicExpression (org.datanucleus.query.expression.DyadicExpression)3 InvokeExpression (org.datanucleus.query.expression.InvokeExpression)3 Literal (org.datanucleus.query.expression.Literal)3 ParameterExpression (org.datanucleus.query.expression.ParameterExpression)3 PrimaryExpression (org.datanucleus.query.expression.PrimaryExpression)3 VariableExpression (org.datanucleus.query.expression.VariableExpression)3 ReferenceMapping (org.datanucleus.store.rdbms.mapping.java.ReferenceMapping)3 SelectStatement (org.datanucleus.store.rdbms.sql.SelectStatement)3 BooleanExpression (org.datanucleus.store.rdbms.sql.expression.BooleanExpression)3 BooleanLiteral (org.datanucleus.store.rdbms.sql.expression.BooleanLiteral)3