Search in sources :

Example 6 with AbstractClassMetaData

use of org.datanucleus.metadata.AbstractClassMetaData in project datanucleus-rdbms by datanucleus.

the class QueryToSQLMapper method bindVariable.

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

Example 7 with AbstractClassMetaData

use of org.datanucleus.metadata.AbstractClassMetaData 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 8 with AbstractClassMetaData

use of org.datanucleus.metadata.AbstractClassMetaData in project datanucleus-rdbms by datanucleus.

the class QueryToSQLMapper method getDiscriminatorValuesForCastClass.

private Object[] getDiscriminatorValuesForCastClass(AbstractClassMetaData cmd) {
    // Restrict discriminator on cast type to be the type+subclasses
    Collection<String> castSubclassNames = storeMgr.getSubClassesForClass(cmd.getFullClassName(), true, clr);
    Object[] castDiscrimValues = new Object[1 + (castSubclassNames != null ? castSubclassNames.size() : 0)];
    int discNo = 0;
    castDiscrimValues[discNo++] = cmd.getDiscriminatorValue();
    if (castSubclassNames != null && !castSubclassNames.isEmpty()) {
        for (String castSubClassName : castSubclassNames) {
            AbstractClassMetaData castSubCmd = storeMgr.getMetaDataManager().getMetaDataForClass(castSubClassName, clr);
            castDiscrimValues[discNo++] = castSubCmd.getDiscriminatorValue();
        }
    }
    return castDiscrimValues;
}
Also used : AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData)

Example 9 with AbstractClassMetaData

use of org.datanucleus.metadata.AbstractClassMetaData in project datanucleus-rdbms by datanucleus.

the class QueryToSQLMapper method processParameterExpression.

/**
 * Method to process a parameter expression.
 * The optional argument controls whether we should create this as a parameter or as a literal (i.e the param value is known etc).
 * If the parameter doesn't have its value defined then returns ParameterLiteral otherwise we get an XXXLiteral of the (declared) type of the parameter
 * @param expr The ParameterExpression
 * @param asLiteral Whether to create a SQLLiteral rather than a parameter literal
 * @return The processed expression
 */
