sql_derived.cc 7.42 KB
Newer Older
1
/* Copyright (C) 2002-2003 MySQL AB
unknown's avatar
unknown committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

   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
20
  These were introduced by Sinisa <sinisa@mysql.com>
unknown's avatar
unknown committed
21 22 23 24 25 26 27
*/


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

28
extern  const char *any_db;	// Special symbol for check_access
unknown's avatar
unknown committed
29

30 31 32 33 34 35 36 37 38 39 40
/*
  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
41 42 43 44 45 46
    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.
47

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

51 52
    Derived tables is stored in thd->derived_tables and freed in
    close_thread_tables()
53

54 55 56 57 58 59 60
  TODO
    Move creation of derived tables in open_and_lock_tables()

  RETURN
    0	ok
    1	Error
    -1	Error and error message given
61 62
*/  

unknown's avatar
unknown committed
63

64 65
int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit,
		  TABLE_LIST *org_table_list)
unknown's avatar
unknown committed
66
{
unknown's avatar
unknown committed
67
  SELECT_LEX *select_cursor= unit->first_select();
unknown's avatar
unknown committed
68 69
  List<Item> item_list;
  TABLE *table;
70
  int res;
unknown's avatar
unknown committed
71
  select_union *derived_result;
unknown's avatar
unknown committed
72
  TABLE_LIST *tables= (TABLE_LIST *)select_cursor->table_list.first;
unknown's avatar
unknown committed
73
  TMP_TABLE_PARAM tmp_table_param;
unknown's avatar
unknown committed
74 75 76
  bool is_union= select_cursor->next_select() && 
    select_cursor->next_select()->linkage == UNION_TYPE;
  bool is_subsel= select_cursor->first_inner_unit() ? 1: 0;
77
  SELECT_LEX_NODE *save_current_select= lex->current_select;
78
  DBUG_ENTER("mysql_derived");
unknown's avatar
unknown committed
79
  
80 81 82 83 84 85 86
  /*
    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.
  */
87
  if ((is_union || is_subsel) && unit->create_total_list(thd, lex, &tables, 1))
unknown's avatar
unknown committed
88 89
    DBUG_RETURN(-1);

90 91 92 93
  /*
    We have to do access checks here as this code is executed before any
    sql command is started to execute.
  */
unknown's avatar
unknown committed
94
  if (tables)
unknown's avatar
unknown committed
95
    res= check_table_access(thd,SELECT_ACL, tables);
unknown's avatar
unknown committed
96
  else
unknown's avatar
unknown committed
97
    res= check_access(thd, SELECT_ACL, any_db);
unknown's avatar
unknown committed
98 99 100
  if (res)
    DBUG_RETURN(-1);
    
101
  if (!(res=open_and_lock_tables(thd,tables)))
unknown's avatar
unknown committed
102
  {
103 104 105 106 107 108 109 110 111 112 113 114
    if (is_union || is_subsel)
    {
      /* 
	 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(unit);
    }

unknown's avatar
unknown committed
115 116 117
    lex->current_select= select_cursor;
    TABLE_LIST *first_table= (TABLE_LIST*) select_cursor->table_list.first;
    /* Setting up. A must if a join or IGNORE, USE or similar are utilised */
118
    if (setup_tables(first_table) ||
unknown's avatar
unknown committed
119 120
	setup_wild(thd, first_table, select_cursor->item_list, 0,
		   select_cursor->with_wild))
121 122 123 124
    {
      res= -1;
      goto exit;
    }
unknown's avatar
unknown committed
125 126 127 128 129 130 131 132 133 134

    /* 
       This is done in order to redo all field optimisations when any of the 
       involved tables is used in the outer query 
    */
    if (tables)
    {
      for (TABLE_LIST *cursor= tables;  cursor;  cursor= cursor->next)
	cursor->table->clear_query_id= 1;
    }
135
	
unknown's avatar
unknown committed
136 137 138
    item_list= select_cursor->item_list;
    select_cursor->with_wild= 0;
    if (setup_ref_array(thd, &select_cursor->ref_pointer_array, 
139 140
			(item_list.elements +
			 select_cursor->select_n_having_items +
unknown's avatar
unknown committed
141 142
			 select_cursor->order_list.elements + 
			 select_cursor->group_list.elements)) ||
unknown's avatar
unknown committed
143 144
	setup_fields(thd, select_cursor->ref_pointer_array, first_table,
		     item_list, 0, 0, 1))
unknown's avatar
unknown committed
145
    {
146
      res= -1;
unknown's avatar
unknown committed
147 148 149
      goto exit;
    }
    bzero((char*) &tmp_table_param,sizeof(tmp_table_param));
150
    tmp_table_param.field_count= item_list.elements;
unknown's avatar
unknown committed
151 152 153 154
    /*
      Temp table is created so that it hounours if UNION without ALL is to be 
      processed
    */
155 156 157
    if (!(table= create_tmp_table(thd, &tmp_table_param, item_list,
				  (ORDER*) 0, 
				  is_union && !unit->union_option, 1,
unknown's avatar
unknown committed
158
				  (select_cursor->options | thd->options |
159 160
				   TMP_TABLE_ALL_COLUMNS),
				  HA_POS_ERROR)))
unknown's avatar
unknown committed
161
    {
162
      res= -1;
unknown's avatar
unknown committed
163 164 165 166 167
      goto exit;
    }
  
    if ((derived_result=new select_union(table)))
    {
168
      derived_result->tmp_table_param=tmp_table_param;
unknown's avatar
unknown committed
169 170 171 172
      unit->offset_limit_cnt= select_cursor->offset_limit;
      unit->select_limit_cnt= select_cursor->select_limit+
	select_cursor->offset_limit;
      if (unit->select_limit_cnt < select_cursor->select_limit)
173 174
	unit->select_limit_cnt= HA_POS_ERROR;
      if (unit->select_limit_cnt == HA_POS_ERROR)
unknown's avatar
unknown committed
175
	select_cursor->options&= ~OPTION_FOUND_ROWS;
176

unknown's avatar
unknown committed
177
      if (is_union)
178
	res= mysql_union(thd, lex, derived_result, unit, 1);
unknown's avatar
unknown committed
179
      else
unknown's avatar
unknown committed
180 181 182 183
        res= mysql_select(thd, &select_cursor->ref_pointer_array, 
			  (TABLE_LIST*) select_cursor->table_list.first,
			  select_cursor->with_wild,
			  select_cursor->item_list, select_cursor->where,
unknown's avatar
unknown committed
184 185
			  (select_cursor->order_list.elements+
			   select_cursor->group_list.elements),
unknown's avatar
unknown committed
186 187 188
			  (ORDER *) select_cursor->order_list.first,
			  (ORDER *) select_cursor->group_list.first,
			  select_cursor->having, (ORDER*) NULL,
unknown's avatar
unknown committed
189 190
			  (select_cursor->options | thd->options |
			   SELECT_NO_UNLOCK),
191
			  derived_result, unit, select_cursor, 1);
192

unknown's avatar
unknown committed
193 194
      if (!res)
      {
195 196 197 198
	/*
	  Here we entirely fix both TABLE_LIST and list of SELECT's as
	  there were no derived tables
	*/
unknown's avatar
unknown committed
199
	if (derived_result->flush())
200
	  res= 1;
unknown's avatar
unknown committed
201 202
	else
	{
203 204
	  org_table_list->real_name=table->real_name;
	  org_table_list->table=table;
unknown's avatar
unknown committed
205
	  table->derived_select_number= select_cursor->select_number;
206
	  table->tmp_table= TMP_TABLE;
unknown's avatar
unknown committed
207
	  org_table_list->grant.privilege= SELECT_ACL;
unknown's avatar
unknown committed
208
	  if (lex->describe)
209
	  {
210
	    // to fix a problem in EXPLAIN
211
	    if (tables)
212 213 214 215
	    {
	      for (TABLE_LIST *cursor= tables;  cursor;  cursor= cursor->next)
		cursor->table_list->table=cursor->table;
	    }
216
	  }
unknown's avatar
unknown committed
217
	  else
unknown's avatar
unknown committed
218
	    unit->exclude_level();
219 220 221 222 223
	  org_table_list->db= (char *)"";
#ifndef DBUG_OFF
	  /* Try to catch errors if this is accessed */
	  org_table_list->derived=(SELECT_LEX_UNIT *) 1;
#endif
unknown's avatar
unknown committed
224
	  // Force read of table stats in the optimizer
unknown's avatar
unknown committed
225
	  table->file->info(HA_STATUS_VARIABLE);
unknown's avatar
unknown committed
226 227 228 229 230
	}
      }
      delete derived_result;
    }
    if (res)
231 232 233
      free_tmp_table(thd, table);
    else
    {
234
      /* Add new temporary table to list of open derived tables */
235 236 237
      table->next= thd->derived_tables;
      thd->derived_tables= table;
    }
unknown's avatar
unknown committed
238

unknown's avatar
unknown committed
239
exit:
240
    lex->current_select= save_current_select;
241
    close_thread_tables(thd, 0, 1);
unknown's avatar
unknown committed
242 243 244
  }
  DBUG_RETURN(res);
}