MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
sql_derived.cc
1 /* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software Foundation,
14  51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
15 
16 
17 /*
18  Derived tables
19  These were introduced by Sinisa <sinisa@mysql.com>
20 */
21 
22 
23 #include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
24 #include "sql_priv.h"
25 #include "unireg.h"
26 #include "sql_derived.h"
27 #include "sql_select.h"
28 #include "sql_optimizer.h" // JOIN
29 #include "sql_view.h" // check_duplicate_names
30 #include "sql_acl.h" // SELECT_ACL
31 #include "sql_tmp_table.h" // Tmp tables
32 
33 
62 bool
63 mysql_handle_derived(LEX *lex, bool (*processor)(THD*, LEX*, TABLE_LIST*))
64 {
65  bool res= FALSE;
66  if (lex->derived_tables)
67  {
68  lex->thd->derived_tables_processing= TRUE;
69  for (SELECT_LEX *sl= lex->all_selects_list;
70  sl;
71  sl= sl->next_select_in_list())
72  {
73  for (TABLE_LIST *table_ref= sl->get_table_list();
74  table_ref;
75  table_ref= table_ref->next_local)
76  {
77  if ((res= mysql_handle_single_derived(lex, table_ref, processor)))
78  goto out;
79  }
80  if (lex->describe)
81  {
82  /*
83  Force join->join_tmp creation, because we will use this JOIN
84  twice for EXPLAIN and we have to have unchanged join for EXPLAINing
85  */
86  sl->uncacheable|= UNCACHEABLE_EXPLAIN;
87  sl->master_unit()->uncacheable|= UNCACHEABLE_EXPLAIN;
88  }
89  }
90  }
91 out:
92  lex->thd->derived_tables_processing= FALSE;
93  return res;
94 }
95 
96 
110 bool
111 mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived,
112  bool (*processor)(THD*, LEX*, TABLE_LIST*))
113 {
114  return (derived->is_view_or_derived() &&
115  (*processor)(lex->thd, lex, derived));
116 }
117 
173 bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
174 {
175  SELECT_LEX_UNIT *unit= derived->get_unit();
176  ulonglong create_options;
177  DBUG_ENTER("mysql_derived_prepare");
178  bool res= FALSE;
179  DBUG_ASSERT(unit);
180  if (derived->uses_materialization())
181  {
182  SELECT_LEX *first_select= unit->first_select();
183  TABLE *table= 0;
184  select_union *derived_result;
185 
186  /* prevent name resolving out of derived table */
187  for (SELECT_LEX *sl= first_select; sl; sl= sl->next_select())
188  sl->context.outer_context= 0;
189 
190  if (!(derived_result= new select_union))
191  DBUG_RETURN(TRUE); // out of memory
192 
193  lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED;
194  // st_select_lex_unit::prepare correctly work for single select
195  if ((res= unit->prepare(thd, derived_result, 0)))
196  goto exit;
197  lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED;
198  if ((res= check_duplicate_names(unit->types, 0)))
199  goto exit;
200 
201  create_options= (first_select->options | thd->variables.option_bits |
202  TMP_TABLE_ALL_COLUMNS);
203  /*
204  Temp table is created so that it honors if UNION without ALL is to be
205  processed
206 
207  As 'distinct' parameter we always pass FALSE (0), because underlying
208  query will control distinct condition by itself. Correct test of
209  distinct underlying query will be is_union &&
210  !unit->union_distinct->next_select() (i.e. it is union and last distinct
211  SELECT is last SELECT of UNION).
212  */
213  if ((res= derived_result->create_result_table(thd, &unit->types, FALSE,
214  create_options,
215  derived->alias, FALSE, FALSE)))
216  goto exit;
217 
218  table= derived_result->table;
219 
220 exit:
221  /* Hide "Unknown column" or "Unknown function" error */
222  if (derived->view)
223  {
224  if (thd->is_error() &&
225  (thd->get_stmt_da()->sql_errno() == ER_BAD_FIELD_ERROR ||
226  thd->get_stmt_da()->sql_errno() == ER_FUNC_INEXISTENT_NAME_COLLISION ||
227  thd->get_stmt_da()->sql_errno() == ER_SP_DOES_NOT_EXIST))
228  {
229  thd->clear_error();
230  my_error(ER_VIEW_INVALID, MYF(0), derived->db,
231  derived->table_name);
232  }
233  }
234 
235  /*
236  if it is preparation PS only or commands that need only VIEW structure
237  then we do not need real data and we can skip execution (and parameters
238  is not defined, too)
239  */
240  if (res)
241  {
242  if (table)
243  free_tmp_table(thd, table);
244  delete derived_result;
245  }
246  else
247  {
248  derived->derived_result= derived_result;
249  derived->table= table;
250  derived->table_name= table->s->table_name.str;
251  derived->table_name_length= table->s->table_name.length;
252  table->s->tmp_table= NON_TRANSACTIONAL_TMP_TABLE;
253 #ifndef NO_EMBEDDED_ACCESS_CHECKS
254  if (derived->referencing_view)
255  table->grant= derived->grant;
256  else
257  table->grant.privilege= SELECT_ACL;
258 #endif
259  derived->db= (char *)"";
260  derived->db_length= 0;
261  /* Add new temporary table to list of open derived tables */
262  table->next= thd->derived_tables;
263  thd->derived_tables= table;
264  }
265  }
266  else
267  derived->set_underlying_merge();
268  DBUG_RETURN(res);
269 }
270 
271 
289 bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived)
290 {
291  SELECT_LEX_UNIT *unit= derived->get_unit();
292  DBUG_ENTER("mysql_derived_optimize");
293 
294  DBUG_ASSERT(unit);
295 
296  // optimize union without execution
297  if (unit->optimize() || thd->is_error())
298  DBUG_RETURN(TRUE);
299 
300  if (derived->materializable_is_const() &&
301  (mysql_derived_create(thd, lex, derived) ||
302  mysql_derived_materialize(thd, lex, derived)))
303  DBUG_RETURN(TRUE);
304 
305  DBUG_RETURN(FALSE);
306 }
307 
308 
326 bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived)
327 {
328  TABLE *table= derived->table;
329  SELECT_LEX_UNIT *unit= derived->get_unit();
330  DBUG_ENTER("mysql_derived_create");
331 
332  DBUG_ASSERT(unit);
333 
334  /*
335  Don't create result table in following cases:
336  *) It's a mergeable view.
337  *) Some commands, like show table status, doesn't prepare views/derived
338  tables => no need to create result table also.
339  *) Table is already created.
340  *) Table is a constant one with all NULL values.
341  */
342  if (!derived->uses_materialization() || !table || table->is_created() ||
343  (derived->select_lex->join != NULL &&
344  (derived->select_lex->join->const_table_map & table->map)))
345  {
346  /*
347  At this point, JT_CONST derived tables should be null rows. Otherwise they
348  would have been materialized already.
349  */
350  DBUG_ASSERT(table == NULL || table->reginfo.join_tab == NULL ||
351  table->reginfo.join_tab->type != JT_CONST ||
352  table->null_row == 1);
353  DBUG_RETURN(FALSE);
354  }
355  /* create tmp table */
356  select_union *result= (select_union*)unit->get_result();
357 
358  if (instantiate_tmp_table(table, table->key_info,
359  result->tmp_table_param.start_recinfo,
360  &result->tmp_table_param.recinfo,
361  (unit->first_select()->options |
362  thd->lex->select_lex.options |
363  thd->variables.option_bits |
364  TMP_TABLE_ALL_COLUMNS),
365  thd->variables.big_tables, &thd->opt_trace))
366  DBUG_RETURN(TRUE);
367 
368  table->file->extra(HA_EXTRA_WRITE_CACHE);
369  table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
370 
371  table->set_created();
372 
373  DBUG_RETURN(FALSE);
374 }
375 
376 
397 bool mysql_derived_materialize(THD *thd, LEX *lex, TABLE_LIST *derived)
398 {
399  SELECT_LEX_UNIT *unit= derived->get_unit();
400  bool res= FALSE;
401  DBUG_ENTER("mysql_derived_materialize");
402 
403  DBUG_ASSERT(unit && derived->table && derived->table->is_created());
404 
405  select_union *derived_result= derived->derived_result;
406 
407  if (unit->is_union())
408  {
409  // execute union without clean up
410  res= unit->exec();
411  }
412  else
413  {
414  SELECT_LEX *first_select= unit->first_select();
415  JOIN *join= first_select->join;
416  SELECT_LEX *save_current_select= lex->current_select;
417  lex->current_select= first_select;
418 
419  DBUG_ASSERT(join && join->optimized);
420 
421  unit->set_limit(first_select);
422  if (unit->select_limit_cnt == HA_POS_ERROR)
423  first_select->options&= ~OPTION_FOUND_ROWS;
424 
425  join->exec();
426  res= join->error;
427  lex->current_select= save_current_select;
428  }
429 
430  if (!res)
431  {
432  /*
433  Here we entirely fix both TABLE_LIST and list of SELECT's as
434  there were no derived tables
435  */
436  if (derived_result->flush())
437  res= TRUE;
438  }
439 
440  DBUG_RETURN(res);
441 }
442 
443 
448 bool mysql_derived_cleanup(THD *thd, LEX *lex, TABLE_LIST *derived)
449 {
450  DBUG_ENTER("mysql_derived_cleanup");
451  SELECT_LEX_UNIT *unit= derived->derived;
452  if (unit)
453  unit->cleanup();
454  DBUG_RETURN(false);
455 }