protected Object processParameterExpression(ParameterExpression expr, boolean asLiteral) {
    if (compileComponent == CompilationComponent.ORDERING || compileComponent == CompilationComponent.RESULT) {
        // All JDBC drivers I know don't allow parameters in the order-by, or update clause
        // Note that we also don't allow parameters in result clause since SQLStatement squashes all SELECT expression to a String so losing info about params
        asLiteral = true;
    } else if (compileComponent == CompilationComponent.UPDATE && processingCase && !storeMgr.getDatastoreAdapter().supportsOption(DatastoreAdapter.PARAMETER_IN_CASE_IN_UPDATE_CLAUSE)) {
        // This database doesn't support parameters within a CASE expression in the UPDATE clause, so process as a literal
        asLiteral = true;
    }
    if (expr.getPosition() >= 0) {
        if (paramNameByPosition == null) {
            paramNameByPosition = new HashMap<>();
        }
        paramNameByPosition.put(Integer.valueOf(expr.getPosition()), expr.getId());
    }
    // Find the parameter value if supplied
    Object paramValue = null;
    boolean paramValueSet = false;
    if (parameters != null && parameters.size() > 0) {
        // Check if the parameter has a value
        if (parameters.containsKey(expr.getId())) {
            // Named parameter
            paramValue = parameters.get(expr.getId());
            paramValueSet = true;
        } else if (parameterValueByName != null && parameterValueByName.containsKey(expr.getId())) {
            // Positional parameter, but already encountered
            paramValue = parameterValueByName.get(expr.getId());
            paramValueSet = true;
        } else {
            // Positional parameter, not yet encountered
            int position = positionalParamNumber;
            if (positionalParamNumber < 0) {
                position = 0;
            }
            if (parameters.containsKey(Integer.valueOf(position))) {
                paramValue = parameters.get(Integer.valueOf(position));
                paramValueSet = true;
                positionalParamNumber = position + 1;
                if (parameterValueByName == null) {
                    parameterValueByName = new HashMap<>();
                }
                parameterValueByName.put(expr.getId(), paramValue);
            }
        }
    }
    // Find the type to use for the parameter
    JavaTypeMapping m = paramMappingForName.get(expr.getId());
    if (m == null) {
        // Try to determine from provided parameter value or from symbol table (declared type)
        if (paramValue != null) {
            if (!storeMgr.getMetaDataManager().isClassPersistable(paramValue.getClass().getName()) && !paramValue.getClass().isArray() && !paramValue.getClass().isInterface() && !Collection.class.isAssignableFrom(paramValue.getClass()) && !Map.class.isAssignableFrom(paramValue.getClass()) && !storeMgr.getNucleusContext().getTypeManager().isSupportedSecondClassType(paramValue.getClass().getName())) {
                // Test for this being the "id" of a persistable object
                // Persistable/array/interface/collection/map/simple cannot be an object "id"
                String className = storeMgr.getClassNameForObjectID(paramValue, clr, ec);
                if (className != null) {
                    // Identity for persistable class
                    AbstractClassMetaData cmd = storeMgr.getMetaDataManager().getMetaDataForClass(className, clr);
                    if (cmd.getIdentityType() == IdentityType.APPLICATION) {
                        Class cls = clr.classForName(className);
                        m = exprFactory.getMappingForType(cls, false);
                        m = new PersistableIdMapping((PersistableMapping) m);
                    }
                }
            }
            if (m == null) {
                // Use the type of the input parameter value
                try {
                    m = exprFactory.getMappingForType(paramValue.getClass(), false);
                    if (m instanceof TypeConverterMapping && expr.getSymbol().getValueType() != null && expr.getSymbol().getValueType() != m.getJavaType()) {
                        // This is because if we have a parameter of type "ZoneInfo" it needs to use TimeZone since we have the TypeConverter for that
                        try {
                            m = exprFactory.getMappingForType(expr.getSymbol().getValueType(), false);
                        } catch (NucleusUserException nue) {
                        }
                    }
                } catch (NucleusUserException nue) {
                    // Maybe it needs a TypeConverter so try with the (declared) symbol type of this parameter
                    m = exprFactory.getMappingForType(expr.getSymbol().getValueType(), false);
                }
            }
            if (expr.getSymbol() != null && expr.getSymbol().getValueType() != null) {
                if (!QueryUtils.queryParameterTypesAreCompatible(expr.getSymbol().getValueType(), paramValue.getClass())) {
                    throw new QueryCompilerSyntaxException(Localiser.msg("021118", expr.getId(), expr.getSymbol().getValueType().getName(), paramValue.getClass().getName()));
                }
                if (expr.getSymbol().getValueType() != paramValue.getClass()) {
                    // Mark as not precompilable since the supplied type implies a subclass of the declared type
                    setNotPrecompilable();
                }
            }
        } else if (expr.getSymbol() != null && expr.getSymbol().getValueType() != null) {
            Class valueType = expr.getSymbol().getValueType();
            if (!paramValueSet) {
                if (valueType.isInterface()) {
                    // Special case where we have an interface parameter (not set), and don't know the type, so we pick the first implementation just to get something that works
                    // This is recompiled when the parameter is provided so is just for use in "compile()"
                    String[] implNames = storeMgr.getMetaDataManager().getClassesImplementingInterface(valueType.getName(), clr);
                    if (implNames != null && implNames.length > 0) {
                        valueType = clr.classForName(implNames[0]);
                        setNotPrecompilable();
                    }
                }
            }
            // Use the declared type of the parameter (explicit params)
            m = exprFactory.getMappingForType(valueType, false);
        }
    }
    if (asLiteral && m != null && !m.representableAsStringLiteralInStatement()) {
        // Must keep this as a parameter since its String form is no good in statements
        asLiteral = false;
    }
    if (asLiteral) {
        // Parameter being represented as a literal (for whatever reason), so no longer precompilable
        if (isPrecompilable()) {
            NucleusLogger.QUERY.debug("Parameter " + expr + " is being resolved as a literal, so the query is no longer precompilable");
        }
        setNotPrecompilable();
    } else if (paramValue == null && expr.getSymbol() != null) {
        if (isPrecompilable()) {
            NucleusLogger.QUERY.debug("Parameter " + expr + " is set to null so this has to be resolved as a NullLiteral, and the query is no longer precompilable");
        }
        setNotPrecompilable();
    }
    // Create the SQLExpression for this parameter, either as value-literal or as parameter-literal
    SQLExpression sqlExpr = null;
    if (paramValueSet && paramValue == null && options.contains(OPTION_NULL_PARAM_USE_IS_NULL)) {
        // Value is set to null, but we enforce a NullLiteral for the case of null comparisons e.g we don't want "field = ?", but instead "field IS NULL"
        sqlExpr = exprFactory.newLiteral(stmt, null, null);
    } else if (asLiteral) {
        // Create a value-literal as requested
        sqlExpr = exprFactory.newLiteral(stmt, m, paramValue);
    } else {
        // Create a parameter-literal with it tied to the parameter name for later replacement in the statement
        sqlExpr = exprFactory.newLiteralParameter(stmt, m, paramValue, expr.getId());
        if (sqlExpr instanceof ParameterLiteral) {
            ((ParameterLiteral) sqlExpr).setName(expr.getId());
        }
        if (expressionForParameter == null) {
            expressionForParameter = new HashMap<>();
        }
        expressionForParameter.put(expr.getId(), sqlExpr);
        paramMappingForName.put(expr.getId(), m);
    }
    stack.push(sqlExpr);
    return sqlExpr;
}
Also used : ParameterLiteral(org.datanucleus.store.rdbms.sql.expression.ParameterLiteral) SQLExpression(org.datanucleus.store.rdbms.sql.expression.SQLExpression) HashMap(java.util.HashMap) JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) NucleusUserException(org.datanucleus.exceptions.NucleusUserException) PersistableIdMapping(org.datanucleus.store.rdbms.mapping.java.PersistableIdMapping) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) PersistableMapping(org.datanucleus.store.rdbms.mapping.java.PersistableMapping) TypeConverterMapping(org.datanucleus.store.rdbms.mapping.java.TypeConverterMapping) QueryCompilerSyntaxException(org.datanucleus.store.query.QueryCompilerSyntaxException) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) FetchPlanForClass(org.datanucleus.FetchPlanForClass) Map(java.util.Map) HashMap(java.util.HashMap)

