sql_delete.cc 16.5 KB
Newer Older
1
/* Copyright (C) 2000 MySQL AB
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3 4 5 6
   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.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
7

bk@work.mysql.com's avatar
bk@work.mysql.com committed
8 9 10 11
   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.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
12

bk@work.mysql.com's avatar
bk@work.mysql.com committed
13 14 15 16
   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 */

17

bk@work.mysql.com's avatar
bk@work.mysql.com committed
18
/*
19
  Delete of records and truncate of tables.
20

21
  Multi-table deletes were introduced by Monty and Sinisa
bk@work.mysql.com's avatar
bk@work.mysql.com committed
22 23
*/

24 25


26
#include "mysql_priv.h"
27
#include "ha_innodb.h"
28
#include "sql_select.h"
bk@work.mysql.com's avatar
bk@work.mysql.com committed
29

30
int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
31
                 ha_rows limit, ulong options)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
32 33 34
{
  int		error;
  TABLE		*table;
35
  SQL_SELECT	*select=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
36 37
  READ_RECORD	info;
  bool 		using_limit=limit != HA_POS_ERROR;
38
  bool		transactional_table, log_delayed, safe_update;
39
  ha_rows	deleted;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
40 41
  DBUG_ENTER("mysql_delete");

42
  if (((safe_update=thd->options & OPTION_SAFE_UPDATES)) && !conds)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
43 44 45 46 47
  {
    send_error(&thd->net,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE);
    DBUG_RETURN(1);
  }

48
  if (!(table = open_ltable(thd, table_list, table_list->lock_type)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
49
    DBUG_RETURN(-1);
50
  table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
51 52
  thd->proc_info="init";
  table->map=1;
53
  if (setup_conds(thd,table_list,&conds) || setup_ftfuncs(thd))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
54 55
    DBUG_RETURN(-1);

56
  /* Test if the user wants to delete all rows */
57
  if (!using_limit && (!conds || (conds->const_item() && conds->val_int())) &&
58
      !(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) && !safe_update)
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
  {
    deleted= table->file->records;
    if (!(error=table->file->delete_all_rows()))
    {
      error= -1;				// ok
      goto cleanup;
    }
    if (error != HA_ERR_WRONG_COMMAND)
    {
      table->file->print_error(error,MYF(0));
      error=0;
      goto cleanup;
    }
    /* Handler didn't support fast delete; Delete rows one by one */
  }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
75 76 77 78
  table->used_keys=table->quick_keys=0;		// Can't use 'only index'
  select=make_select(table,0,0,conds,&error);
  if (error)
    DBUG_RETURN(-1);
79 80
  if ((select && select->check_quick(thd,
				     test(thd->options & OPTION_SAFE_UPDATES),
81
				     limit)) || 
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
82
      !limit)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
83 84 85
  {
    delete select;
    send_ok(&thd->net,0L);
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
86
    DBUG_RETURN(0);				// Nothing to delete
bk@work.mysql.com's avatar
bk@work.mysql.com committed
87 88 89
  }

  /* If running in safe sql mode, don't allow updates without keys */
90
  if (!table->quick_keys)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
91
  {
92
    thd->lex.select_lex.options|=QUERY_NO_INDEX_USED;
93
    if (safe_update && !using_limit)
94 95 96 97 98
    {
      delete select;
      send_error(&thd->net,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE);
      DBUG_RETURN(1);
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
99
  }
100
  if (options & OPTION_QUICK)
101 102 103 104 105 106 107 108 109
    (void) table->file->extra(HA_EXTRA_QUICK);

  if (order)
  {
    uint         length;
    SORT_FIELD  *sortorder;
    TABLE_LIST   tables;
    List<Item>   fields;
    List<Item>   all_fields;
110
    ha_rows examined_rows;
111 112 113 114 115 116 117 118

    bzero((char*) &tables,sizeof(tables));
    tables.table = table;

    table->io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
                                             MYF(MY_FAE | MY_ZEROFILL));
    if (setup_order(thd, &tables, fields, all_fields, order) ||
        !(sortorder=make_unireg_sortorder(order, &length)) ||
119
        (table->found_records = filesort(table, sortorder, length,
120 121
                                        (SQL_SELECT *) 0, 0L, HA_POS_ERROR,
					 &examined_rows))
122 123 124 125 126 127 128 129
        == HA_POS_ERROR)
    {
      delete select;
      DBUG_RETURN(-1);		// This will force out message
    }
  }

  init_read_record(&info,thd,table,select,1,1);
130
  deleted=0L;
131
  init_ftfuncs(thd,1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
  thd->proc_info="updating";
  while (!(error=info.read_record(&info)) && !thd->killed)
  {
    if (!(select && select->skipp_record()))
    {
      if (!(error=table->file->delete_row(table->record[0])))
      {
	deleted++;
	if (!--limit && using_limit)
	{
	  error= -1;
	  break;
	}
      }
      else
      {
	table->file->print_error(error,MYF(0));
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
149 150 151 152 153 154 155 156 157
	/*
	  In < 4.0.14 we set the error number to 0 here, but that
	  was not sensible, because then MySQL would not roll back the
	  failed DELETE, and also wrote it to the binlog. For MyISAM
	  tables a DELETE probably never should fail (?), but for
	  InnoDB it can fail in a FOREIGN KEY error or an
	  out-of-tablespace error.
	*/
 	error= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
158 159 160
	break;
      }
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
161 162
    else
      table->file->unlock_row();  // Row failed selection, release lock on it
bk@work.mysql.com's avatar
bk@work.mysql.com committed
163 164 165
  }
  thd->proc_info="end";
  end_read_record(&info);
166
  free_io_cache(table);				// Will not do any harm
167
  if (options & OPTION_QUICK)
168
    (void) table->file->extra(HA_EXTRA_NORMAL);
169 170

cleanup:
171 172 173
  transactional_table= table->file->has_transactions();
  log_delayed= (transactional_table || table->tmp_table);
  if (deleted && (error <= 0 || !transactional_table))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
174
  {
175 176 177
    mysql_update_log.write(thd,thd->query, thd->query_length);
    if (mysql_bin_log.is_open())
    {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
178 179
      if (error <= 0)
        thd->clear_error();
180
      Query_log_event qinfo(thd, thd->query, thd->query_length, 
181 182
			    log_delayed);
      if (mysql_bin_log.write(&qinfo) && transactional_table)
183
	error=1;
184
    }
185
    if (!log_delayed)
186
      thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
187
  }
188 189 190 191 192
  if (transactional_table)
  {
    if (ha_autocommit_or_rollback(thd,error >= 0))
      error=1;
  }
193

194
  /*
195 196
    Store table for future invalidation  or invalidate it in
    the query cache if something changed
197
  */
198
  if (deleted)
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
199
  {
200
    query_cache_invalidate3(thd, table_list, 1);
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
201
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
  if (thd->lock)
  {
    mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;
  }
  delete select;
  if (error >= 0)				// Fatal error
    send_error(&thd->net,thd->killed ? ER_SERVER_SHUTDOWN: 0);
  else
  {
    send_ok(&thd->net,deleted);
    DBUG_PRINT("info",("%d records deleted",deleted));
  }
  DBUG_RETURN(0);
}


219
/***************************************************************************
220
  Delete multiple tables from join 
221 222
***************************************************************************/

223
#define MEM_STRIP_BUF_SIZE current_thd->variables.sortbuff_size
224

225
extern "C" int refposcmp2(void* arg, const void *a,const void *b)
226
{
227
  /* arg is a pointer to file->ref_length */
228
  return memcmp(a,b, *(int*) arg);
229
}
230

231 232
multi_delete::multi_delete(THD *thd_arg, TABLE_LIST *dt,
			   uint num_of_tables_arg)
233
  : delete_tables(dt), thd(thd_arg), deleted(0),
234
    num_of_tables(num_of_tables_arg), error(0),
235
    do_delete(0), transactional_tables(0), log_delayed(0), normal_tables(0)
236
{
237 238 239 240 241 242 243 244
  tempfiles = (Unique **) sql_calloc(sizeof(Unique *) * (num_of_tables-1));
}


int
multi_delete::prepare(List<Item> &values)
{
  DBUG_ENTER("multi_delete::prepare");
245
  do_delete= 1;
246
  thd->proc_info="deleting from main table";
247 248 249
  DBUG_RETURN(0);
}

250

251
bool
252 253
multi_delete::initialize_tables(JOIN *join)
{
254
  TABLE_LIST *walk;
255 256 257 258 259 260
  Unique **tempfiles_ptr;
  DBUG_ENTER("initialize_tables");

  if ((thd->options & OPTION_SAFE_UPDATES) && error_if_full_join(join))
    DBUG_RETURN(1);

261 262 263
  table_map tables_to_delete_from=0;
  for (walk= delete_tables ; walk ; walk=walk->next)
    tables_to_delete_from|= walk->table->map;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
264

265
  walk= delete_tables;
266 267 268 269
  for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables;
       tab < end;
       tab++)
  {
270
    if (tab->table->map & tables_to_delete_from)
271
    {
272
      /* We are going to delete from this table */
273
      TABLE *tbl=walk->table=tab->table;
274
      walk=walk->next;
275
      /* Don't use KEYREAD optimization on this table */
276
      tbl->no_keyread=1;
277
      tbl->used_keys= 0;
278 279 280 281 282 283
      if (tbl->file->has_transactions())
	log_delayed= transactional_tables= 1;
      else if (tbl->tmp_table != NO_TMP_TABLE)
	log_delayed= 1;
      else
	normal_tables= 1;
284 285
    }
  }
286
  walk= delete_tables;
287 288
  tempfiles_ptr= tempfiles;
  for (walk=walk->next ; walk ; walk=walk->next)
289 290
  {
    TABLE *table=walk->table;
291 292 293 294
    *tempfiles_ptr++= new Unique (refposcmp2,
				  (void *) &table->file->ref_length,
				  table->file->ref_length,
				  MEM_STRIP_BUF_SIZE);
295
  }
296
  init_ftfuncs(thd,1);
297
  DBUG_RETURN(thd->fatal_error != 0);
298
}
299

300

301 302
multi_delete::~multi_delete()
{
303 304 305
  for (table_being_deleted=delete_tables ;
       table_being_deleted ;
       table_being_deleted=table_being_deleted->next)
306 307
  {
    TABLE *t=table_being_deleted->table;
308
    free_io_cache(t);				// Alloced by unique
309 310
    t->no_keyread=0;
  }
311

312
  for (uint counter= 0; counter < num_of_tables-1; counter++)
313
  {
314
    if (tempfiles[counter])
315 316
      delete tempfiles[counter];
  }
317 318
}

319

320 321
bool multi_delete::send_data(List<Item> &values)
{
322
  int secure_counter= -1;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
323 324
  DBUG_ENTER("multi_delete::send_data");

325 326 327
  for (table_being_deleted=delete_tables ;
       table_being_deleted ;
       table_being_deleted=table_being_deleted->next, secure_counter++)
328 329
  {
    TABLE *table=table_being_deleted->table;
330 331 332 333 334 335

    /* Check if we are using outer join and we didn't find the row */
    if (table->status & (STATUS_NULL_ROW | STATUS_DELETED))
      continue;

    table->file->position(table->record[0]);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
336

337
    if (secure_counter < 0)
338
    {
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
339
      /* If this is the table we are scanning */
340 341 342
      table->status|= STATUS_DELETED;
      if (!(error=table->file->delete_row(table->record[0])))
	deleted++;
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
343
      else if (!table_being_deleted->next)
344
      {
345
	table->file->print_error(error,MYF(0));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
346
	DBUG_RETURN(1);
347 348 349 350
      }
    }
    else
    {
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
351
      error=tempfiles[secure_counter]->unique_add((char*) table->file->ref);
352 353 354
      if (error)
      {
	error=-1;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
355
	DBUG_RETURN(1);
356
      }
357 358
    }
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
359
  DBUG_RETURN(0);
360 361 362 363
}

void multi_delete::send_error(uint errcode,const char *err)
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
364 365
  DBUG_ENTER("multi_delete::send_error");

366
  /* First send error what ever it is ... */
367
  ::send_error(&thd->net,errcode,err);
368

369 370
  /* If nothing deleted return */
  if (!deleted)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
371
    DBUG_VOID_RETURN;
372

373
  /* Something already deleted so we have to invalidate cache */
374 375
  query_cache_invalidate3(thd, delete_tables, 1);

376 377 378 379 380
  /* Below can happen when thread is killed early ... */
  if (!table_being_deleted)
    table_being_deleted=delete_tables;

  /*
381 382
    If rows from the first table only has been deleted and it is
    transactional, just do rollback.
383 384 385 386
    The same if all tables are transactional, regardless of where we are.
    In all other cases do attempt deletes ...
  */
  if ((table_being_deleted->table->file->has_transactions() &&
387
       table_being_deleted == delete_tables) || !normal_tables)
388
    ha_rollback_stmt(thd);
389
  else if (do_delete)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
390 391 392 393
  {
    VOID(do_deletes(1));
  }
  DBUG_VOID_RETURN;
394 395
}

396

397 398 399 400 401 402 403
/*
  Do delete from other tables.
  Returns values:
	0 ok
	1 error
*/

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
404
int multi_delete::do_deletes(bool from_send_error)
405
{
406
  int local_error= 0, counter= 0;
407
  DBUG_ENTER("do_deletes");
408

409 410
  if (from_send_error)
  {
411 412 413 414
    /* Found out table number for 'table_being_deleted' */
    for (TABLE_LIST *aux=delete_tables;
	 aux != table_being_deleted;
	 aux=aux->next)
415 416 417 418
      counter++;
  }
  else
    table_being_deleted = delete_tables;
419

420
  do_delete= 0;
421 422 423
  for (table_being_deleted=table_being_deleted->next;
       table_being_deleted ;
       table_being_deleted=table_being_deleted->next, counter++)
424
  { 
425 426 427
    TABLE *table = table_being_deleted->table;
    if (tempfiles[counter]->get(table))
    {
428
      local_error=1;
429 430 431 432
      break;
    }

    READ_RECORD	info;
433 434 435 436 437 438
    init_read_record(&info,thd,table,NULL,0,1);
    /*
      Ignore any rows not found in reference tables as they may already have
      been deleted by foreign key handling
    */
    info.ignore_not_found_rows= 1;
monty@butch's avatar
merge  
monty@butch committed
439
    while (!(local_error=info.read_record(&info)) && !thd->killed)
440
    {
441
      if ((local_error=table->file->delete_row(table->record[0])))
442
      {
443
	table->file->print_error(local_error,MYF(0));
444
	break;
445
      }
446
      deleted++;
447
    }
448
    end_read_record(&info);
449 450
    if (local_error == -1)				// End of file
      local_error = 0;
451
  }
452
  DBUG_RETURN(local_error);
453 454
}

455

456
/*
457 458
  Send ok to the client

459 460 461 462
  return:  0 sucess
	   1 error
*/

463 464
bool multi_delete::send_eof()
{
465
  thd->proc_info="deleting from reference tables";
466 467

  /* Does deletes for the last n - 1 tables, returns 0 if ok */
468
  int local_error= do_deletes(0);		// returns 0 if success
469

470
  /* reset used flags */
471
  thd->proc_info="end";
472

473 474 475 476 477 478
  /*
    Write the SQL statement to the binlog if we deleted
    rows and we succeeded, or also in an error case when there
    was a non-transaction-safe table involved, since
    modifications in it cannot be rolled back.
  */
479
  if (deleted && (error <= 0 || normal_tables))
480 481
  {
    mysql_update_log.write(thd,thd->query,thd->query_length);
482 483
    if (mysql_bin_log.is_open())
    {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
484 485
      if (error <= 0)
        thd->clear_error();
486 487 488
      Query_log_event qinfo(thd, thd->query, thd->query_length,
			    log_delayed);
      if (mysql_bin_log.write(&qinfo) && !normal_tables)
489
	local_error=1;  // Log write failed: roll back the SQL statement
490
    }
491 492
    if (!log_delayed)
      thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
493
  }
494 495 496 497 498 499 500 501
  /* Commit or rollback the current SQL statement */ 
  if (transactional_tables)
    if (ha_autocommit_or_rollback(thd,local_error > 0))
      local_error=1;
  
  if (deleted)
    query_cache_invalidate3(thd, delete_tables, 1);

monty@butch's avatar
merge  
monty@butch committed
502
  if (local_error)
503 504 505
    ::send_error(&thd->net);
  else
    ::send_ok(&thd->net,deleted);
506 507
  return 0;
}
508 509 510


/***************************************************************************
511
  TRUNCATE TABLE
512 513 514 515 516 517 518 519 520 521 522
****************************************************************************/

/*
  Optimize delete of all rows by doing a full generate of the table
  This will work even if the .ISM and .ISD tables are destroyed

  dont_send_ok should be set if:
  - We should always wants to generate the table (even if the table type
    normally can't safely do this.
  - We don't want an ok to be sent to the end user.
  - We don't want to log the truncate command
523
  - If we want to have a name lock on the table on exit without errors.
524 525 526 527 528 529 530 531 532 533 534
*/

int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
{
  HA_CREATE_INFO create_info;
  char path[FN_REFLEN];
  TABLE **table_ptr;
  int error;
  DBUG_ENTER("mysql_truncate");

  /* If it is a temporary table, close and regenerate it */
535 536
  if (!dont_send_ok && (table_ptr=find_temporary_table(thd,table_list->db,
						       table_list->real_name)))
537 538 539 540 541 542 543 544 545 546 547 548 549
  {
    TABLE *table= *table_ptr;
    HA_CREATE_INFO create_info;
    table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
    bzero((char*) &create_info,sizeof(create_info));
    create_info.auto_increment_value= table->file->auto_increment_value;
    db_type table_type=table->db_type;

    strmov(path,table->path);
    *table_ptr= table->next;			// Unlink table from list
    close_temporary(table,0);
    *fn_ext(path)=0;				// Remove the .frm extension
    ha_create_table(path, &create_info,1);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
550
    // We don't need to call invalidate() because this table is not in cache
551 552 553
    if ((error= (int) !(open_temporary_table(thd, path, table_list->db,
					     table_list->real_name, 1))))
      (void) rm_temporary_table(table_type, path);
554 555 556
    /*
      Sasha: if we return here we will not have binloged the truncation and
      we will not send_ok() to the client.
557 558
    */
    goto end; 
559 560 561 562 563 564 565 566 567 568 569
  }

  (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,table_list->db,
		 table_list->real_name,reg_ext);
  fn_format(path,path,"","",4);

  if (!dont_send_ok)
  {
    db_type table_type;
    if ((table_type=get_table_type(path)) == DB_TYPE_UNKNOWN)
    {
570 571
      my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db,
	       table_list->real_name);
572 573 574 575 576
      DBUG_RETURN(-1);
    }
    if (!ha_supports_generate(table_type))
    {
      /* Probably InnoDB table */
577 578 579
      table_list->lock_type= TL_WRITE;
      DBUG_RETURN(mysql_delete(thd, table_list, (COND*) 0, (ORDER*) 0,
			       HA_POS_ERROR, 0));
580 581 582 583 584 585 586 587
    }
    if (lock_and_wait_for_table_name(thd, table_list))
      DBUG_RETURN(-1);
  }

  bzero((char*) &create_info,sizeof(create_info));
  *fn_ext(path)=0;				// Remove the .frm extension
  error= ha_create_table(path,&create_info,1) ? -1 : 0;
588
  query_cache_invalidate3(thd, table_list, 0); 
589

590
end:
591
  if (!dont_send_ok)
592
  {
593
    if (!error)
594
    {
595 596 597
      mysql_update_log.write(thd,thd->query,thd->query_length);
      if (mysql_bin_log.is_open())
      {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
598
        thd->clear_error();
599 600
	Query_log_event qinfo(thd, thd->query, thd->query_length,
			      thd->tmp_table);
601 602 603
	mysql_bin_log.write(&qinfo);
      }
      send_ok(&thd->net);		// This should return record count
604
    }
605
    VOID(pthread_mutex_lock(&LOCK_open));
606
    unlock_table_name(thd, table_list);
607
    VOID(pthread_mutex_unlock(&LOCK_open));
608
  }
609
  else if (error)
610 611
  {
    VOID(pthread_mutex_lock(&LOCK_open));
612
    unlock_table_name(thd, table_list);
613 614
    VOID(pthread_mutex_unlock(&LOCK_open));
  }
615 616
  DBUG_RETURN(error ? -1 : 0);
}