001/* 002// This software is subject to the terms of the Eclipse Public License v1.0 003// Agreement, available at the following URL: 004// http://www.eclipse.org/legal/epl-v10.html. 005// You must accept the terms of that agreement to use this software. 006// 007// Copyright (C) 2007-2013 Pentaho 008// All Rights Reserved. 009*/ 010package mondrian.rolap.agg; 011 012import mondrian.rolap.*; 013import mondrian.rolap.sql.SqlQuery; 014 015import java.util.*; 016 017/** 018 * Predicate which is the intersection of a list of predicates. It evaluates to 019 * true if all of the predicates evaluate to true. 020 * 021 * @see OrPredicate 022 * 023 * @author jhyde 024 */ 025public class AndPredicate extends ListPredicate { 026 027 public AndPredicate(List<StarPredicate> predicateList) { 028 super(predicateList); 029 } 030 031 public boolean evaluate(List<Object> valueList) { 032 // NOTE: If we know that every predicate in the list is a 033 // ValueColumnPredicate, we could optimize the evaluate method by 034 // building a value list at construction time. But it's a tradeoff, 035 // considering the extra time and space required. 036 for (StarPredicate childPredicate : children) { 037 if (childPredicate.evaluate(valueList)) { 038 return true; 039 } 040 } 041 return false; 042 } 043 044 public StarPredicate and(StarPredicate predicate) { 045 if (predicate instanceof AndPredicate) { 046 ListPredicate that = (ListPredicate) predicate; 047 final List<StarPredicate> list = 048 new ArrayList<StarPredicate>(children); 049 list.addAll(that.children); 050 return new AndPredicate(list); 051 } else { 052 final List<StarPredicate> list = 053 new ArrayList<StarPredicate>(children); 054 list.add(predicate); 055 return new AndPredicate(list); 056 } 057 } 058 059 060 public StarPredicate or(StarPredicate predicate) { 061 List<StarPredicate> list = new ArrayList<StarPredicate>(); 062 list.add(this); 063 list.add(predicate); 064 return new OrPredicate(list); 065 } 066 067 public BitKey checkInList(SqlQuery sqlQuery, BitKey inListLHSBitKey) { 068 // AND predicate by itself is not using IN list; when it is 069 // one of the children to an OR predicate, then using IN list 070 // is helpful. The later is checked by passing in a bitmap that 071 // represent the LHS or the IN list, i.e. the columns that are 072 // constrained by the OR. 073 074 // If the child predicates contains null values, those predicates cannot 075 // be translated as IN list; however, the rest of the child predicates 076 // might still be translated to IN. For example, neither of the two AND 077 // conditions below(part of an OR list) can be translated using IN list, 078 // covering all the levels 079 // 080 // (null, null, San Francisco) 081 // (null, null, New York) 082 // 083 // However, after extracting the null part, they can be translated to: 084 // 085 // (country is null AND state is null AND city IN ("San Fancisco", "New 086 // York")) 087 // 088 // which is still more compact than the default AND/OR translation: 089 // 090 // (country is null AND state is null AND city = "San Francisco") OR 091 // (country is null AND state is null AND city = "New York") 092 // 093 // This method will mark all the columns that can be translated as part 094 // of IN list, so that similar predicates can be grouped together to 095 // form partial IN list sql. By default, all columns constrained by this 096 // predicates can be part of an IN list. 097 // 098 // This is very similar to the logic in 099 // SqlConstraintUtil.generateMultiValueInExpr(). The only difference 100 // being that the predicates here are all "flattened" so the hierarchy 101 // information is no longer available to guide the grouping of 102 // predicates with common parents. So some optimization possible in 103 // generateMultiValueInExpr() is not tried here, as they require 104 // implementing "longest common prefix" algorithm which is an overkill. 105 BitKey inListRHSBitKey = inListLHSBitKey.copy(); 106 107 if (!getConstrainedColumnBitKey().equals(inListLHSBitKey) 108 || (children.size() > 1 109 && !sqlQuery.getDialect().supportsMultiValueInExpr())) 110 { 111 inListRHSBitKey.clear(); 112 } else { 113 for (StarPredicate predicate : children) { 114 // If any predicate requires comparison to null value, cannot 115 // use IN list for this predicate. 116 if (predicate instanceof ValueColumnPredicate) { 117 ValueColumnPredicate columnPred = 118 ((ValueColumnPredicate) predicate); 119 if (columnPred.getValue() == RolapUtil.sqlNullValue) { 120 // This column predicate cannot be translated to IN 121 inListRHSBitKey.clear( 122 columnPred.getConstrainedColumn().getBitPosition()); 123 } 124 // else do nothing because this column predicate can be 125 // translated to IN 126 } else { 127 inListRHSBitKey.clear(); 128 break; 129 } 130 } 131 } 132 return inListRHSBitKey; 133 } 134 135 /** 136 * Generate value list for this predicate to be used in an IN-list 137 * sql predicate. 138 * 139 * The values in a multi-column IN list predicates are generated in the 140 * same order, based on the bit position from the columnBitKey. 141 * 142 */ 143 public void toInListSql( 144 SqlQuery sqlQuery, 145 StringBuilder buf, 146 BitKey inListRHSBitKey) 147 { 148 boolean firstValue = true; 149 final boolean multiValueInList = children.size() > 1; 150 if (multiValueInList) { 151 buf.append("("); 152 } 153 // Arranging children according to the bit position. This is required 154 // as RHS of IN list needs to list the column values in the same order. 155 Set<ValueColumnPredicate> sortedPredicates = 156 new TreeSet<ValueColumnPredicate>(); 157 158 for (StarPredicate predicate : children) { 159 // inListPossible() checks guarantees that predicate is of type 160 // ValueColumnPredicate 161 assert predicate instanceof ValueColumnPredicate; 162 if (inListRHSBitKey.get( 163 ((ValueColumnPredicate) predicate).getConstrainedColumn() 164 .getBitPosition())) 165 { 166 sortedPredicates.add((ValueColumnPredicate)predicate); 167 } 168 } 169 170 for (ValueColumnPredicate predicate : sortedPredicates) { 171 if (firstValue) { 172 firstValue = false; 173 } else { 174 buf.append(", "); 175 } 176 sqlQuery.getDialect().quote( 177 buf, predicate.getValue(), 178 predicate.getConstrainedColumn().getDatatype()); 179 } 180 if (multiValueInList) { 181 buf.append(")"); 182 } 183 } 184 185 protected String getOp() { 186 return "and"; 187 } 188} 189 190// End AndPredicate.java