Example 10 with AbstractClassMetaData

use of org.datanucleus.metadata.AbstractClassMetaData in project datanucleus-rdbms by datanucleus.

the class SQLQuery method getResultObjectFactoryForCandidateClass.

/**
 * Method to generate a ResultObjectFactory for converting rows of the provided ResultSet into instances of the candidate class.
 * Populates "stmtMappings".
 * @param rs The ResultSet
 * @return The ResultObjectFactory
 * @throws SQLException Thrown if an error occurs processing the ResultSet
 */
protected ResultObjectFactory getResultObjectFactoryForCandidateClass(ResultSet rs) throws SQLException {
    ClassLoaderResolver clr = ec.getClassLoaderResolver();
    RDBMSStoreManager storeMgr = (RDBMSStoreManager) getStoreManager();
    DatastoreAdapter dba = storeMgr.getDatastoreAdapter();
    // Create an index listing for ALL (fetchable) fields in the result class.
    final AbstractClassMetaData candidateCmd = ec.getMetaDataManager().getMetaDataForClass(candidateClass, clr);
    int fieldCount = candidateCmd.getNoOfManagedMembers() + candidateCmd.getNoOfInheritedManagedMembers();
    // Map of field numbers keyed by the column name
    Map columnFieldNumberMap = new HashMap();
    stmtMappings = new StatementMappingIndex[fieldCount];
    DatastoreClass tbl = storeMgr.getDatastoreClass(candidateClass.getName(), clr);
    for (int fieldNumber = 0; fieldNumber < fieldCount; ++fieldNumber) {
        AbstractMemberMetaData mmd = candidateCmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
        String fieldName = mmd.getName();
        Class fieldType = mmd.getType();
        JavaTypeMapping m = null;
        if (mmd.getPersistenceModifier() != FieldPersistenceModifier.NONE) {
            if (tbl != null) {
                // Get the field mapping from the candidate table
                m = tbl.getMemberMapping(mmd);
            } else {
                // Fall back to generating a mapping for this type - does this ever happen?
                m = storeMgr.getMappingManager().getMappingWithDatastoreMapping(fieldType, false, false, clr);
            }
            if (m.includeInFetchStatement()) {
                // Set mapping for this field since it can potentially be returned from a fetch
                String columnName = null;
                if (mmd.getColumnMetaData() != null && mmd.getColumnMetaData().length > 0) {
                    for (int colNum = 0; colNum < mmd.getColumnMetaData().length; colNum++) {
                        columnName = mmd.getColumnMetaData()[colNum].getName();
                        columnFieldNumberMap.put(columnName, Integer.valueOf(fieldNumber));
                    }
                } else {
                    columnName = storeMgr.getIdentifierFactory().newColumnIdentifier(fieldName, ec.getNucleusContext().getTypeManager().isDefaultEmbeddedType(fieldType), FieldRole.ROLE_NONE, false).getName();
                    columnFieldNumberMap.put(columnName, Integer.valueOf(fieldNumber));
                }
            } else {
            // Don't put anything in this position (field has no column in the result set)
            }
        } else {
        // Don't put anything in this position (field has no column in the result set)
        }
        stmtMappings[fieldNumber] = new StatementMappingIndex(m);
    }
    if (columnFieldNumberMap.size() == 0) {
        // None of the fields in the class have columns in the datastore table!
        throw new NucleusUserException(Localiser.msg("059030", candidateClass.getName())).setFatal();
    }
    // Generate id column field information for later checking the id is present
    DatastoreClass table = storeMgr.getDatastoreClass(candidateClass.getName(), clr);
    if (table == null) {
        AbstractClassMetaData[] cmds = storeMgr.getClassesManagingTableForClass(candidateCmd, clr);
        if (cmds != null && cmds.length == 1) {
            table = storeMgr.getDatastoreClass(cmds[0].getFullClassName(), clr);
        } else {
            throw new NucleusUserException("SQL query specified with class " + candidateClass.getName() + " but this doesn't have its own table, or is mapped to multiple tables. Unsupported");
        }
    }
    PersistableMapping idMapping = (PersistableMapping) table.getIdMapping();
    String[] idColNames = new String[idMapping.getNumberOfDatastoreMappings()];
    for (int i = 0; i < idMapping.getNumberOfDatastoreMappings(); i++) {
        idColNames[i] = idMapping.getDatastoreMapping(i).getColumn().getIdentifier().toString();
    }
    // Generate discriminator information for later checking it is present
    JavaTypeMapping discrimMapping = table.getSurrogateMapping(SurrogateColumnType.DISCRIMINATOR, false);
    String discrimColName = discrimMapping != null ? discrimMapping.getDatastoreMapping(0).getColumn().getIdentifier().toString() : null;
    // Generate version information for later checking it is present
    JavaTypeMapping versionMapping = table.getSurrogateMapping(SurrogateColumnType.VERSION, false);
    String versionColName = versionMapping != null ? versionMapping.getDatastoreMapping(0).getColumn().getIdentifier().toString() : null;
    // Go through the fields of the ResultSet and map to the required fields in the candidate
    ResultSetMetaData rsmd = rs.getMetaData();
    // TODO We put nothing in this, so what is it for?!
    HashSet remainingColumnNames = new HashSet(columnFieldNumberMap.size());
    int colCount = rsmd.getColumnCount();
    int[] datastoreIndex = null;
    int[] versionIndex = null;
    int[] discrimIndex = null;
    int[] matchedFieldNumbers = new int[colCount];
    int fieldNumberPosition = 0;
    for (int colNum = 1; colNum <= colCount; ++colNum) {
        String colName = rsmd.getColumnName(colNum);
        // Find the field for this column
        int fieldNumber = -1;
        Integer fieldNum = (Integer) columnFieldNumberMap.get(colName);
        if (fieldNum == null) {
            // Try column name in lowercase
            fieldNum = (Integer) columnFieldNumberMap.get(colName.toLowerCase());
            if (fieldNum == null) {
                // Try column name in UPPERCASE
                fieldNum = (Integer) columnFieldNumberMap.get(colName.toUpperCase());
            }
        }
        if (fieldNum != null) {
            fieldNumber = fieldNum.intValue();
        }
        if (fieldNumber >= 0) {
            int[] exprIndices = null;
            if (stmtMappings[fieldNumber].getColumnPositions() != null) {
                exprIndices = new int[stmtMappings[fieldNumber].getColumnPositions().length + 1];
                for (int i = 0; i < stmtMappings[fieldNumber].getColumnPositions().length; i++) {
                    exprIndices[i] = stmtMappings[fieldNumber].getColumnPositions()[i];
                }
                exprIndices[exprIndices.length - 1] = colNum;
            } else {
                exprIndices = new int[] { colNum };
            }
            stmtMappings[fieldNumber].setColumnPositions(exprIndices);
            remainingColumnNames.remove(colName);
            matchedFieldNumbers[fieldNumberPosition++] = fieldNumber;
        }
        if (discrimColName != null && colName.equals(discrimColName)) {
            // Identify the location of the discriminator column
            discrimIndex = new int[1];
            discrimIndex[0] = colNum;
        }
        if (versionColName != null && colName.equals(versionColName)) {
            // Identify the location of the version column
            versionIndex = new int[1];
            versionIndex[0] = colNum;
        }
        if (candidateCmd.getIdentityType() == IdentityType.DATASTORE) {
            // Check for existence of id column, allowing for any RDBMS using quoted identifiers
            if (columnNamesAreTheSame(dba, idColNames[0], colName)) {
                datastoreIndex = new int[1];
                datastoreIndex[0] = colNum;
            }
        }
    }
    // Set the field numbers found to match what we really have
    int[] fieldNumbers = new int[fieldNumberPosition];
    for (int i = 0; i < fieldNumberPosition; i++) {
        fieldNumbers[i] = matchedFieldNumbers[i];
    }
    StatementClassMapping mappingDefinition = new StatementClassMapping();
    for (int i = 0; i < fieldNumbers.length; i++) {
        mappingDefinition.addMappingForMember(fieldNumbers[i], stmtMappings[fieldNumbers[i]]);
    }
    if (datastoreIndex != null) {
        StatementMappingIndex datastoreMappingIdx = new StatementMappingIndex(table.getSurrogateMapping(SurrogateColumnType.DATASTORE_ID, false));
        datastoreMappingIdx.setColumnPositions(datastoreIndex);
        mappingDefinition.addMappingForMember(SurrogateColumnType.DATASTORE_ID.getFieldNumber(), datastoreMappingIdx);
    }
    if (discrimIndex != null) {
        StatementMappingIndex discrimMappingIdx = new StatementMappingIndex(table.getSurrogateMapping(SurrogateColumnType.DISCRIMINATOR, true));
        discrimMappingIdx.setColumnPositions(discrimIndex);
        mappingDefinition.addMappingForMember(SurrogateColumnType.DISCRIMINATOR.getFieldNumber(), discrimMappingIdx);
    }
    if (versionIndex != null) {
        StatementMappingIndex versionMappingIdx = new StatementMappingIndex(table.getSurrogateMapping(SurrogateColumnType.VERSION, true));
        versionMappingIdx.setColumnPositions(versionIndex);
        mappingDefinition.addMappingForMember(SurrogateColumnType.VERSION.getFieldNumber(), versionMappingIdx);
    }
    return new PersistentClassROF(ec, rs, ignoreCache, mappingDefinition, candidateCmd, getCandidateClass());
}
Also used : HashMap(java.util.HashMap) JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) NucleusUserException(org.datanucleus.exceptions.NucleusUserException) ClassLoaderResolver(org.datanucleus.ClassLoaderResolver) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) RDBMSStoreManager(org.datanucleus.store.rdbms.RDBMSStoreManager) ResultSetMetaData(java.sql.ResultSetMetaData) PersistableMapping(org.datanucleus.store.rdbms.mapping.java.PersistableMapping) DatastoreAdapter(org.datanucleus.store.rdbms.adapter.DatastoreAdapter) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) HashMap(java.util.HashMap) Map(java.util.Map) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData) HashSet(java.util.HashSet)

