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