sql_delete.cc 16.2 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, const_cond; 
39
  ha_rows	deleted;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
40 41
  DBUG_ENTER("mysql_delete");

42
  if (!(table = open_ltable(thd, table_list, table_list->lock_type)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
43
    DBUG_RETURN(-1);
44
  table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
45 46
  thd->proc_info="init";
  table->map=1;
47 48
  if (setup_conds(thd,table_list,&conds) || 
      setup_ftfuncs(&thd->lex.select_lex))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
49 50
    DBUG_RETURN(-1);

51 52 53 54 55 56 57 58
  const_cond= (!conds || conds->const_item());
  safe_update=test(thd->options & OPTION_SAFE_UPDATES);
  if (safe_update && const_cond)
  {
    send_error(thd,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE);
    DBUG_RETURN(1);
  }

59
  /* Test if the user wants to delete all rows */
60 61
  if (!using_limit && const_cond &&
      !(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)))
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
  {
    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
78 79 80 81
  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);
82
  if ((select && select->check_quick(safe_update, limit)) || !limit)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
83 84
  {
    delete select;
85
    send_ok(thd,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
    {
      delete select;
96
      send_error(thd,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE);
97 98
      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 120
        (table->found_records = filesort(thd, table, sortorder, length,
                                        (SQL_SELECT *) 0, HA_POS_ERROR,
121
					 &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, &thd->lex.select_lex, 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 149 150 151 152
  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));
	error=0;
	break;
      }
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
153 154
    else
      table->file->unlock_row();  // Row failed selection, release lock on it
bk@work.mysql.com's avatar
bk@work.mysql.com committed
155 156 157
  }
  thd->proc_info="end";
  end_read_record(&info);
158
  free_io_cache(table);				// Will not do any harm
159
  if (options & OPTION_QUICK)
160
    (void) table->file->extra(HA_EXTRA_NORMAL);
161 162

cleanup:
163 164 165
  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
166
  {
167 168 169
    mysql_update_log.write(thd,thd->query, thd->query_length);
    if (mysql_bin_log.is_open())
    {
170
      Query_log_event qinfo(thd, thd->query, thd->query_length, 
171 172
			    log_delayed);
      if (mysql_bin_log.write(&qinfo) && transactional_table)
173
	error=1;
174
    }
175
    if (!log_delayed)
176
      thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
177
  }
178 179 180 181 182 183 184 185 186 187 188 189 190
  if (transactional_table)
  {
    if (ha_autocommit_or_rollback(thd,error >= 0))
      error=1;
  }
  /*
    Only invalidate the query cache if something changed or if we
    didn't commit the transacion (query cache is automaticly
    invalidated on commit)
  */
  if (deleted &&
      (!transactional_table ||
       thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
191
  {
192
    query_cache_invalidate3(thd, table_list, 1);
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
193
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
194 195 196 197 198 199 200
  if (thd->lock)
  {
    mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;
  }
  delete select;
  if (error >= 0)				// Fatal error
201
    send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN: 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
202 203
  else
  {
204
    send_ok(thd,deleted);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
205 206 207 208 209 210
    DBUG_PRINT("info",("%d records deleted",deleted));
  }
  DBUG_RETURN(0);
}


211
/***************************************************************************
212
  Delete multiple tables from join 
213 214
***************************************************************************/

215
#define MEM_STRIP_BUF_SIZE current_thd->variables.sortbuff_size
216

217
extern "C" int refposcmp2(void* arg, const void *a,const void *b)
218
{
219
  return memcmp(a,b, *(int*) arg);
220
}
221

222 223 224
multi_delete::multi_delete(THD *thd_arg, TABLE_LIST *dt,
			   uint num_of_tables_arg)
  : delete_tables (dt), thd(thd_arg), deleted(0),
225
    num_of_tables(num_of_tables_arg), error(0),
226
    do_delete(0), transactional_tables(0), log_delayed(0), normal_tables(0)
227
{
228 229 230 231 232
  tempfiles = (Unique **) sql_calloc(sizeof(Unique *) * (num_of_tables-1));
}


int
233
multi_delete::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
234 235
{
  DBUG_ENTER("multi_delete::prepare");
236
  unit= u;
237 238 239 240 241 242 243
  do_delete = true;   
  thd->proc_info="deleting from main table";

  if (thd->options & OPTION_SAFE_UPDATES)
  {
    TABLE_LIST *table_ref;
    for (table_ref=delete_tables;  table_ref; table_ref=table_ref->next)
244
    {
245 246
      TABLE *table=table_ref->table;
      if ((thd->options & OPTION_SAFE_UPDATES) && !table->quick_keys)
247
      {
248
	my_error(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,MYF(0));
249 250 251 252 253 254 255
	DBUG_RETURN(1);
      }
    }
  }
  DBUG_RETURN(0);
}

256 257 258 259

void
multi_delete::initialize_tables(JOIN *join)
{
260
  int counter=0;
261 262 263 264
  TABLE_LIST *walk;
  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
265

266
  walk= delete_tables;
267 268 269 270
  for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables;
       tab < end;
       tab++)
  {
271
    if (tab->table->map & tables_to_delete_from)
272
    {
273
      /* We are going to delete from this table */
274
      TABLE *tbl=walk->table=tab->table;
275
      /* Don't use KEYREAD optimization on this table */
276
      tbl->no_keyread=1;
277
      walk=walk->next;
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 287 288 289 290 291 292 293 294 295 296 297 298
  walk= delete_tables;
  walk->table->used_keys=0;
  for (walk=walk->next ; walk ; walk=walk->next, counter++)
  {
    tables_to_delete_from|= walk->table->map;
    TABLE *table=walk->table;
  /* Don't use key read with MULTI-TABLE-DELETE */
    table->used_keys=0;
    tempfiles[counter] = new Unique (refposcmp2,
				     (void *) &table->file->ref_length,
				     table->file->ref_length,
				     MEM_STRIP_BUF_SIZE);
  }
299
  init_ftfuncs(thd, thd->lex.current_select->select_lex(), 1);
300
}
301

302

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

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

321

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

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

    /* 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
338

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

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

368
  /* First send error what ever it is ... */
369
  ::send_error(thd,errcode,err);
370

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

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

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

  /*
383 384
    If rows from the first table only has been deleted and it is
    transactional, just do rollback.
385 386 387 388
    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() &&
389
       table_being_deleted == delete_tables) || !normal_tables)
390
    ha_rollback_stmt(thd);
391
  else if (do_delete)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
392 393 394 395
  {
    VOID(do_deletes(1));
  }
  DBUG_VOID_RETURN;
396 397
}

398

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

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

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

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

    READ_RECORD	info;
    init_read_record(&info,thd,table,NULL,0,0);
monty@butch's avatar
merge  
monty@butch committed
435
    while (!(local_error=info.read_record(&info)) && !thd->killed)
436
    {
437
      if ((local_error=table->file->delete_row(table->record[0])))
438
      {
439
	table->file->print_error(local_error,MYF(0));
440
	break;
441
      }
442
      deleted++;
443
    }
444
    end_read_record(&info);
445 446
    if (local_error == -1)				// End of file
      local_error = 0;
447
  }
448
  return local_error;
449 450
}

451

452
/*
453 454
  Send ok to the client

455 456 457 458
  return:  0 sucess
	   1 error
*/

459 460
bool multi_delete::send_eof()
{
461
  thd->proc_info="deleting from reference tables";
462 463

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

466
  /* reset used flags */
467
  thd->proc_info="end";
468 469


470 471 472 473 474 475
  /*
    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.
  */
476
  if (deleted)
477 478
  {
    mysql_update_log.write(thd,thd->query,thd->query_length);
479 480
    if (mysql_bin_log.is_open())
    {
481 482 483
      Query_log_event qinfo(thd, thd->query, thd->query_length,
			    log_delayed);
      if (mysql_bin_log.write(&qinfo) && !normal_tables)
484
	local_error=1;  // Log write failed: roll back the SQL statement
485
    }
486
    /* Commit or rollback the current SQL statement */ 
487
    VOID(ha_autocommit_or_rollback(thd,local_error > 0));
488

489
    query_cache_invalidate3(thd, delete_tables, 1);
490
  }
monty@butch's avatar
merge  
monty@butch committed
491
  if (local_error)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
492
    ::send_error(thd);
493
  else
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
494
    ::send_ok(thd, deleted);
495 496
  return 0;
}
497 498 499


/***************************************************************************
500
  TRUNCATE TABLE
501 502 503 504 505 506 507 508 509 510 511
****************************************************************************/

/*
  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
512
  - If we want to have a name lock on the table on exit without errors.
513 514 515 516 517 518 519 520 521 522 523
*/

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 */
524 525
  if (!dont_send_ok && (table_ptr=find_temporary_table(thd,table_list->db,
						       table_list->real_name)))
526 527 528 529 530 531
  {
    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;
532
    create_info.table_charset=default_charset_info;
533

534
    db_type table_type=table->db_type;
535 536 537 538 539
    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
540
    // We don't need to call invalidate() because this table is not in cache
541 542 543
    if ((error= (int) !(open_temporary_table(thd, path, table_list->db,
					     table_list->real_name, 1))))
      (void) rm_temporary_table(table_type, path);
544
    /*
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
545 546
      If we return here we will not have logged the truncation to the bin log
      and we will not send_ok() to the client.
547 548
    */
    goto end; 
549 550 551 552 553 554 555 556 557 558 559
  }

  (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)
    {
560 561
      my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db,
	       table_list->real_name);
562 563 564 565 566
      DBUG_RETURN(-1);
    }
    if (!ha_supports_generate(table_type))
    {
      /* Probably InnoDB table */
567 568 569
      table_list->lock_type= TL_WRITE;
      DBUG_RETURN(mysql_delete(thd, table_list, (COND*) 0, (ORDER*) 0,
			       HA_POS_ERROR, 0));
570 571 572 573 574 575
    }
    if (lock_and_wait_for_table_name(thd, table_list))
      DBUG_RETURN(-1);
  }

  bzero((char*) &create_info,sizeof(create_info));
576 577
  create_info.table_charset=default_charset_info;

578 579
  *fn_ext(path)=0;				// Remove the .frm extension
  error= ha_create_table(path,&create_info,1) ? -1 : 0;
580
  query_cache_invalidate3(thd, table_list, 0); 
581

582
end:
583
  if (!dont_send_ok)
584
  {
585
    if (!error)
586
    {
587 588 589
      mysql_update_log.write(thd,thd->query,thd->query_length);
      if (mysql_bin_log.is_open())
      {
590 591
	Query_log_event qinfo(thd, thd->query, thd->query_length,
			      thd->tmp_table);
592 593
	mysql_bin_log.write(&qinfo);
      }
594
      send_ok(thd);		// This should return record count
595
    }
596
    VOID(pthread_mutex_lock(&LOCK_open));
597
    unlock_table_name(thd, table_list);
598
    VOID(pthread_mutex_unlock(&LOCK_open));
599
  }
600
  else if (error)
601 602
  {
    VOID(pthread_mutex_lock(&LOCK_open));
603
    unlock_table_name(thd, table_list);
604 605
    VOID(pthread_mutex_unlock(&LOCK_open));
  }
606 607
  DBUG_RETURN(error ? -1 : 0);
}