sql_union.cc 8.79 KB
Newer Older
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi 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
/* 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 */


/*
  UNION  of select's
  UNION's  were introduced by Monty and Sinisa <sinisa@mysql.com>
*/


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

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
int mysql_union(THD *thd, LEX *lex, select_result *result)
{
  DBUG_ENTER("mysql_union");
  SELECT_LEX_UNIT *unit= &lex->unit;
  int res= 0;
  if (!(res= unit->prepare(thd, result)))
    res= unit->exec();
  res|= unit->cleanup();
  DBUG_RETURN(res);
}


/***************************************************************************
** store records in temporary table for UNION
***************************************************************************/

select_union::select_union(TABLE *table_par)
    :table(table_par)
{
  bzero((char*) &info,sizeof(info));
  /*
    We can always use DUP_IGNORE because the temporary table will only
    contain a unique key if we are using not using UNION ALL
  */
  info.handle_duplicates= DUP_IGNORE;
}

select_union::~select_union()
{
}


int select_union::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
{
  unit= u;
  if (save_time_stamp && list.elements != table->fields)
  {
    my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
	       ER(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),MYF(0));
    return -1;
  }
  return 0;
}

bool select_union::send_data(List<Item> &values)
{
  if (unit->offset_limit_cnt)
  {						// using limit offset,count
    unit->offset_limit_cnt--;
    return 0;
  }
  fill_record(table->field,values);
  if ((write_record(table,&info)))
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
81
    if (create_myisam_from_heap(table, tmp_table_param, info.last_errno, 0))
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
82 83 84 85 86 87 88 89 90
      return 1;
  }
  return 0;
}

bool select_union::send_eof()
{
  return 0;
}
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
91

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
92
bool select_union::flush()
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
93
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
94 95 96 97
  int error;
  if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
  {
    table->file->print_error(error,MYF(0));
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
98
    ::send_error(thd);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
99 100 101 102 103 104 105 106
    return 1;
  }
  return 0;
}

typedef JOIN * JOIN_P;
int st_select_lex_unit::prepare(THD *thd, select_result *result)
{
107 108 109 110 111 112
  DBUG_ENTER("st_select_lex_unit::prepare");

  if (prepared)
    DBUG_RETURN(0);
  prepared= 1;
    
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
113 114 115
  describe=(first_select()->options & SELECT_DESCRIBE) ? 1 : 0;
  res= 0;
  found_rows_for_union= false;
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
116
  TMP_TABLE_PARAM tmp_table_param;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
117 118
  this->thd= thd;
  this->result= result;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
119
  SELECT_LEX *lex_select_save= thd->lex.select;
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
120

121
  /* Global option */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
122
  if (((void*)(global_parameters)) == ((void*)this))
123
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
124
    found_rows_for_union= first_select()->options & OPTION_FOUND_ROWS && 
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
125
      !describe && global_parameters->select_limit;
126
    if (found_rows_for_union)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
127
      first_select()->options ^=  OPTION_FOUND_ROWS;
128
  }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
129
  item_list.empty();
130
  if (describe)
131
  {
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
    Item *item;
    item_list.push_back(new Item_empty_string("table",NAME_LEN));
    item_list.push_back(new Item_empty_string("type",10));
    item_list.push_back(item=new Item_empty_string("possible_keys",
						  NAME_LEN*MAX_KEY));
    item->maybe_null=1;
    item_list.push_back(item=new Item_empty_string("key",NAME_LEN));
    item->maybe_null=1;
    item_list.push_back(item=new Item_int("key_len",0,3));
    item->maybe_null=1;
    item_list.push_back(item=new Item_empty_string("ref",
						    NAME_LEN*MAX_REF_PARTS));
    item->maybe_null=1;
    item_list.push_back(new Item_real("rows",0.0,0,10));
    item_list.push_back(new Item_empty_string("Extra",255));
147
  }
148
  else
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
149 150
  {
    Item *item;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
151 152
    List_iterator<Item> it(first_select()->item_list);
    TABLE_LIST *first_table= (TABLE_LIST*) first_select()->table_list.first;
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
153 154 155 156

    /* Create a list of items that will be in the result set */
    while ((item= it++))
      if (item_list.push_back(item))
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
157
	goto err;
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
158
    if (setup_fields(thd,first_table,item_list,0,0,1))
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
159
      goto err;
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
160
  }
161

monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
162
  bzero((char*) &tmp_table_param,sizeof(tmp_table_param));
163
  tmp_table_param.field_count=item_list.elements;
164
  if (!(table= create_tmp_table(thd, &tmp_table_param, item_list,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
165 166
				(ORDER*) 0, !describe & 
				!thd->lex.union_option,
167
				1, 0,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
168
				(first_select()->options | thd->options |
169
				 TMP_TABLE_ALL_COLUMNS),
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
170
				this)))
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
171
    goto err;
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
172 173 174 175
  table->file->extra(HA_EXTRA_WRITE_CACHE);
  table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
  bzero((char*) &result_table_list,sizeof(result_table_list));
  result_table_list.db= (char*) "";
176
  result_table_list.real_name=result_table_list.alias= (char*) "union";
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
177 178 179
  result_table_list.table=table;

  if (!(union_result=new select_union(table)))
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
180
    goto err;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
181

182
  union_result->save_time_stamp=!describe;
183
  union_result->tmp_table_param=&tmp_table_param;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
