sql_derived.cc 5.54 KB
Newer Older
unknown's avatar
unknown committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/* Copyright (C) 2000 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */


/*
  Derived tables
  These were introduced by Monty and Sinisa <sinisa@mysql.com>
*/


#include "mysql_priv.h"
#include "sql_select.h"
#include "sql_acl.h"

static const char *any_db="*any*";	// Special symbol for check_access

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
/*
  Resolve derived tables in all queries

  SYNOPSIS
    mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, TABLE_LIST *t)
    thd			Thread handle
    lex                 LEX for this thread
    unit                node that contains all SELECT's for derived tables
    t                   TABLE_LIST for the upper SELECT

  IMPLEMENTATION
  
  Derived table is  resolved with temporary table. It is created based on the
  queries defined. After temporary table is created, if this is not EXPLAIN,
  then the entire unit / node is deleted. unit is deleted if UNION is used 
  for derived table and node is deleted is it is a  simple SELECT.

  After table creation, the above TABLE_LIST is updated with a new table.

  This function is called before any command containing derived table is executed. 

  TODO: To move creation of derived tables IN  open_and_lock_tables()
*/  

unknown's avatar
unknown committed
54

55
int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, TABLE_LIST *t)
unknown's avatar
unknown committed
56
{
57
  SELECT_LEX *sl= unit->first_select();
unknown's avatar
unknown committed
58 59
  List<Item> item_list;
  TABLE *table;
unknown's avatar
unknown committed
60
  int res= 0;
unknown's avatar
unknown committed
61
  select_union *derived_result;
unknown's avatar
unknown committed
62
  TABLE_LIST *tables= (TABLE_LIST *)sl->table_list.first;
unknown's avatar
unknown committed
63
  TMP_TABLE_PARAM tmp_table_param;
unknown's avatar
unknown committed
64
  bool is_union=sl->next_select() && sl->next_select()->linkage == UNION_TYPE;
unknown's avatar
unknown committed
65 66
  DBUG_ENTER("mysql_derived");
  
unknown's avatar
unknown committed
67

68 69 70 71 72 73
/*
  In create_total_list, derived tables have to be treated in case of EXPLAIN, 
  This is because unit/node is not deleted in that case. Current code in this 
  function has to be improved to recognize better when this function is called 
  from derived tables and when from other functions.
*/
unknown's avatar
unknown committed
74 75 76
  if (is_union && unit->create_total_list(thd, lex, &tables))
    DBUG_RETURN(-1);

unknown's avatar
unknown committed
77
  if (tables)
unknown's avatar
unknown committed
78
    res= check_table_access(thd,SELECT_ACL, tables);
unknown's avatar
unknown committed
79
  else
unknown's avatar
unknown committed
80
    res= check_access(thd, SELECT_ACL, any_db);
unknown's avatar
unknown committed
81 82 83 84 85 86 87 88 89
  if (res)
    DBUG_RETURN(-1);

  Item *item;
  List_iterator<Item> it(sl->item_list);

  while ((item= it++))
    item_list.push_back(item);
    
90
  if (!(res=open_and_lock_tables(thd,tables)))
unknown's avatar
unknown committed
91
  {
unknown's avatar
unknown committed
92 93
    if (is_union)
    {
94 95 96 97 98 99 100 101 102
/* 
   The following code is a re-do of fix_tables_pointers() found in sql_select.cc
   for UNION's within derived tables. The only difference is in navigation, as in 
   derived tables we care for this level only.

   fix_tables_pointers makes sure that in UNION's we do not open single table twice 
   if found in different SELECT's.

*/
unknown's avatar
unknown committed
103 104 105 106 107 108 109 110 111 112 113
      for (SELECT_LEX *sel= sl;
	   sel;
	   sel= sel->next_select())
      {
	for (TABLE_LIST *cursor= (TABLE_LIST *)sel->table_list.first;
	     cursor;
	     cursor=cursor->next)
	  cursor->table= cursor->table_list->table;
      }
    }

unknown's avatar
unknown committed
114
    if (setup_fields(thd,tables,item_list,0,0,1))
unknown's avatar
unknown committed
115 116 117 118 119 120
    {
      res=-1;
      goto exit;
    }
    bzero((char*) &tmp_table_param,sizeof(tmp_table_param));
    tmp_table_param.field_count=item_list.elements;
unknown's avatar
unknown committed
121
    if (!(table=create_tmp_table(thd, &tmp_table_param, item_list,
unknown's avatar
unknown committed
122
			         (ORDER*) 0, is_union && !unit->union_option, 1,
unknown's avatar
unknown committed
123 124
			         (sl->options | thd->options |
				  TMP_TABLE_ALL_COLUMNS),
unknown's avatar
unknown committed
125
                                 HA_POS_ERROR)))
unknown's avatar
unknown committed
126 127 128 129 130 131 132
    {
      res=-1;
      goto exit;
    }
  
    if ((derived_result=new select_union(table)))
    {
133
      derived_result->tmp_table_param=&tmp_table_param;
134 135 136 137 138
      unit->offset_limit_cnt= sl->offset_limit;
      unit->select_limit_cnt= sl->select_limit+sl->offset_limit;
      if (unit->select_limit_cnt < sl->select_limit)
	unit->select_limit_cnt= HA_POS_ERROR;
      if (unit->select_limit_cnt == HA_POS_ERROR)
unknown's avatar
unknown committed
139
	sl->options&= ~OPTION_FOUND_ROWS;
140 141 142

      SELECT_LEX_NODE *save_current_select= lex->current_select;
      lex->current_select= sl;
unknown's avatar
unknown committed
143 144 145 146
      if (is_union)
	res=mysql_union(thd,lex,derived_result,unit);
      else
	res= mysql_select(thd, tables,  sl->item_list,
unknown's avatar
unknown committed
147 148 149
			sl->where, (ORDER *) sl->order_list.first,
			(ORDER*) sl->group_list.first,
			sl->having, (ORDER*) NULL,
unknown's avatar
unknown committed
150
			sl->options | thd->options  | SELECT_NO_UNLOCK,
unknown's avatar
unknown committed
151
			derived_result, unit, sl, 0);
152 153
      lex->current_select= save_current_select;

unknown's avatar
unknown committed
154 155 156 157 158 159 160 161 162
      if (!res)
      {
// Here we entirely fix both TABLE_LIST and list of SELECT's as there were no derived tables
	if (derived_result->flush())
	  res=1;
	else
	{
	  t->real_name=table->real_name;
	  t->table=table;
unknown's avatar
unknown committed
163
	  table->derived_select_number= sl->select_number;
unknown's avatar
unknown committed
164
	  table->tmp_table=TMP_TABLE;
unknown's avatar
unknown committed
165
	  if (lex->describe)
166 167 168 169
	  {
	    if (tables)
	      tables->table_list->table=tables->table; // to fix a problem in EXPLAIN
	  }
unknown's avatar
unknown committed
170
	  else
unknown's avatar
unknown committed
171
	    unit->exclude();
unknown's avatar
unknown committed
172
	  t->db=(char *)"";
unknown's avatar
unknown committed
173
	  t->derived=(SELECT_LEX *)1; // just in case ...
unknown's avatar
unknown committed
174
	  table->file->info(HA_STATUS_VARIABLE);
unknown's avatar
unknown committed
175 176 177 178 179 180 181 182 183 184 185
	}
      }
      delete derived_result;
    }
    if (res)
      free_tmp_table(thd,table);
exit:
    close_thread_tables(thd);
  }
  DBUG_RETURN(res);
}