/* (non-Javadoc) * @see org.datanucleus.store.rdbms.sql.method.SQLMethod#getExpression(org.datanucleus.store.rdbms.sql.expression.SQLExpression, java.util.List) */ public SQLExpression getExpression(SQLExpression expr, List args) { if (expr != null) { throw new NucleusException(LOCALISER.msg("060002", getFunctionName(), expr)); } else if (args == null || args.size() != 1) { throw new NucleusException(getFunctionName() + " is only supported with a single argument"); } if (stmt.getQueryGenerator().getCompilationComponent() == CompilationComponent.RESULT || stmt.getQueryGenerator().getCompilationComponent() == CompilationComponent.HAVING) { // FUNC(argExpr) JavaTypeMapping m = null; if (args.get(0) instanceof SQLExpression) { // Use same java type as the argument SQLExpression argExpr = (SQLExpression) args.get(0); m = getMappingForClass(argExpr.getJavaTypeMapping().getJavaType()); if (args.get(0) instanceof TemporalExpression) { return new AggregateTemporalExpression(stmt, m, getFunctionName(), args); } else { return new AggregateNumericExpression(stmt, m, getFunctionName(), args); } } else { // What is coming through here? // Fallback to the type for this aggregate TODO Allow for temporal types m = getMappingForClass(double.class); return new AggregateNumericExpression(stmt, m, getFunctionName(), args); } } else { // Handle as Subquery "SELECT AVG(expr) FROM tbl" SQLExpression argExpr = (SQLExpression) args.get(0); SQLStatement subStmt = new SQLStatement( stmt, stmt.getRDBMSManager(), argExpr.getSQLTable().getTable(), argExpr.getSQLTable().getAlias(), null); subStmt.setClassLoaderResolver(clr); JavaTypeMapping mapping = stmt.getRDBMSManager() .getMappingManager() .getMappingWithDatastoreMapping(String.class, false, false, clr); String aggregateString = getFunctionName() + "(" + argExpr.toSQLText() + ")"; SQLExpression aggExpr = exprFactory.newLiteral(subStmt, mapping, aggregateString); ((StringLiteral) aggExpr).generateStatementWithoutQuotes(); subStmt.select(aggExpr, null); JavaTypeMapping subqMapping = exprFactory.getMappingForType(Integer.class, false); SQLExpression subqExpr = null; if (argExpr instanceof TemporalExpression) { subqExpr = new TemporalSubqueryExpression(stmt, subStmt); } else { subqExpr = new NumericSubqueryExpression(stmt, subStmt); } subqExpr.setJavaTypeMapping(subqMapping); return subqExpr; } }
/** * Method to return a statement selecting the candidate table(s) required to cover all possible * types for this candidates inheritance strategy. * * @param storeMgr RDBMS StoreManager * @param parentStmt Parent statement (if there is one) * @param cmd Metadata for the class * @param clsMapping Mapping for the results of the statement * @param ec ExecutionContext * @param candidateCls Candidate class * @param subclasses Whether to create a statement for subclasses of the candidate too * @param result The result clause * @param candidateAlias alias for the candidate (if any) * @param candidateTableGroupName TableGroup name for the candidate (if any) * @return The SQLStatement * @throws NucleusException if there are no tables for concrete classes in this query (hence would * return null) */ public static SQLStatement getStatementForCandidates( RDBMSStoreManager storeMgr, SQLStatement parentStmt, AbstractClassMetaData cmd, StatementClassMapping clsMapping, ExecutionContext ec, Class candidateCls, boolean subclasses, String result, String candidateAlias, String candidateTableGroupName) { SQLStatement stmt = null; DatastoreIdentifier candidateAliasId = null; if (candidateAlias != null) { candidateAliasId = storeMgr.getIdentifierFactory().newTableIdentifier(candidateAlias); } ClassLoaderResolver clr = ec.getClassLoaderResolver(); List<DatastoreClass> candidateTables = new ArrayList<DatastoreClass>(); if (cmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.COMPLETE_TABLE) { DatastoreClass candidateTable = storeMgr.getDatastoreClass(cmd.getFullClassName(), clr); if (candidateTable != null) { candidateTables.add(candidateTable); } if (subclasses) { Collection<String> subclassNames = storeMgr.getSubClassesForClass(cmd.getFullClassName(), subclasses, clr); if (subclassNames != null) { Iterator<String> subclassIter = subclassNames.iterator(); while (subclassIter.hasNext()) { String subclassName = subclassIter.next(); DatastoreClass tbl = storeMgr.getDatastoreClass(subclassName, clr); if (tbl != null) { candidateTables.add(tbl); } } } } Iterator<DatastoreClass> iter = candidateTables.iterator(); int maxClassNameLength = cmd.getFullClassName().length(); while (iter.hasNext()) { DatastoreClass cls = iter.next(); String className = cls.getType(); if (className.length() > maxClassNameLength) { maxClassNameLength = className.length(); } } iter = candidateTables.iterator(); while (iter.hasNext()) { DatastoreClass cls = iter.next(); SQLStatement tblStmt = new SQLStatement(parentStmt, storeMgr, cls, candidateAliasId, candidateTableGroupName); tblStmt.setClassLoaderResolver(clr); tblStmt.setCandidateClassName(cls.getType()); // Add SELECT of dummy column accessible as "NUCLEUS_TYPE" containing the classname JavaTypeMapping m = storeMgr.getMappingManager().getMapping(String.class); String nuctypeName = cls.getType(); if (maxClassNameLength > nuctypeName.length()) { nuctypeName = StringUtils.leftAlignedPaddedString(nuctypeName, maxClassNameLength); } StringLiteral lit = new StringLiteral(tblStmt, m, nuctypeName, null); tblStmt.select(lit, UnionStatementGenerator.NUC_TYPE_COLUMN); if (stmt == null) { stmt = tblStmt; } else { stmt.union(tblStmt); } } if (clsMapping != null) { clsMapping.setNucleusTypeColumnName(UnionStatementGenerator.NUC_TYPE_COLUMN); } } else { // "new-table", "superclass-table", "subclass-table" List<Class> candidateClasses = new ArrayList<Class>(); if (ClassUtils.isReferenceType(candidateCls)) { // Persistent interface, so find all persistent implementations String[] clsNames = storeMgr .getNucleusContext() .getMetaDataManager() .getClassesImplementingInterface(candidateCls.getName(), clr); for (int i = 0; i < clsNames.length; i++) { Class cls = clr.classForName(clsNames[i]); DatastoreClass table = storeMgr.getDatastoreClass(clsNames[i], clr); candidateClasses.add(cls); candidateTables.add(table); AbstractClassMetaData implCmd = storeMgr.getNucleusContext().getMetaDataManager().getMetaDataForClass(cls, clr); if (implCmd.getIdentityType() != cmd.getIdentityType()) { throw new NucleusUserException( "You are querying an interface (" + cmd.getFullClassName() + ") " + "yet one of its implementations (" + implCmd.getFullClassName() + ") " + " uses a different identity type!"); } else if (cmd.getIdentityType() == IdentityType.APPLICATION) { if (cmd.getPKMemberPositions().length != implCmd.getPKMemberPositions().length) { throw new NucleusUserException( "You are querying an interface (" + cmd.getFullClassName() + ") " + "yet one of its implementations (" + implCmd.getFullClassName() + ") " + " has a different number of PK members!"); } } } } else { DatastoreClass candidateTable = storeMgr.getDatastoreClass(cmd.getFullClassName(), clr); if (candidateTable != null) { // Candidate has own table candidateClasses.add(candidateCls); candidateTables.add(candidateTable); } else { // Candidate stored in subclass tables AbstractClassMetaData[] cmds = storeMgr.getClassesManagingTableForClass(cmd, clr); if (cmds != null && cmds.length > 0) { for (int i = 0; i < cmds.length; i++) { DatastoreClass table = storeMgr.getDatastoreClass(cmds[i].getFullClassName(), clr); Class cls = clr.classForName(cmds[i].getFullClassName()); candidateClasses.add(cls); candidateTables.add(table); } } else { throw new UnsupportedOperationException( "No tables for query of " + cmd.getFullClassName()); } } } for (int i = 0; i < candidateTables.size(); i++) { DatastoreClass tbl = candidateTables.get(i); Class cls = candidateClasses.get(i); StatementGenerator stmtGen = null; if (tbl.getDiscriminatorMapping(true) != null || QueryUtils.resultHasOnlyAggregates(result)) { // Either has a discriminator, or only selecting aggregates so need single select stmtGen = new DiscriminatorStatementGenerator( storeMgr, clr, cls, subclasses, candidateAliasId, candidateTableGroupName); stmtGen.setOption(StatementGenerator.OPTION_RESTRICT_DISCRIM); } else { stmtGen = new UnionStatementGenerator( storeMgr, clr, cls, subclasses, candidateAliasId, candidateTableGroupName); if (result == null) { // Returning one row per candidate so include distinguisher column stmtGen.setOption(StatementGenerator.OPTION_SELECT_NUCLEUS_TYPE); clsMapping.setNucleusTypeColumnName(UnionStatementGenerator.NUC_TYPE_COLUMN); } } stmtGen.setParentStatement(parentStmt); SQLStatement tblStmt = stmtGen.getStatement(); if (stmt == null) { stmt = tblStmt; } else { stmt.union(tblStmt); } } } return stmt; }
/** * Method to return an expression for Map.containsValue using a subquery "EXISTS". This is for use * when there are "!contains" or "OR" operations in the filter. Creates the following SQL, * * <ul> * <li><b>Map using join table</b> * <pre> * SELECT 1 FROM JOIN_TBL A0_SUB * WHERE A0_SUB.JOIN_OWN_ID = A0.ID AND A0_SUB.JOIN_VAL_ID = {valExpr} * </pre> * <li><b>Map with key stored in value</b> * <pre> * SELECT 1 FROM VAL_TABLE A0_SUB INNER JOIN KEY_TBL B0 ON ... * WHERE B0.JOIN_OWN_ID = A0.ID AND A0_SUB.ID = {valExpr} * </pre> * <li><b>Map of value stored in key</b> * <pre> * SELECT 1 FROM VAL_TABLE A0_SUB * WHERE A0_SUB.OWN_ID = A0.ID AND A0_SUB.ID = {valExpr} * </pre> * </ul> * * and returns a BooleanSubqueryExpression ("EXISTS (subquery)") * * @param mapExpr Map expression * @param valExpr Expression for the value * @return Contains expression */ protected SQLExpression containsAsSubquery(MapExpression mapExpr, SQLExpression valExpr) { boolean valIsUnbound = (valExpr instanceof UnboundExpression); String varName = null; if (valIsUnbound) { varName = ((UnboundExpression) valExpr).getVariableName(); NucleusLogger.QUERY.debug( "map.containsValue binding unbound variable " + varName + " using SUBQUERY"); // TODO What if the variable is declared as a subtype, handle this see // CollectionContainsMethod } RDBMSStoreManager storeMgr = stmt.getRDBMSManager(); MetaDataManager mmgr = storeMgr.getMetaDataManager(); AbstractMemberMetaData mmd = mapExpr.getJavaTypeMapping().getMemberMetaData(); AbstractClassMetaData valCmd = mmd.getMap().getValueClassMetaData(clr, mmgr); MapTable joinTbl = (MapTable) storeMgr.getTable(mmd); SQLStatement subStmt = null; if (mmd.getMap().getMapType() == MapType.MAP_TYPE_JOIN) { // JoinTable Map if (valCmd == null) { // Map<?, Non-PC> subStmt = new SQLStatement(stmt, storeMgr, joinTbl, null, null); subStmt.setClassLoaderResolver(clr); JavaTypeMapping oneMapping = storeMgr.getMappingManager().getMapping(Integer.class); subStmt.select(exprFactory.newLiteral(subStmt, oneMapping, 1), null); // Restrict to map owner JavaTypeMapping ownerMapping = ((JoinTable) joinTbl).getOwnerMapping(); SQLExpression ownerExpr = exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), ownerMapping); SQLExpression ownerIdExpr = exprFactory.newExpression( stmt, mapExpr.getSQLTable(), mapExpr.getSQLTable().getTable().getIdMapping()); subStmt.whereAnd(ownerExpr.eq(ownerIdExpr), true); if (valIsUnbound) { // Bind the variable in the QueryGenerator valExpr = exprFactory.newExpression( subStmt, subStmt.getPrimaryTable(), joinTbl.getValueMapping()); stmt.getQueryGenerator() .bindVariable(varName, null, valExpr.getSQLTable(), valExpr.getJavaTypeMapping()); } else { // Add restrict to value SQLExpression valIdExpr = exprFactory.newExpression( subStmt, subStmt.getPrimaryTable(), joinTbl.getValueMapping()); subStmt.whereAnd(valIdExpr.eq(valExpr), true); } } else { // Map<?, PC> DatastoreClass valTbl = storeMgr.getDatastoreClass(mmd.getMap().getValueType(), clr); subStmt = new SQLStatement(stmt, storeMgr, valTbl, null, null); subStmt.setClassLoaderResolver(clr); JavaTypeMapping oneMapping = storeMgr.getMappingManager().getMapping(Integer.class); subStmt.select(exprFactory.newLiteral(subStmt, oneMapping, 1), null); // Join to join table SQLTable joinSqlTbl = subStmt.innerJoin( subStmt.getPrimaryTable(), valTbl.getIdMapping(), joinTbl, null, joinTbl.getValueMapping(), null, null); // Restrict to map owner JavaTypeMapping ownerMapping = joinTbl.getOwnerMapping(); SQLExpression ownerExpr = exprFactory.newExpression(subStmt, joinSqlTbl, ownerMapping); SQLExpression ownerIdExpr = exprFactory.newExpression( stmt, mapExpr.getSQLTable(), mapExpr.getSQLTable().getTable().getIdMapping()); subStmt.whereAnd(ownerExpr.eq(ownerIdExpr), true); if (valIsUnbound) { // Bind the variable in the QueryGenerator valExpr = exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), valTbl.getIdMapping()); stmt.getQueryGenerator() .bindVariable(varName, valCmd, valExpr.getSQLTable(), valExpr.getJavaTypeMapping()); } else { // Add restrict to value SQLExpression valIdExpr = exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), valTbl.getIdMapping()); subStmt.whereAnd(valIdExpr.eq(valExpr), true); } } } else if (mmd.getMap().getMapType() == MapType.MAP_TYPE_KEY_IN_VALUE) { // Key stored in value table DatastoreClass valTbl = storeMgr.getDatastoreClass(mmd.getMap().getValueType(), clr); JavaTypeMapping ownerMapping = null; if (mmd.getMappedBy() != null) { ownerMapping = valTbl.getMemberMapping(valCmd.getMetaDataForMember(mmd.getMappedBy())); } else { ownerMapping = valTbl.getExternalMapping(mmd, MappingConsumer.MAPPING_TYPE_EXTERNAL_FK); } subStmt = new SQLStatement(stmt, storeMgr, valTbl, null, null); subStmt.setClassLoaderResolver(clr); JavaTypeMapping oneMapping = storeMgr.getMappingManager().getMapping(Integer.class); subStmt.select(exprFactory.newLiteral(subStmt, oneMapping, 1), null); // Restrict to map owner (on value table) SQLExpression ownerExpr = exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), ownerMapping); SQLExpression ownerIdExpr = exprFactory.newExpression( stmt, mapExpr.getSQLTable(), mapExpr.getSQLTable().getTable().getIdMapping()); subStmt.whereAnd(ownerExpr.eq(ownerIdExpr), true); if (valIsUnbound) { // Bind the variable in the QueryGenerator valExpr = exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), valTbl.getIdMapping()); stmt.getQueryGenerator() .bindVariable(varName, valCmd, valExpr.getSQLTable(), valExpr.getJavaTypeMapping()); } else { // Add restrict to value JavaTypeMapping valMapping = valTbl.getIdMapping(); SQLExpression valIdExpr = exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), valMapping); subStmt.whereAnd(valIdExpr.eq(valExpr), true); } } else if (mmd.getMap().getMapType() == MapType.MAP_TYPE_VALUE_IN_KEY) { AbstractClassMetaData keyCmd = mmd.getMap().getKeyClassMetaData(clr, mmgr); DatastoreClass keyTbl = storeMgr.getDatastoreClass(mmd.getMap().getKeyType(), clr); JavaTypeMapping ownerMapping = null; if (mmd.getMappedBy() != null) { ownerMapping = keyTbl.getMemberMapping(keyCmd.getMetaDataForMember(mmd.getMappedBy())); } else { ownerMapping = keyTbl.getExternalMapping(mmd, MappingConsumer.MAPPING_TYPE_EXTERNAL_FK); } AbstractMemberMetaData keyValMmd = keyCmd.getMetaDataForMember(mmd.getValueMetaData().getMappedBy()); if (valCmd == null) { subStmt = new SQLStatement(stmt, storeMgr, keyTbl, null, null); subStmt.setClassLoaderResolver(clr); JavaTypeMapping oneMapping = storeMgr.getMappingManager().getMapping(Integer.class); subStmt.select(exprFactory.newLiteral(subStmt, oneMapping, 1), null); // Restrict to map owner (on key table) SQLExpression ownerExpr = exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), ownerMapping); SQLExpression ownerIdExpr = exprFactory.newExpression( stmt, mapExpr.getSQLTable(), mapExpr.getSQLTable().getTable().getIdMapping()); subStmt.whereAnd(ownerExpr.eq(ownerIdExpr), true); if (valIsUnbound) { // Bind the variable in the QueryGenerator valExpr = exprFactory.newExpression( subStmt, subStmt.getPrimaryTable(), keyTbl.getMemberMapping(keyValMmd)); stmt.getQueryGenerator() .bindVariable(varName, null, valExpr.getSQLTable(), valExpr.getJavaTypeMapping()); } else { // Add restrict to value JavaTypeMapping valMapping = keyTbl.getMemberMapping(keyValMmd); SQLExpression valIdExpr = exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), valMapping); subStmt.whereAnd(valIdExpr.eq(valExpr), true); } } else { DatastoreClass valTbl = storeMgr.getDatastoreClass(mmd.getMap().getValueType(), clr); subStmt = new SQLStatement(stmt, storeMgr, valTbl, null, null); subStmt.setClassLoaderResolver(clr); JavaTypeMapping oneMapping = storeMgr.getMappingManager().getMapping(Integer.class); subStmt.select(exprFactory.newLiteral(subStmt, oneMapping, 1), null); // Join to key table SQLTable keySqlTbl = subStmt.innerJoin( subStmt.getPrimaryTable(), valTbl.getIdMapping(), keyTbl, null, keyTbl.getMemberMapping(keyValMmd), null, null); // Restrict to map owner (on key table) SQLExpression ownerExpr = exprFactory.newExpression(subStmt, keySqlTbl, ownerMapping); SQLExpression ownerIdExpr = exprFactory.newExpression( stmt, mapExpr.getSQLTable(), mapExpr.getSQLTable().getTable().getIdMapping()); subStmt.whereAnd(ownerExpr.eq(ownerIdExpr), true); if (valIsUnbound) { // Bind the variable in the QueryGenerator valExpr = exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), valTbl.getIdMapping()); stmt.getQueryGenerator() .bindVariable(varName, valCmd, valExpr.getSQLTable(), valExpr.getJavaTypeMapping()); } else { // Add restrict to value SQLExpression valIdExpr = exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), valTbl.getIdMapping()); subStmt.whereAnd(valIdExpr.eq(valExpr), true); } } } return new BooleanSubqueryExpression(stmt, "EXISTS", subStmt); }