184 185 186 187

  // prepare selects
  joins.empty();
  for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
188
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
189 190 191 192 193 194 195 196 197 198 199
    JOIN *join= new JOIN(thd, sl->item_list, 
			 sl->options | thd->options | SELECT_NO_UNLOCK | 
			 ((describe) ? SELECT_DESCRIBE : 0),
			 union_result);
    joins.push_back(new JOIN_P(join));
    thd->lex.select=sl;
    offset_limit_cnt= sl->offset_limit;
    select_limit_cnt= sl->select_limit+sl->offset_limit;
    if (select_limit_cnt < sl->select_limit)
      select_limit_cnt= HA_POS_ERROR;		// no limit
    if (select_limit_cnt == HA_POS_ERROR)
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
200 201
      sl->options&= ~OPTION_FOUND_ROWS;

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
202 203 204 205 206 207 208 209 210
    res= join->prepare((TABLE_LIST*) sl->table_list.first,
		       sl->where,
		       (sl->braces) ? 
		       (ORDER *)sl->order_list.first : (ORDER *) 0,
		       (ORDER*) sl->group_list.first,
		       sl->having,
		       (ORDER*) NULL,
		       sl, this, 0);
    if (res | thd->fatal_error)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
211
      goto err;
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
212
  }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
213
  thd->lex.select= lex_select_save;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
214
  DBUG_RETURN(res | thd->fatal_error);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
215 216 217
err:
  thd->lex.select= lex_select_save;
  DBUG_RETURN(-1);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
218 219 220 221 222 223 224 225 226
}

int st_select_lex_unit::exec()
{
  DBUG_ENTER("st_select_lex_unit::exec");
  if(depended || !item || !item->assigned())
  {
    if (optimized && item && item->assigned())
      item->assigned(0); // We will reinit & rexecute unit
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
227
    SELECT_LEX *lex_select_save= thd->lex.select;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
    for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
    {
      thd->lex.select=sl;
      offset_limit_cnt= sl->offset_limit;
      select_limit_cnt= sl->select_limit+sl->offset_limit;
      if (select_limit_cnt < sl->select_limit)
	select_limit_cnt= HA_POS_ERROR;		// no limit
      if (select_limit_cnt == HA_POS_ERROR)
	sl->options&= ~OPTION_FOUND_ROWS;

      if (!optimized)
	sl->join->optimize();
      else
	sl->join->reinit();

      sl->join->exec();
      res= sl->join->error;

      if (res)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
247 248
      {
	thd->lex.select= lex_select_save;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
249
	DBUG_RETURN(res);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
250
      }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
251
    }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
252
    thd->lex.select= lex_select_save;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
253 254 255
    optimized= 1;
  }

monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
256 257 258
  if (union_result->flush())
  {
    res= 1;					// Error is already sent
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
259
    DBUG_RETURN(res);
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
260
  }
261 262

  /* Send result to 'result' */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
263
  thd->lex.select = first_select();
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
264 265 266 267 268
  res =-1;
  {
    /* Create a list of fields in the temporary table */
    List_iterator<Item> it(item_list);
    Field **field;
269
#if 0
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
270 271
    List<Item_func_match> ftfunc_list;
    ftfunc_list.empty();
272
#else
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
273 274 275
    List<Item_func_match> empty_list;
    empty_list.empty();
    thd->lex.select_lex.ftfunc_list= &empty_list;
276
#endif
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
277 278 279 280 281 282 283

    for (field=table->field ; *field ; field++)
    {
      (void) it++;
      (void) it.replace(new Item_field(*field));
    }
    if (!thd->fatal_error)			// Check if EOM
284
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
285 286 287 288 289 290
      offset_limit_cnt= global_parameters->offset_limit;
      select_limit_cnt= global_parameters->select_limit+
	global_parameters->offset_limit;
      if (select_limit_cnt < global_parameters->select_limit)
	select_limit_cnt= HA_POS_ERROR;		// no limit
      if (select_limit_cnt == HA_POS_ERROR)
291
	thd->options&= ~OPTION_FOUND_ROWS;
292
      if (describe)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
293
	select_limit_cnt= HA_POS_ERROR;		// no limit
294 295
      res= mysql_select(thd,&result_table_list,
			item_list, NULL,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
296 297 298
			(describe) ? 
			0: 
			(ORDER*)global_parameters->order_list.first,
299
			(ORDER*) NULL, NULL, (ORDER*) NULL,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
300
			thd->options, result, this, first_select(), 1);
301
      if (found_rows_for_union && !res)
302
	thd->limit_found_rows = (ulonglong)table->file->records;
303
    }
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
304
  }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
305
  thd->lex.select_lex.ftfunc_list= &thd->lex.select_lex.ftfunc_list_alloc;
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
306 307 308
  DBUG_RETURN(res);
}

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
309
int st_select_lex_unit::cleanup()
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
310
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
311 312 313 314 315 316 317 318
  DBUG_ENTER("st_select_lex_unit::cleanup");
  delete union_result;
  free_tmp_table(thd,table);
  table= 0; // Safety
  
  List_iterator<JOIN*> j(joins);
  JOIN** join;
  while ((join= j++))
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
319
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
320 321 322
    (*join)->cleanup(thd);
    delete *join;
    delete join;
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
323
  }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
324 325
  joins.empty();
  DBUG_RETURN(0);
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
326
}