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