Gaia
queryoptimizer_impl.h
1 /*
2  * Copyright (C) 2006-2013 Music Technology Group - Universitat Pompeu Fabra
3  *
4  * This file is part of Gaia
5  *
6  * Gaia is free software: you can redistribute it and/or modify it under
7  * the terms of the GNU Affero General Public License as published by the Free
8  * Software Foundation (FSF), either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14  * details.
15  *
16  * You should have received a copy of the Affero GNU General Public License
17  * version 3 along with this program. If not, see http://www.gnu.org/licenses/
18  */
19 
20 #include "queryoptimizer.h"
21 #include "parser/parsertypes.h"
22 
23 namespace gaia2 {
24 
25 /* Note: here's the parser types hierarchy:
26 
27 Predicate
28  |
29  |- PredValueComparison (uses 2 Values)
30  |- PredValueRange (uses 1 Value)
31  |- PredLabelComparison (uses 2 Labels)
32  |- PredLogicalOp (uses 2 Predicates)
33  |- PredNegate (uses 1 Predicate)
34  |- BooleanConstant
35  |- PredValueIsIn (uses 1 Value)
36  \- PredLabelIsIn (uses 1 Label)
37 
38 Value
39  |
40  |- ValueConstant
41  \- ValueVariable
42 
43 Label
44  |
45  |- LabelConstant
46  |- LabelVariable
47  \- LabelPointID
48 
49 */
50 
51 
52 template <typename SearchPointType, typename DataSetType>
53 parser::PredLabelIsIn* BaseQueryOptimizer<SearchPointType, DataSetType>::findPointID(parser::Predicate* pred) {
54 
55  // if pred it is, return it
56  parser::PredLabelIsIn* labelPred = dynamic_cast<parser::PredLabelIsIn*>(pred);
57  if (labelPred && dynamic_cast<parser::LabelPointID*>(labelPred->_var)) {
58  return labelPred;
59  }
60 
61  // predicate is a negation, recurse down on inner predicate
62  parser::PredNegate* negPred = dynamic_cast<parser::PredNegate*>(pred);
63  if (negPred) return findPointID(negPred->_pred);
64 
65  // predicate is a logical comparison, recurse down on inner predicates
66  parser::PredLogicalOp* logicPred = dynamic_cast<parser::PredLogicalOp*>(pred);
67  if (logicPred) {
68  parser::PredLabelIsIn* labelPred = findPointID(logicPred->_lhs);
69  if (labelPred) return labelPred;
70  return findPointID(logicPred->_rhs);
71  }
72 
73  // didn't find anything, return 0
74  return 0;
75 }
76 
77 template <typename SearchPointType, typename DataSetType>
78 template <typename PredicateType>
79 PredicateType* BaseQueryOptimizer<SearchPointType, DataSetType>::findPredicate(parser::Predicate* pred) {
80  // if we found it, return it
81  PredicateType* result = dynamic_cast<PredicateType*>(pred);
82  if (result) return result;
83 
84  // predicate is a negation, recurse down on inner predicate
85  parser::PredNegate* negPred = dynamic_cast<parser::PredNegate*>(pred);
86  if (negPred) return findPredicate<PredicateType>(negPred->_pred);
87 
88  // predicate is a logical comparison, recurse down on inner predicates
89  parser::PredLogicalOp* logicPred = dynamic_cast<parser::PredLogicalOp*>(pred);
90  if (logicPred) {
91  result = findPredicate<PredicateType>(logicPred->_lhs);
92  if (result) return result;
93  return findPredicate<PredicateType>(logicPred->_rhs);
94  }
95 
96  // didn't find anything, return 0
97  return 0;
98 }
99 
100 
101 template <typename SearchPointType, typename DataSetType>
103  parser::Predicate* swapOut,
104  parser::Predicate* swapIn) {
105  if (filter->_pred == swapOut) {
106  filter->_pred = swapIn;
107  return true;
108  }
109 
110  return swapPredicate(filter->_pred, swapOut, swapIn);
111 }
112 
113 template <typename SearchPointType, typename DataSetType>
115  parser::Predicate* swapOut,
116  parser::Predicate* swapIn) {
117  // predicate is a negation, recurse down on inner predicate
118  parser::PredNegate* negPred = dynamic_cast<parser::PredNegate*>(parent);
119  if (negPred) {
120  if (negPred->_pred == swapOut) {
121  negPred->_pred = swapIn;
122  return true;
123  }
124 
125  return swapPredicate(negPred->_pred, swapOut, swapIn);
126  }
127 
128  // predicate is a logical comparison, recurse down on inner predicates
129  parser::PredLogicalOp* logicPred = dynamic_cast<parser::PredLogicalOp*>(parent);
130  if (logicPred) {
131  if (logicPred->_lhs == swapOut) { logicPred->_lhs = swapIn; return true; }
132  if (logicPred->_rhs == swapOut) { logicPred->_rhs = swapIn; return true; }
133 
134  return swapPredicate(logicPred->_lhs, swapOut, swapIn) ||
135  swapPredicate(logicPred->_rhs, swapOut, swapIn);
136  }
137 
138  return false;
139 }
140 
141 template <typename SearchPointType, typename DataSetType>
143  if (swapPredicate(filter, pred, new parser::BooleanConstant(true))) {
144  delete pred;
145  }
146 }
147 
148 template <typename SearchPointType, typename DataSetType>
150  parser::Predicate* reduced, *finalPred = filter->_pred;
151  while ((reduced = reducePredicate(finalPred))) finalPred = reduced;
152 
153  filter->_pred = finalPred;
154 
155  G_DEBUG(GParser, "optimized filter: " << finalPred->toString());
156 }
157 
158 template <typename SearchPointType, typename DataSetType>
160  parser::PredNegate* negPred = dynamic_cast<parser::PredNegate*>(pred);
161 
162  // not not X -> X
163  if (negPred && dynamic_cast<parser::PredNegate*>(negPred->_pred)) {
164  parser::PredNegate* dblNeg = (parser::PredNegate*)(negPred->_pred);
165  parser::Predicate* result = dblNeg->_pred;
166  dblNeg->_pred = 0; // make sure we don't delete it twice!
167  delete pred;
168  return result;
169  }
170 
171  // not true -> false, not false -> true
172  if (negPred && dynamic_cast<parser::BooleanConstant*>(negPred->_pred)) {
173  parser::Predicate* result = new parser::BooleanConstant(!((parser::BooleanConstant*)negPred->_pred)->value());
174  delete pred;
175  return result;
176  }
177 
178  // not X -> not (reduce(X))
179  if (negPred) {
180  parser::Predicate* result = reducePredicate(negPred->_pred);
181  if (!result) return 0;
182 
183  negPred->_pred = result;
184  return pred;
185  }
186 
187 
188  parser::PredLogicalOp* logicPred = dynamic_cast<parser::PredLogicalOp*>(pred);
189 
190  // True AND X -> X, False AND X -> False,
191  // True OR X -> True, False OR X -> X
192  if (logicPred && dynamic_cast<parser::BooleanConstant*>(logicPred->_lhs)) {
193  parser::Predicate* result = 0;
194  bool lvalue = ((parser::BooleanConstant*)logicPred->_lhs)->value();
195  switch (logicPred->_op) {
196 
197  case AND:
198  if (lvalue == true) { result = logicPred->_rhs; logicPred->_rhs = 0; }
199  else { result = new parser::BooleanConstant(false); }
200  break;
201 
202  case OR:
203  if (lvalue == true) { result = new parser::BooleanConstant(true); }
204  else { result = logicPred->_rhs; logicPred->_rhs = 0; }
205  break;
206  }
207 
208  if (result) delete logicPred;
209  return result;
210  }
211 
212  // X AND True -> X, X AND False -> False,
213  // X OR True -> True, X OR False -> X
214  if (logicPred && dynamic_cast<parser::BooleanConstant*>(logicPred->_rhs)) {
215  parser::Predicate* result = 0;
216  bool rvalue = ((parser::BooleanConstant*)logicPred->_rhs)->value();
217  switch (logicPred->_op) {
218 
219  case AND:
220  if (rvalue == true) { result = logicPred->_lhs; logicPred->_lhs = 0; }
221  else { result = new parser::BooleanConstant(false); }
222  break;
223 
224  case OR:
225  if (rvalue == true) { result = new parser::BooleanConstant(true); }
226  else { result = logicPred->_lhs; logicPred->_lhs = 0; }
227  break;
228  }
229 
230  if (result) delete logicPred;
231  return result;
232  }
233 
234  // NOT (A OR NOT B) -> NOT A AND (NOT NOT) B (spare 1 negation)
235  // and all variants of it... let's do it later if it's really worth it...
236 
237  // X AND Y -> reduce(X) AND reduce(Y), X OR Y -> reduce(X) OR reduce(Y)
238  if (logicPred) {
239  parser::Predicate* lhs = reducePredicate(logicPred->_lhs);
240  parser::Predicate* rhs = reducePredicate(logicPred->_rhs);
241 
242  if (!lhs && !rhs) return 0;
243 
244  if (lhs) logicPred->_lhs = lhs;
245  if (rhs) logicPred->_rhs = rhs;
246 
247  return pred;
248  }
249 
250  return 0;
251 }
252 
253 
254 template <typename SearchPointType, typename DataSetType>
256  G_DEBUG(GParser, "optimizing filter: " << filter->_pred->toString());
257  // first reduce the filter, to make sure there's no obvious optimization that
258  // we might miss, or to avoid much more complexity than what's necessary in
259  // the optimize function
260  reduceFilter(filter);
261 
262  SearchSpaceType* result = optimize(filter->_pred);
263 
264  if (!result) {
265  // we could not optimize the filter, just return the full search space with
266  // the original filter
267  result = _spool->getAllPoints();
268  result->setFilter(filter, true);
269  }
270  else {
271  // optimization worked, we have a new search space with associated filter
272  // we can safely delete the old one;
273  delete filter;
274  }
275 
276  return result;
277 }
278 
279 template <typename SearchPointType, typename DataSetType>
281  G_DEBUG(GParser, "optimizing predicate:" << pred->toString());
282 
283  if (dynamic_cast<parser::PredNegate*>(pred)) return 0;
284 
285  // Indexed subspaces: value <= x, < x, > x, >= x, ...
286  // label == "s", != "s", ...
287  if (dynamic_cast<parser::PredValueComparison*>(pred) ||
288  dynamic_cast<parser::PredLabelComparison*>(pred) ||
289  dynamic_cast<parser::PredValueRange*>(pred)) {
290  return _spool->getSubSpace(pred);
291  }
292 
293  // Logical operator: A OR B, A AND B
294  parser::PredLogicalOp* logicPred = dynamic_cast<parser::PredLogicalOp*>(pred);
295  if (logicPred) {
296  SearchSpaceType* lhs = optimize(logicPred->_lhs);
297  if (!lhs || !isAlwaysTrue(lhs->_filter->_pred)) return 0;
298  SearchSpaceType* rhs = optimize(logicPred->_rhs);
299  if (!rhs || !isAlwaysTrue(rhs->_filter->_pred)) return 0;
300 
301  switch (logicPred->_op) {
302  case AND:
303  lhs->setIntersection(rhs);
304  break;
305  case OR:
306  lhs->setUnion(rhs);
307  break;
308  }
309 
310  delete rhs;
311  return lhs;
312  }
313 
314  // Boolean constant
315  if (isAlwaysTrue(pred)) return _spool->getAllPoints();
316  if (isAlwaysFalse(pred)) {
317  SearchSpaceType* result = new SearchSpaceType();
318  Filter* alwaysFalse = Filter::parse("where false");
319  result->setFilter(alwaysFalse);
320  return result;
321  }
322 
323  // Point.id in (...)
324  parser::PredLabelIsIn* labelPred = dynamic_cast<parser::PredLabelIsIn*>(pred);
325  if (labelPred) {
326  if (!dynamic_cast<parser::LabelPointID*>(labelPred->_var)) {
327  return 0; // not implemented at the moment
328  }
329 
330  return SearchSpaceType::createFromPoints(_spool->dataset(), labelPred->_slist);
331 
332  /*
333  const DataSetType* dataset = _spool->dataset();
334  int npoints = labelPred->_slist.size();
335  SearchSpaceType* result = BaseSearchSpacePool<SearchPointType, DataSetType>::acquire();
336  result->setFilter(Filter::parse("where true"), true);
337  result->resize(npoints);
338 
339  for (int i=0; i<npoints; i++) {
340  try {
341  (*result)[i].ptr = dataset->point(labelPred->_slist[i]);
342  (*result)[i].ref = refDataSet->point(labelPred->_slist[i]);
343  }
344  catch (GaiaException&) {
345  npoints--; i--;
346  result->resize(npoints);
347  }
348  }
349 
350  return result;
351  */
352  }
353 
354  if (dynamic_cast<parser::PredValueIsIn*>(pred)) {
355  //G_WARNING("QueryOptimizer: 'Value is in' not implemented at the moment");
356  return 0; // not implemented at the moment
357  }
358 
359  return 0;
360 }
361 
362 } // namespace gaia2
Definition: parsertypes.h:210
static parser::Predicate * reducePredicate(parser::Predicate *pred)
Tries to apply simplification on the predicate, such as replacing "X and True" by "X"...
Definition: queryoptimizer_impl.h:159
static void reduceFilter(Filter *filter)
Tries to apply simplification on the filter, such as replacing "X and True" by "X", etc...
Definition: queryoptimizer_impl.h:149
static void deletePredicate(Filter *filter, parser::Predicate *pred)
Deletes given predicate inside filter (replaces it with the always true predicate).
Definition: queryoptimizer_impl.h:142
static Filter * parse(const QString &str)
Parses a given string and returns the newly created Filter object.
Definition: filter.cpp:181
Definition: parsertypes.h:40
void setUnion(const BaseSearchSpace< SearchPointType, DataSetType > *other)
This computes the union of this SearchSpace with the other one.
Definition: searchspace.h:299
The Filter class allows to check whether a predicate is true for a given Point.
Definition: filter.h:73
Definition: parsertypes.h:256
Main Gaia namespace, which contains all the library functions.
Definition: addfield.cpp:22
Definition: parsertypes.h:164
SearchSpaceType * optimize(Filter *filter)
Ownership of the SearchSpace is yielded to the caller of the function.
Definition: queryoptimizer_impl.h:255
bool isAlwaysFalse(parser::Predicate *pred)
Returns true if the given predicate is always false (ie: it is a boolean constant which value is Fals...
Definition: filter.h:57
static bool swapPredicate(Filter *filter, parser::Predicate *swapOut, parser::Predicate *swapIn)
Swaps predicate swapOut inside of filter for predicate swapIn.
Definition: queryoptimizer_impl.h:102
A SearchSpace is a structure dedicated to the task of storing pointers to Point with an associated di...
Definition: searchspace.h:91
bool isAlwaysTrue(parser::Predicate *pred)
Returns true if the given predicate is always true (ie: it is a boolean constant which value is True)...
Definition: filter.h:48
Definition: parsertypes.h:281
Definition: parsertypes.h:329
void setIntersection(const BaseSearchSpace< SearchPointType, DataSetType > *other)
This computes the intersection of this SearchSpace with the other one.
Definition: searchspace.h:277