Aggregations

AbstractClassMetaData (org.datanucleus.metadata.AbstractClassMetaData)204 AbstractMemberMetaData (org.datanucleus.metadata.AbstractMemberMetaData)90 JavaTypeMapping (org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping)69 ClassLoaderResolver (org.datanucleus.ClassLoaderResolver)59 DatastoreClass (org.datanucleus.store.rdbms.table.DatastoreClass)55 NucleusUserException (org.datanucleus.exceptions.NucleusUserException)42 RDBMSStoreManager (org.datanucleus.store.rdbms.RDBMSStoreManager)42 NucleusException (org.datanucleus.exceptions.NucleusException)37 MetaDataManager (org.datanucleus.metadata.MetaDataManager)37 ArrayList (java.util.ArrayList)31 SQLExpression (org.datanucleus.store.rdbms.sql.expression.SQLExpression)26 SQLTable (org.datanucleus.store.rdbms.sql.SQLTable)23 SQLExpressionFactory (org.datanucleus.store.rdbms.sql.expression.SQLExpressionFactory)22 ClassLoaderResolverImpl (org.datanucleus.ClassLoaderResolverImpl)19 MapTable (org.datanucleus.store.rdbms.table.MapTable)18 List (java.util.List)16 ObjectProvider (org.datanucleus.state.ObjectProvider)16 PersistableMapping (org.datanucleus.store.rdbms.mapping.java.PersistableMapping)16 SelectStatement (org.datanucleus.store.rdbms.sql.SelectStatement)16 Iterator (java.util.Iterator)15