1 /*
2    Copyright (c) 2009, 2012, Monty Program Ab
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; version 2 of the License.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
16 
17 #include "mariadb.h"
18 #include "sql_select.h"
19 #include "sql_test.h"
20 
21 /****************************************************************************
22  * Index Condition Pushdown code starts
23  ***************************************************************************/
24 /*
25   Check if given expression uses only table fields covered by the given index
26 
27   SYNOPSIS
28     uses_index_fields_only()
29       item           Expression to check
30       tbl            The table having the index
31       keyno          The index number
32       other_tbls_ok  TRUE <=> Fields of other non-const tables are allowed
33 
34   DESCRIPTION
35     Check if given expression only uses fields covered by index #keyno in the
36     table tbl. The expression can use any fields in any other tables.
37 
38     The expression is guaranteed not to be AND or OR - those constructs are
39     handled outside of this function.
40 
41   RETURN
42     TRUE   Yes
43     FALSE  No
44 */
45 
uses_index_fields_only(Item * item,TABLE * tbl,uint keyno,bool other_tbls_ok)46 bool uses_index_fields_only(Item *item, TABLE *tbl, uint keyno,
47                             bool other_tbls_ok)
48 {
49   if (item->walk(&Item::limit_index_condition_pushdown_processor, FALSE, NULL))
50   {
51     return FALSE;
52   }
53 
54   if (item->const_item())
55     return TRUE;
56 
57   /*
58     Don't push down the triggered conditions. Nested outer joins execution
59     code may need to evaluate a condition several times (both triggered and
60     untriggered), and there is no way to put thi
61     TODO: Consider cloning the triggered condition and using the copies for:
62       1. push the first copy down, to have most restrictive index condition
63          possible
64       2. Put the second copy into tab->select_cond.
65   */
66   if (item->type() == Item::FUNC_ITEM &&
67       ((Item_func*)item)->functype() == Item_func::TRIG_COND_FUNC)
68     return FALSE;
69 
70   if (!(item->used_tables() & tbl->map))
71     return other_tbls_ok;
72 
73   Item::Type item_type= item->type();
74   switch (item_type) {
75   case Item::FUNC_ITEM:
76     {
77       /* This is a function, apply condition recursively to arguments */
78       Item_func *item_func= (Item_func*)item;
79       Item **child;
80       Item **item_end= (item_func->arguments()) + item_func->argument_count();
81       for (child= item_func->arguments(); child != item_end; child++)
82       {
83         if (!uses_index_fields_only(*child, tbl, keyno, other_tbls_ok))
84           return FALSE;
85       }
86       return TRUE;
87     }
88   case Item::COND_ITEM:
89     {
90       /*
91         This is a AND/OR condition. Regular AND/OR clauses are handled by
92         make_cond_for_index() which will chop off the part that can be
93         checked with index. This code is for handling non-top-level AND/ORs,
94         e.g. func(x AND y).
95       */
96       List_iterator<Item> li(*((Item_cond*)item)->argument_list());
97       Item *item;
98       while ((item=li++))
99       {
100         if (!uses_index_fields_only(item, tbl, keyno, other_tbls_ok))
101           return FALSE;
102       }
103       return TRUE;
104     }
105   case Item::FIELD_ITEM:
106     {
107       Item_field *item_field= (Item_field*)item;
108       Field *field= item_field->field;
109       if (field->table != tbl)
110         return TRUE;
111       /*
112         The below is probably a repetition - the first part checks the
113         other two, but let's play it safe:
114       */
115       if(!field->part_of_key.is_set(keyno) ||
116          field->type() == MYSQL_TYPE_GEOMETRY ||
117          field->type() == MYSQL_TYPE_BLOB)
118         return FALSE;
119       KEY *key_info= tbl->key_info + keyno;
120       KEY_PART_INFO *key_part= key_info->key_part;
121       KEY_PART_INFO *key_part_end= key_part + key_info->user_defined_key_parts;
122       for ( ; key_part < key_part_end; key_part++)
123       {
124         if (field->eq(key_part->field))
125 	  return !(key_part->key_part_flag & HA_PART_KEY_SEG);
126       }
127       if ((tbl->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
128           tbl->s->primary_key != MAX_KEY &&
129 	  tbl->s->primary_key != keyno)
130       {
131         key_info= tbl->key_info + tbl->s->primary_key;
132         key_part= key_info->key_part;
133         key_part_end= key_part + key_info->user_defined_key_parts;
134         for ( ; key_part < key_part_end; key_part++)
135         {
136           /*
137             It does not make sense to use the fact that the engine can read in
138             a full field if the key if the index is built only over a part
139             of this field.
140 	  */
141           if (field->eq(key_part->field))
142 	    return !(key_part->key_part_flag & HA_PART_KEY_SEG);
143         }
144       }
145       return FALSE;
146     }
147   case Item::REF_ITEM:
148     return uses_index_fields_only(item->real_item(), tbl, keyno,
149                                   other_tbls_ok);
150   default:
151     return FALSE; /* Play it safe, don't push unknown non-const items */
152   }
153 }
154 
155 #define ICP_COND_USES_INDEX_ONLY 10
156 
157 /*
158   Get a part of the condition that can be checked using only index fields
159 
160   SYNOPSIS
161     make_cond_for_index()
162       cond           The source condition
163       table          The table that is partially available
164       keyno          The index in the above table. Only fields covered by the index
165                      are available
166       other_tbls_ok  TRUE <=> Fields of other non-const tables are allowed
167 
168   DESCRIPTION
169     Get a part of the condition that can be checked when for the given table
170     we have values only of fields covered by some index. The condition may
171     refer to other tables, it is assumed that we have values of all of their
172     fields.
173 
174     Example:
175       make_cond_for_index(
176          "cond(t1.field) AND cond(t2.key1) AND cond(t2.non_key) AND cond(t2.key2)",
177           t2, keyno(t2.key1))
178       will return
179         "cond(t1.field) AND cond(t2.key2)"
180 
181   RETURN
182     Index condition, or NULL if no condition could be inferred.
183 */
184 
make_cond_for_index(THD * thd,Item * cond,TABLE * table,uint keyno,bool other_tbls_ok)185 static Item *make_cond_for_index(THD *thd, Item *cond, TABLE *table, uint keyno,
186                                  bool other_tbls_ok)
187 {
188   if (!cond)
189     return NULL;
190   if (cond->type() == Item::COND_ITEM)
191   {
192     uint n_marked= 0;
193     if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
194     {
195       table_map used_tables= 0;
196       Item_cond_and *new_cond= new (thd->mem_root) Item_cond_and(thd);
197       if (!new_cond)
198 	return (COND*) 0;
199       List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
200       Item *item;
201       while ((item=li++))
202       {
203 	Item *fix= make_cond_for_index(thd, item, table, keyno, other_tbls_ok);
204 	if (fix)
205         {
206 	  new_cond->argument_list()->push_back(fix, thd->mem_root);
207           used_tables|= fix->used_tables();
208         }
209         if (item->marker == ICP_COND_USES_INDEX_ONLY)
210         {
211           n_marked++;
212           item->marker= 0;
213         }
214       }
215       if (n_marked ==((Item_cond*)cond)->argument_list()->elements)
216         cond->marker= ICP_COND_USES_INDEX_ONLY;
217       switch (new_cond->argument_list()->elements) {
218       case 0:
219 	return (COND*) 0;
220       case 1:
221         new_cond->used_tables_cache= used_tables;
222 	return new_cond->argument_list()->head();
223       default:
224 	new_cond->quick_fix_field();
225         new_cond->used_tables_cache= used_tables;
226 	return new_cond;
227       }
228     }
229     else /* It's OR */
230     {
231       Item_cond_or *new_cond= new (thd->mem_root) Item_cond_or(thd);
232       if (!new_cond)
233 	return (COND*) 0;
234       List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
235       Item *item;
236       while ((item=li++))
237       {
238         Item *fix= make_cond_for_index(thd, item, table, keyno, other_tbls_ok);
239 	if (!fix)
240 	  return (COND*) 0;
241 	new_cond->argument_list()->push_back(fix, thd->mem_root);
242         if (item->marker == ICP_COND_USES_INDEX_ONLY)
243         {
244           n_marked++;
245           item->marker= 0;
246         }
247       }
248       if (n_marked ==((Item_cond*)cond)->argument_list()->elements)
249         cond->marker= ICP_COND_USES_INDEX_ONLY;
250       new_cond->quick_fix_field();
251       new_cond->used_tables_cache= ((Item_cond_or*) cond)->used_tables_cache;
252       new_cond->top_level_item();
253       return new_cond;
254     }
255   }
256 
257   if (!uses_index_fields_only(cond, table, keyno, other_tbls_ok))
258     return (COND*) 0;
259   cond->marker= ICP_COND_USES_INDEX_ONLY;
260   return cond;
261 }
262 
263 
make_cond_remainder(THD * thd,Item * cond,TABLE * table,uint keyno,bool other_tbls_ok,bool exclude_index)264 static Item *make_cond_remainder(THD *thd, Item *cond, TABLE *table, uint keyno,
265                                  bool other_tbls_ok, bool exclude_index)
266 {
267   if (exclude_index &&
268       uses_index_fields_only(cond, table, keyno, other_tbls_ok))
269     return 0;
270 
271   if (cond->type() == Item::COND_ITEM)
272   {
273     table_map tbl_map= 0;
274     if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
275     {
276       /* Create new top level AND item */
277       Item_cond_and *new_cond= new (thd->mem_root) Item_cond_and(thd);
278       if (!new_cond)
279         return (COND*) 0;
280       List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
281       Item *item;
282       while ((item=li++))
283       {
284         Item *fix= make_cond_remainder(thd, item, table, keyno,
285                                        other_tbls_ok, exclude_index);
286 	if (fix)
287         {
288 	  new_cond->argument_list()->push_back(fix, thd->mem_root);
289           tbl_map |= fix->used_tables();
290         }
291       }
292       switch (new_cond->argument_list()->elements) {
293       case 0:
294 	return (COND*) 0;
295       case 1:
296 	return new_cond->argument_list()->head();
297       default:
298 	new_cond->quick_fix_field();
299         ((Item_cond*)new_cond)->used_tables_cache= tbl_map;
300 	return new_cond;
301       }
302     }
303     else /* It's OR */
304     {
305       Item_cond_or *new_cond= new (thd->mem_root) Item_cond_or(thd);
306       if (!new_cond)
307 	return (COND*) 0;
308       List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
309       Item *item;
310       while ((item=li++))
311       {
312         Item *fix= make_cond_remainder(thd, item, table, keyno,
313                                        other_tbls_ok, FALSE);
314 	if (!fix)
315 	  return (COND*) 0;
316 	new_cond->argument_list()->push_back(fix, thd->mem_root);
317         tbl_map |= fix->used_tables();
318       }
319       new_cond->quick_fix_field();
320       ((Item_cond*)new_cond)->used_tables_cache= tbl_map;
321       new_cond->top_level_item();
322       return new_cond;
323     }
324   }
325   return cond;
326 }
327 
328 
329 /*
330   Try to extract and push the index condition
331 
332   SYNOPSIS
333     push_index_cond()
334       tab            A join tab that has tab->table->file and its condition
335                      in tab->select_cond
336       keyno          Index for which extract and push the condition
337 
338   DESCRIPTION
339     Try to extract and push the index condition down to table handler
340 */
341 
push_index_cond(JOIN_TAB * tab,uint keyno)342 void push_index_cond(JOIN_TAB *tab, uint keyno)
343 {
344   DBUG_ENTER("push_index_cond");
345   Item *idx_cond;
346 
347   /*
348   Backported the following from MySQL 5.6:
349     6. The index is not a clustered index. The performance improvement
350        of pushing an index condition on a clustered key is much lower
351        than on a non-clustered key. This restriction should be
352        re-evaluated when WL#6061 is implemented.
353   */
354   if ((tab->table->file->index_flags(keyno, 0, 1) &
355       HA_DO_INDEX_COND_PUSHDOWN) &&
356       optimizer_flag(tab->join->thd, OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN) &&
357       tab->join->thd->lex->sql_command != SQLCOM_UPDATE_MULTI &&
358       tab->join->thd->lex->sql_command != SQLCOM_DELETE_MULTI &&
359       tab->type != JT_CONST && tab->type != JT_SYSTEM &&
360       !tab->table->file->is_clustering_key(keyno)) // 6
361   {
362     DBUG_EXECUTE("where",
363                  print_where(tab->select_cond, "full cond", QT_ORDINARY););
364 
365     idx_cond= make_cond_for_index(tab->join->thd, tab->select_cond, tab->table,
366                                   keyno, tab->icp_other_tables_ok);
367 
368     DBUG_EXECUTE("where",
369                  print_where(idx_cond, "idx cond", QT_ORDINARY););
370 
371     if (idx_cond)
372     {
373       Item *idx_remainder_cond= 0;
374       tab->pre_idx_push_select_cond= tab->select_cond;
375       /*
376         For BKA cache we store condition to special BKA cache field
377         because evaluation of the condition requires additional operations
378         before the evaluation. This condition is used in
379         JOIN_CACHE_BKA[_UNIQUE]::skip_index_tuple() functions.
380       */
381       if (tab->use_join_cache &&
382           /*
383             if cache is used then the value is TRUE only
384             for BKA[_UNIQUE] cache (see check_join_cache_usage func).
385           */
386           tab->icp_other_tables_ok &&
387           (idx_cond->used_tables() &
388            ~(tab->table->map | tab->join->const_table_map)))
389         tab->cache_idx_cond= idx_cond;
390       else
391       {
392         idx_remainder_cond= tab->table->file->idx_cond_push(keyno, idx_cond);
393 
394         /*
395           If (1) there is an index condition that we couldn't push using ICP,
396              (2) we are using Join Buffering
397              (3) and we are using BKA
398           then use BKA's Index Condition Pushdown mechanism to check it.
399         */
400         if (idx_remainder_cond && tab->use_join_cache &&   // (1) && (2)
401             tab->icp_other_tables_ok)                      // (3)
402         {
403           tab->cache_idx_cond= idx_remainder_cond;
404           idx_remainder_cond= NULL;
405         }
406       }
407 
408       /*
409         Disable eq_ref's "lookup cache" if we've pushed down an index
410         condition.
411         TODO: This check happens to work on current ICP implementations, but
412         there may exist a compliant implementation that will not work
413         correctly with it. Sort this out when we stabilize the condition
414         pushdown APIs.
415       */
416       if (idx_remainder_cond != idx_cond)
417         tab->ref.disable_cache= TRUE;
418 
419       Item *row_cond= tab->idx_cond_fact_out ?
420                         make_cond_remainder(tab->join->thd, tab->select_cond,
421                                             tab->table, keyno,
422 			                    tab->icp_other_tables_ok, TRUE) :
423 	                tab->pre_idx_push_select_cond;
424 
425       DBUG_EXECUTE("where",
426                    print_where(row_cond, "remainder cond", QT_ORDINARY););
427 
428       if (row_cond)
429       {
430         if (!idx_remainder_cond)
431           tab->select_cond= row_cond;
432         else
433         {
434           COND *new_cond= new (tab->join->thd->mem_root)
435             Item_cond_and(tab->join->thd, row_cond, idx_remainder_cond);
436           tab->select_cond= new_cond;
437 	  tab->select_cond->quick_fix_field();
438           ((Item_cond_and*)tab->select_cond)->used_tables_cache=
439             row_cond->used_tables() | idx_remainder_cond->used_tables();
440         }
441       }
442       else
443         tab->select_cond= idx_remainder_cond;
444       if (tab->select)
445       {
446         DBUG_EXECUTE("where",
447                      print_where(tab->select->cond,
448                                  "select_cond",
449                                  QT_ORDINARY););
450 
451         tab->select->cond= tab->select_cond;
452         tab->select->pre_idx_push_select_cond= tab->pre_idx_push_select_cond;
453       }
454     }
455   }
456   DBUG_VOID_RETURN;
457 }
458 
459 
460