/* (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<SQLExpression> args) { if (args == null || args.size() == 0 || args.size() > 1) { throw new NucleusException(Localiser.msg("060016", "containsValue", "MapExpression", 1)); } MapExpression mapExpr = (MapExpression) expr; SQLExpression valExpr = args.get(0); if (valExpr.isParameter()) { // Value is a parameter so make sure its type is set AbstractMemberMetaData mmd = mapExpr.getJavaTypeMapping().getMemberMetaData(); if (mmd != null && mmd.getMap() != null) { Class valCls = stmt.getQueryGenerator() .getClassLoaderResolver() .classForName(mmd.getMap().getValueType()); stmt.getQueryGenerator().bindParameter(valExpr.getParameterName(), valCls); } } if (mapExpr instanceof MapLiteral) { MapLiteral lit = (MapLiteral) mapExpr; Map map = (Map) lit.getValue(); if (map == null || map.size() == 0) { return new BooleanLiteral(stmt, expr.getJavaTypeMapping(), Boolean.FALSE); } // TODO If valExpr is a parameter and mapExpr is derived from a parameter ? MapValueLiteral mapValueLiteral = lit.getValueLiteral(); BooleanExpression bExpr = null; List<SQLExpression> elementExprs = mapValueLiteral.getValueExpressions(); for (int i = 0; i < elementExprs.size(); i++) { if (bExpr == null) { bExpr = (elementExprs.get(i)).eq(valExpr); } else { bExpr = bExpr.ior((elementExprs.get(i)).eq(valExpr)); } } if (bExpr != null) { bExpr.encloseInParentheses(); } return bExpr; } if (stmt.getQueryGenerator().getCompilationComponent() == CompilationComponent.FILTER) { boolean needsSubquery = getNeedsSubquery(); // TODO Check if *this* "containsValue" is negated, not any of them (and remove above check) if (needsSubquery) { NucleusLogger.QUERY.debug( "map.containsValue on " + mapExpr + "(" + valExpr + ") using SUBQUERY"); return containsAsSubquery(mapExpr, valExpr); } NucleusLogger.QUERY.debug( "map.containsValue on " + mapExpr + "(" + valExpr + ") using INNERJOIN"); return containsAsInnerJoin(mapExpr, valExpr); } return containsAsSubquery(mapExpr, valExpr); }
/** * Method to return if it is valid to select the specified mapping for the specified statement for * this datastore adapter. Sometimes, dependent on the type of the column(s), and what other * components are present in the statement, it may be invalid to select the mapping. This * implementation returns true, so override in database-specific subclass as required. * * @param stmt The statement * @param m The mapping that we want to select * @return Whether it is valid */ public boolean validToSelectMappingInStatement(SelectStatement stmt, JavaTypeMapping m) { if (m.getNumberOfDatastoreMappings() <= 0) { return true; } for (int i = 0; i < m.getNumberOfDatastoreMappings(); i++) { Column col = m.getDatastoreMapping(i).getColumn(); if (col.getJdbcType() == JdbcType.CLOB || col.getJdbcType() == JdbcType.BLOB) { // "The ... data type cannot be selected as DISTINCT because it is not comparable." if (stmt.isDistinct()) { NucleusLogger.QUERY.debug( "Not selecting " + m + " since is for BLOB/CLOB and using DISTINCT"); return false; } else if (stmt.getNumberOfUnions() > 0) { NucleusLogger.QUERY.debug( "Not selecting " + m + " since is for BLOB/CLOB and using UNION"); return false; } } } return true; }
/** * 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); }
/** * Method to return an expression for Map.containsValue using INNER JOIN to the element. This is * only for use when there are no "!containsValue" and no "OR" operations. Creates SQL by adding * INNER JOIN to the join table (where it exists), and also to the value table adding an AND * condition on the value (with value of the valueExpr). Returns a BooleanExpression "TRUE" (since * the INNER JOIN will guarantee if the value is contained of not). * * @param mapExpr Map expression * @param valExpr Expression for the value * @return Contains expression */ protected SQLExpression containsAsInnerJoin(MapExpression mapExpr, SQLExpression valExpr) { boolean valIsUnbound = (valExpr instanceof UnboundExpression); String varName = null; String valAlias = null; if (valIsUnbound) { varName = ((UnboundExpression) valExpr).getVariableName(); NucleusLogger.QUERY.debug( "map.containsValue(" + valExpr + ") binding unbound variable " + varName + " using INNER JOIN"); // TODO What if the variable is declared as a subtype, handle this see // CollectionContainsMethod } else if (!stmt.getQueryGenerator().hasExplicitJoins()) { JoinType joinType = stmt.getJoinTypeForTable(valExpr.getSQLTable()); if (joinType == JoinType.CROSS_JOIN) { // Value is currently joined via CROSS JOIN, so remove it (and use INNER JOIN below) valAlias = stmt.removeCrossJoin(valExpr.getSQLTable()); valIsUnbound = true; NucleusLogger.QUERY.debug( "map.containsValue(" + valExpr + ") was previously bound as CROSS JOIN but changing to INNER JOIN"); } // TODO If owner is joined via CROSS JOIN and value is already present then remove CROSS JOIN // and join via INNER JOIN } RDBMSStoreManager storeMgr = stmt.getRDBMSManager(); MetaDataManager mmgr = storeMgr.getMetaDataManager(); AbstractMemberMetaData mmd = mapExpr.getJavaTypeMapping().getMemberMetaData(); AbstractClassMetaData valCmd = mmd.getMap().getValueClassMetaData(clr, mmgr); if (mmd.getMap().getMapType() == MapType.MAP_TYPE_JOIN) { // Map formed in join table - add join to join table, then to value table (if present) MapTable mapTbl = (MapTable) storeMgr.getTable(mmd); SQLTable joinSqlTbl = stmt.innerJoin( mapExpr.getSQLTable(), mapExpr.getSQLTable().getTable().getIdMapping(), mapTbl, null, mapTbl.getOwnerMapping(), null, null); if (valCmd != null) { if (valIsUnbound) { DatastoreClass valTbl = storeMgr.getDatastoreClass(valCmd.getFullClassName(), clr); SQLTable valSqlTbl = stmt.innerJoin( joinSqlTbl, mapTbl.getValueMapping(), valTbl, valAlias, valTbl.getIdMapping(), null, null); // Bind the variable in the QueryGenerator valExpr = exprFactory.newExpression(stmt, valSqlTbl, valSqlTbl.getTable().getIdMapping()); stmt.getQueryGenerator() .bindVariable(varName, valCmd, valExpr.getSQLTable(), valExpr.getJavaTypeMapping()); } else { // Add restrict to value SQLExpression valIdExpr = exprFactory.newExpression(stmt, joinSqlTbl, mapTbl.getValueMapping()); stmt.whereAnd(valIdExpr.eq(valExpr), true); } } else { if (valIsUnbound) { // Bind the variable in the QueryGenerator valExpr = exprFactory.newExpression(stmt, joinSqlTbl, mapTbl.getValueMapping()); stmt.getQueryGenerator() .bindVariable(varName, null, valExpr.getSQLTable(), valExpr.getJavaTypeMapping()); } else { // Add restrict to value SQLExpression valIdExpr = exprFactory.newExpression(stmt, joinSqlTbl, mapTbl.getValueMapping()); stmt.whereAnd(valIdExpr.eq(valExpr), true); } } } else if (mmd.getMap().getMapType() == MapType.MAP_TYPE_KEY_IN_VALUE) { // Map formed in value table - add join to value table DatastoreClass valTbl = storeMgr.getDatastoreClass(valCmd.getFullClassName(), 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); } SQLTable valSqlTbl = stmt.innerJoin( mapExpr.getSQLTable(), mapExpr.getSQLTable().getTable().getIdMapping(), valTbl, valAlias, ownerMapping, null, null); if (valIsUnbound) { // Bind the variable in the QueryGenerator valExpr = exprFactory.newExpression(stmt, valSqlTbl, valTbl.getIdMapping()); stmt.getQueryGenerator() .bindVariable(varName, valCmd, valExpr.getSQLTable(), valExpr.getJavaTypeMapping()); } else { // Add restrict to value SQLExpression valIdExpr = exprFactory.newExpression(stmt, valSqlTbl, valTbl.getIdMapping()); stmt.whereAnd(valIdExpr.eq(valExpr), true); } } else if (mmd.getMap().getMapType() == MapType.MAP_TYPE_VALUE_IN_KEY) { // Map formed in key table - add join to key table then to value table AbstractClassMetaData keyCmd = mmd.getMap().getKeyClassMetaData(clr, mmgr); DatastoreClass keyTbl = storeMgr.getDatastoreClass(keyCmd.getFullClassName(), clr); AbstractMemberMetaData keyValMmd = keyCmd.getMetaDataForMember(mmd.getValueMetaData().getMappedBy()); JavaTypeMapping ownerMapping = null; if (mmd.getMappedBy() != null) { ownerMapping = keyTbl.getMemberMapping(keyCmd.getMetaDataForMember(mmd.getMappedBy())); } else { ownerMapping = keyTbl.getExternalMapping(mmd, MappingConsumer.MAPPING_TYPE_EXTERNAL_FK); } SQLTable keySqlTbl = stmt.innerJoin( mapExpr.getSQLTable(), mapExpr.getSQLTable().getTable().getIdMapping(), keyTbl, null, ownerMapping, null, null); if (valCmd != null) { DatastoreClass valTbl = storeMgr.getDatastoreClass(valCmd.getFullClassName(), clr); SQLTable valSqlTbl = stmt.innerJoin( keySqlTbl, keyTbl.getMemberMapping(keyValMmd), valTbl, valAlias, valTbl.getIdMapping(), null, null); if (valIsUnbound) { // Bind the variable in the QueryGenerator valExpr = exprFactory.newExpression(stmt, valSqlTbl, valTbl.getIdMapping()); stmt.getQueryGenerator() .bindVariable(varName, valCmd, valExpr.getSQLTable(), valExpr.getJavaTypeMapping()); } else { // Add restrict to value SQLExpression valIdExpr = exprFactory.newExpression(stmt, valSqlTbl, valTbl.getIdMapping()); stmt.whereAnd(valIdExpr.eq(valExpr), true); } } else { if (valIsUnbound) { // Bind the variable in the QueryGenerator valExpr = exprFactory.newExpression(stmt, keySqlTbl, keyTbl.getMemberMapping(keyValMmd)); stmt.getQueryGenerator() .bindVariable(varName, null, valExpr.getSQLTable(), valExpr.getJavaTypeMapping()); } else { // Add restrict to value SQLExpression valIdExpr = exprFactory.newExpression(stmt, keySqlTbl, keyTbl.getMemberMapping(keyValMmd)); stmt.whereAnd(valIdExpr.eq(valExpr), true); } } } JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true); return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, true)); }