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) 2006-2011 Pentaho 008// All Rights Reserved. 009*/ 010package mondrian.olap.fun; 011 012import mondrian.calc.*; 013import mondrian.calc.impl.*; 014import mondrian.mdx.ResolvedFunCall; 015import mondrian.olap.*; 016import mondrian.server.Locus; 017 018import java.util.List; 019 020/** 021 * Definition of the <code>Filter</code> MDX function. 022 * 023 * <p>Syntax: 024 * <blockquote><code>Filter(<Set>, <Search 025 * Condition>)</code></blockquote> 026 * 027 * @author jhyde 028 * @since Mar 23, 2006 029 */ 030class FilterFunDef extends FunDefBase { 031 032 private static final String TIMING_NAME = 033 FilterFunDef.class.getSimpleName(); 034 035 static final FilterFunDef instance = new FilterFunDef(); 036 037 private FilterFunDef() { 038 super( 039 "Filter", 040 "Returns the set resulting from filtering a set based on a search condition.", 041 "fxxb"); 042 } 043 044 public Calc compileCall(final ResolvedFunCall call, ExpCompiler compiler) { 045 // Ignore the caller's priority. We prefer to return iterable, because 046 // it makes NamedSet.CurrentOrdinal work. 047 List<ResultStyle> styles = compiler.getAcceptableResultStyles(); 048 if (call.getArg(0) instanceof ResolvedFunCall 049 && ((ResolvedFunCall) call.getArg(0)).getFunName().equals("AS")) 050 { 051 styles = ResultStyle.ITERABLE_ONLY; 052 } 053 if (styles.contains(ResultStyle.ITERABLE) 054 || styles.contains(ResultStyle.ANY)) 055 { 056 return compileCallIterable(call, compiler); 057 } else if (styles.contains(ResultStyle.LIST) 058 || styles.contains(ResultStyle.MUTABLE_LIST)) 059 { 060 return compileCallList(call, compiler); 061 } else { 062 throw ResultStyleException.generate( 063 ResultStyle.ITERABLE_LIST_MUTABLELIST_ANY, 064 styles); 065 } 066 } 067 068 /** 069 * Returns an IterCalc. 070 * 071 * <p>Here we would like to get either a IterCalc or ListCalc (mutable) 072 * from the inner expression. For the IterCalc, its Iterator 073 * can be wrapped with another Iterator that filters each element. 074 * For the mutable list, remove all members that are filtered. 075 * 076 * @param call Call 077 * @param compiler Compiler 078 * @return Implementation of this function call in the Iterable result style 079 */ 080 protected IterCalc compileCallIterable( 081 final ResolvedFunCall call, 082 ExpCompiler compiler) 083 { 084 // want iterable, mutable list or immutable list in that order 085 Calc imlcalc = compiler.compileAs( 086 call.getArg(0), null, ResultStyle.ITERABLE_LIST_MUTABLELIST); 087 BooleanCalc bcalc = compiler.compileBoolean(call.getArg(1)); 088 Calc[] calcs = new Calc[] {imlcalc, bcalc}; 089 090 // check returned calc ResultStyles 091 checkIterListResultStyles(imlcalc); 092 093 if (imlcalc.getResultStyle() == ResultStyle.ITERABLE) { 094 return new IterIterCalc(call, calcs); 095 } else if (imlcalc.getResultStyle() == ResultStyle.LIST) { 096 return new ImmutableIterCalc(call, calcs); 097 } else { 098 return new MutableIterCalc(call, calcs); 099 } 100 } 101 102 private static abstract class BaseIterCalc extends AbstractIterCalc { 103 protected BaseIterCalc(ResolvedFunCall call, Calc[] calcs) { 104 super(call, calcs); 105 } 106 107 public TupleIterable evaluateIterable(Evaluator evaluator) { 108 evaluator.getTiming().markStart(TIMING_NAME); 109 try { 110 ResolvedFunCall call = (ResolvedFunCall) exp; 111 // Use a native evaluator, if more efficient. 112 // TODO: Figure this out at compile time. 113 SchemaReader schemaReader = evaluator.getSchemaReader(); 114 NativeEvaluator nativeEvaluator = 115 schemaReader.getNativeSetEvaluator( 116 call.getFunDef(), call.getArgs(), evaluator, this); 117 if (nativeEvaluator != null) { 118 return (TupleIterable) 119 nativeEvaluator.execute(ResultStyle.ITERABLE); 120 } else { 121 return makeIterable(evaluator); 122 } 123 } finally { 124 evaluator.getTiming().markEnd(TIMING_NAME); 125 } 126 } 127 128 protected abstract TupleIterable makeIterable(Evaluator evaluator); 129 130 public boolean dependsOn(Hierarchy hierarchy) { 131 return anyDependsButFirst(getCalcs(), hierarchy); 132 } 133 } 134 135 private static class MutableIterCalc extends BaseIterCalc { 136 MutableIterCalc(ResolvedFunCall call, Calc[] calcs) { 137 super(call, calcs); 138 assert calcs[0] instanceof ListCalc; 139 assert calcs[1] instanceof BooleanCalc; 140 } 141 142 protected TupleIterable makeIterable(Evaluator evaluator) { 143 evaluator.getTiming().markStart(TIMING_NAME); 144 final int savepoint = evaluator.savepoint(); 145 try { 146 Calc[] calcs = getCalcs(); 147 ListCalc lcalc = (ListCalc) calcs[0]; 148 BooleanCalc bcalc = (BooleanCalc) calcs[1]; 149 150 TupleList list = lcalc.evaluateList(evaluator); 151 152 // make list mutable; guess selectivity .5 153 TupleList result = 154 TupleCollections.createList( 155 list.getArity(), list.size() / 2); 156 evaluator.setNonEmpty(false); 157 TupleCursor cursor = list.tupleCursor(); 158 while (cursor.forward()) { 159 cursor.setContext(evaluator); 160 if (bcalc.evaluateBoolean(evaluator)) { 161 result.addCurrent(cursor); 162 } 163 } 164 return result; 165 } finally { 166 evaluator.restore(savepoint); 167 evaluator.getTiming().markEnd(TIMING_NAME); 168 } 169 } 170 } 171 172 private static class ImmutableIterCalc extends BaseIterCalc { 173 ImmutableIterCalc(ResolvedFunCall call, Calc[] calcs) { 174 super(call, calcs); 175 assert calcs[0] instanceof ListCalc; 176 assert calcs[1] instanceof BooleanCalc; 177 } 178 179 protected TupleIterable makeIterable(Evaluator evaluator) { 180 Calc[] calcs = getCalcs(); 181 ListCalc lcalc = (ListCalc) calcs[0]; 182 BooleanCalc bcalc = (BooleanCalc) calcs[1]; 183 TupleList members = lcalc.evaluateList(evaluator); 184 185 // Not mutable, must create new list 186 TupleList result = members.cloneList(members.size() / 2); 187 final int savepoint = evaluator.savepoint(); 188 try { 189 evaluator.setNonEmpty(false); 190 TupleCursor cursor = members.tupleCursor(); 191 while (cursor.forward()) { 192 cursor.setContext(evaluator); 193 if (bcalc.evaluateBoolean(evaluator)) { 194 result.addCurrent(cursor); 195 } 196 } 197 return result; 198 } finally { 199 evaluator.restore(savepoint); 200 } 201 } 202 } 203 204 private static class IterIterCalc 205 extends BaseIterCalc 206 { 207 IterIterCalc(ResolvedFunCall call, Calc[] calcs) { 208 super(call, calcs); 209 assert calcs[0] instanceof IterCalc; 210 assert calcs[1] instanceof BooleanCalc; 211 } 212 213 protected TupleIterable makeIterable(Evaluator evaluator) { 214 Calc[] calcs = getCalcs(); 215 IterCalc icalc = (IterCalc) calcs[0]; 216 final BooleanCalc bcalc = (BooleanCalc) calcs[1]; 217 218 // This does dynamics, just in time, 219 // as needed filtering 220 final TupleIterable iterable = 221 icalc.evaluateIterable(evaluator); 222 final Evaluator evaluator2 = evaluator.push(); 223 evaluator2.setNonEmpty(false); 224 return new AbstractTupleIterable(iterable.getArity()) { 225 public TupleCursor tupleCursor() { 226 return new AbstractTupleCursor(iterable.getArity()) { 227 final TupleCursor cursor = iterable.tupleCursor(); 228 229 public boolean forward() { 230 while (cursor.forward()) { 231 Locus.peek().execution.checkCancelOrTimeout(); 232 cursor.setContext(evaluator2); 233 if (bcalc.evaluateBoolean(evaluator2)) { 234 return true; 235 } 236 } 237 return false; 238 } 239 240 public List<Member> current() { 241 return cursor.current(); 242 } 243 }; 244 } 245 }; 246 } 247 } 248 249 250 /** 251 * Returns a ListCalc. 252 * 253 * @param call Call 254 * @param compiler Compiler 255 * @return Implementation of this function call in the List result style 256 */ 257 protected ListCalc compileCallList( 258 final ResolvedFunCall call, 259 ExpCompiler compiler) 260 { 261 Calc ilcalc = compiler.compileList(call.getArg(0), false); 262 BooleanCalc bcalc = compiler.compileBoolean(call.getArg(1)); 263 Calc[] calcs = new Calc[] {ilcalc, bcalc}; 264 265 // Note that all of the ListCalc's return will be mutable 266 switch (ilcalc.getResultStyle()) { 267 case LIST: 268 return new ImmutableListCalc(call, calcs); 269 case MUTABLE_LIST: 270 return new MutableListCalc(call, calcs); 271 } 272 throw ResultStyleException.generateBadType( 273 ResultStyle.MUTABLELIST_LIST, 274 ilcalc.getResultStyle()); 275 } 276 277 private static abstract class BaseListCalc extends AbstractListCalc { 278 protected BaseListCalc(ResolvedFunCall call, Calc[] calcs) { 279 super(call, calcs); 280 } 281 282 public TupleList evaluateList(Evaluator evaluator) { 283 ResolvedFunCall call = (ResolvedFunCall) exp; 284 // Use a native evaluator, if more efficient. 285 // TODO: Figure this out at compile time. 286 SchemaReader schemaReader = evaluator.getSchemaReader(); 287 NativeEvaluator nativeEvaluator = 288 schemaReader.getNativeSetEvaluator( 289 call.getFunDef(), call.getArgs(), evaluator, this); 290 if (nativeEvaluator != null) { 291 return (TupleList) nativeEvaluator.execute( 292 ResultStyle.ITERABLE); 293 } else { 294 return makeList(evaluator); 295 } 296 } 297 protected abstract TupleList makeList(Evaluator evaluator); 298 299 public boolean dependsOn(Hierarchy hierarchy) { 300 return anyDependsButFirst(getCalcs(), hierarchy); 301 } 302 } 303 304 private static class MutableListCalc extends BaseListCalc { 305 MutableListCalc(ResolvedFunCall call, Calc[] calcs) { 306 super(call, calcs); 307 assert calcs[0] instanceof ListCalc; 308 assert calcs[1] instanceof BooleanCalc; 309 } 310 311 protected TupleList makeList(Evaluator evaluator) { 312 Calc[] calcs = getCalcs(); 313 ListCalc lcalc = (ListCalc) calcs[0]; 314 BooleanCalc bcalc = (BooleanCalc) calcs[1]; 315 TupleList members0 = lcalc.evaluateList(evaluator); 316 317 // make list mutable; 318 // for capacity planning, guess selectivity = .5 319 TupleList result = members0.cloneList(members0.size() / 2); 320 final int savepoint = evaluator.savepoint(); 321 try { 322 evaluator.setNonEmpty(false); 323 final TupleCursor cursor = members0.tupleCursor(); 324 while (cursor.forward()) { 325 cursor.setContext(evaluator); 326 if (bcalc.evaluateBoolean(evaluator)) { 327 result.addCurrent(cursor); 328 } 329 } 330 return result; 331 } finally { 332 evaluator.restore(savepoint); 333 } 334 } 335 } 336 337 private static class ImmutableListCalc extends BaseListCalc { 338 ImmutableListCalc(ResolvedFunCall call, Calc[] calcs) { 339 super(call, calcs); 340 assert calcs[0] instanceof ListCalc; 341 assert calcs[1] instanceof BooleanCalc; 342 } 343 344 protected TupleList makeList(Evaluator evaluator) { 345 evaluator.getTiming().markStart(TIMING_NAME); 346 final int savepoint = evaluator.savepoint(); 347 try { 348 Calc[] calcs = getCalcs(); 349 ListCalc lcalc = (ListCalc) calcs[0]; 350 BooleanCalc bcalc = (BooleanCalc) calcs[1]; 351 TupleList members0 = lcalc.evaluateList(evaluator); 352 353 // Not mutable, must create new list; 354 // for capacity planning, guess selectivity = .5 355 TupleList result = members0.cloneList(members0.size() / 2); 356 evaluator.setNonEmpty(false); 357 final TupleCursor cursor = members0.tupleCursor(); 358 while (cursor.forward()) { 359 cursor.setContext(evaluator); 360 if (bcalc.evaluateBoolean(evaluator)) { 361 result.addCurrent(cursor); 362 } 363 } 364 return result; 365 } finally { 366 evaluator.restore(savepoint); 367 evaluator.getTiming().markEnd(TIMING_NAME); 368 } 369 } 370 } 371} 372 373// End FilterFunDef.java