~ubuntu-branches/ubuntu/precise/mysql-5.1/precise

« back to all changes in this revision

Viewing changes to sql/sql_handler.cc

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Tretkowski
  • Date: 2010-03-17 14:56:02 UTC
  • Revision ID: james.westby@ubuntu.com-20100317145602-x7e30l1b2sb5s6w6
Tags: upstream-5.1.45
ImportĀ upstreamĀ versionĀ 5.1.45

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2000-2004 MySQL AB
 
2
   This program is free software; you can redistribute it and/or modify
 
3
   it under the terms of the GNU General Public License as published by
 
4
   the Free Software Foundation; version 2 of the License.
 
5
 
 
6
   This program is distributed in the hope that it will be useful,
 
7
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
8
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
9
   GNU General Public License for more details.
 
10
 
 
11
   You should have received a copy of the GNU General Public License
 
12
   along with this program; if not, write to the Free Software
 
13
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
14
 
 
15
 
 
16
/* HANDLER ... commands - direct access to ISAM */
 
17
 
 
18
/* TODO:
 
19
  HANDLER blabla OPEN [ AS foobar ] [ (column-list) ]
 
20
 
 
21
  the most natural (easiest, fastest) way to do it is to
 
22
  compute List<Item> field_list not in mysql_ha_read
 
23
  but in mysql_ha_open, and then store it in TABLE structure.
 
24
 
 
25
  The problem here is that mysql_parse calls free_item to free all the
 
26
  items allocated at the end of every query. The workaround would to
 
27
  keep two item lists per THD - normal free_list and handler_items.
 
28
  The second is to be freeed only on thread end. mysql_ha_open should
 
29
  then do { handler_items=concat(handler_items, free_list); free_list=0; }
 
30
 
 
31
  But !!! do_command calls free_root at the end of every query and frees up
 
32
  all the sql_alloc'ed memory. It's harder to work around...
 
33
*/
 
34
 
 
35
/*
 
36
  There are two containers holding information about open handler tables.
 
37
  The first is 'thd->handler_tables'. It is a linked list of TABLE objects.
 
38
  It is used like 'thd->open_tables' in the table cache. The trick is to
 
39
  exchange these two lists during open and lock of tables. Thus the normal
 
40
  table cache code can be used.
 
41
  The second container is a HASH. It holds objects of the type TABLE_LIST.
 
42
  Despite its name, no lists of tables but only single structs are hashed
 
43
  (the 'next' pointer is always NULL). The reason for theis second container
 
44
  is, that we want handler tables to survive FLUSH TABLE commands. A table
 
45
  affected by FLUSH TABLE must be closed so that other threads are not
 
46
  blocked by handler tables still in use. Since we use the normal table cache
 
47
  functions with 'thd->handler_tables', the closed tables are removed from
 
48
  this list. Hence we need the original open information for the handler
 
49
  table in the case that it is used again. This information is handed over
 
50
  to mysql_ha_open() as a TABLE_LIST. So we store this information in the
 
51
  second container, where it is not affected by FLUSH TABLE. The second
 
52
  container is implemented as a hash for performance reasons. Consequently,
 
53
  we use it not only for re-opening a handler table, but also for the
 
54
  HANDLER ... READ commands. For this purpose, we store a pointer to the
 
55
  TABLE structure (in the first container) in the TBALE_LIST object in the
 
56
  second container. When the table is flushed, the pointer is cleared.
 
57
*/
 
58
 
 
59
#include "mysql_priv.h"
 
60
#include "sql_select.h"
 
61
#include <assert.h>
 
62
 
 
63
#define HANDLER_TABLES_HASH_SIZE 120
 
64
 
 
65
static enum enum_ha_read_modes rkey_to_rnext[]=
 
66
{ RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV };
 
67
 
 
68
/*
 
69
  Get hash key and hash key length.
 
70
 
 
71
  SYNOPSIS
 
72
    mysql_ha_hash_get_key()
 
73
    tables                      Pointer to the hash object.
 
74
    key_len_p   (out)           Pointer to the result for key length.
 
75
    first                       Unused.
 
76
 
 
77
  DESCRIPTION
 
78
    The hash object is an TABLE_LIST struct.
 
79
    The hash key is the alias name.
 
80
    The hash key length is the alias name length plus one for the
 
81
    terminateing NUL character.
 
82
 
 
83
  RETURN
 
84
    Pointer to the TABLE_LIST struct.
 
85
*/
 
86
 
 
87
static char *mysql_ha_hash_get_key(TABLE_LIST *tables, size_t *key_len_p,
 
88
                                   my_bool first __attribute__((unused)))
 
89
{
 
90
  *key_len_p= strlen(tables->alias) + 1 ; /* include '\0' in comparisons */
 
91
  return tables->alias;
 
92
}
 
93
 
 
94
 
 
95
/*
 
96
  Free an hash object.
 
97
 
 
98
  SYNOPSIS
 
99
    mysql_ha_hash_free()
 
100
    tables                      Pointer to the hash object.
 
101
 
 
102
  DESCRIPTION
 
103
    The hash object is an TABLE_LIST struct.
 
104
 
 
105
  RETURN
 
106
    Nothing
 
107
*/
 
108
 
 
109
static void mysql_ha_hash_free(TABLE_LIST *tables)
 
110
{
 
111
  my_free((char*) tables, MYF(0));
 
112
}
 
113
 
 
114
/**
 
115
  Close a HANDLER table.
 
116
 
 
117
  @param thd Thread identifier.
 
118
  @param tables A list of tables with the first entry to close.
 
119
  @param is_locked If LOCK_open is locked.
 
120
 
 
121
  @note Though this function takes a list of tables, only the first list entry
 
122
  will be closed.
 
123
  @note Broadcasts refresh if it closed a table with old version.
 
124
*/
 
125
 
 
126
static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
 
127
                                 bool is_locked)
 
128
{
 
129
  TABLE **table_ptr;
 
130
 
 
131
  /*
 
132
    Though we could take the table pointer from hash_tables->table,
 
133
    we must follow the thd->handler_tables chain anyway, as we need the
 
134
    address of the 'next' pointer referencing this table
 
135
    for close_thread_table().
 
136
  */
 
137
  for (table_ptr= &(thd->handler_tables);
 
138
       *table_ptr && (*table_ptr != tables->table);
 
139
         table_ptr= &(*table_ptr)->next)
 
140
    ;
 
141
 
 
142
  if (*table_ptr)
 
143
  {
 
144
    (*table_ptr)->file->ha_index_or_rnd_end();
 
145
    if (! is_locked)
 
146
      VOID(pthread_mutex_lock(&LOCK_open));
 
147
    if (close_thread_table(thd, table_ptr))
 
148
    {
 
149
      /* Tell threads waiting for refresh that something has happened */
 
150
      broadcast_refresh();
 
151
    }
 
152
    if (! is_locked)
 
153
      VOID(pthread_mutex_unlock(&LOCK_open));
 
154
  }
 
155
  else if (tables->table)
 
156
  {
 
157
    /* Must be a temporary table */
 
158
    TABLE *table= tables->table;
 
159
    table->file->ha_index_or_rnd_end();
 
160
    table->query_id= thd->query_id;
 
161
    table->open_by_handler= 0;
 
162
  }
 
163
 
 
164
  /* Mark table as closed, ready for re-open if necessary. */
 
165
  tables->table= NULL;
 
166
}
 
167
 
 
168
/*
 
169
  Open a HANDLER table.
 
170
 
 
171
  SYNOPSIS
 
172
    mysql_ha_open()
 
173
    thd                         Thread identifier.
 
174
    tables                      A list of tables with the first entry to open.
 
175
    reopen                      Re-open a previously opened handler table.
 
176
 
 
177
  DESCRIPTION
 
178
    Though this function takes a list of tables, only the first list entry
 
179
    will be opened.
 
180
    'reopen' is set when a handler table is to be re-opened. In this case,
 
181
    'tables' is the pointer to the hashed TABLE_LIST object which has been
 
182
    saved on the original open.
 
183
    'reopen' is also used to suppress the sending of an 'ok' message.
 
184
 
 
185
  RETURN
 
186
    FALSE OK
 
187
    TRUE  Error
 
188
*/
 
189
 
 
190
bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
 
191
{
 
192
  TABLE_LIST    *hash_tables = NULL;
 
193
  char          *db, *name, *alias;
 
194
  uint          dblen, namelen, aliaslen, counter;
 
195
  int           error;
 
196
  TABLE         *backup_open_tables;
 
197
  DBUG_ENTER("mysql_ha_open");
 
198
  DBUG_PRINT("enter",("'%s'.'%s' as '%s'  reopen: %d",
 
199
                      tables->db, tables->table_name, tables->alias,
 
200
                      (int) reopen));
 
201
 
 
202
  if (tables->schema_table)
 
203
  {
 
204
    my_error(ER_WRONG_USAGE, MYF(0), "HANDLER OPEN",
 
205
             INFORMATION_SCHEMA_NAME.str);
 
206
    DBUG_PRINT("exit",("ERROR"));
 
207
    DBUG_RETURN(TRUE);
 
208
  }
 
209
 
 
210
  if (! hash_inited(&thd->handler_tables_hash))
 
211
  {
 
212
    /*
 
213
      HASH entries are of type TABLE_LIST.
 
214
    */
 
215
    if (hash_init(&thd->handler_tables_hash, &my_charset_latin1,
 
216
                  HANDLER_TABLES_HASH_SIZE, 0, 0,
 
217
                  (hash_get_key) mysql_ha_hash_get_key,
 
218
                  (hash_free_key) mysql_ha_hash_free, 0))
 
219
      goto err;
 
220
  }
 
221
  else if (! reopen) /* Otherwise we have 'tables' already. */
 
222
  {
 
223
    if (hash_search(&thd->handler_tables_hash, (uchar*) tables->alias,
 
224
                    strlen(tables->alias) + 1))
 
225
    {
 
226
      DBUG_PRINT("info",("duplicate '%s'", tables->alias));
 
227
      my_error(ER_NONUNIQ_TABLE, MYF(0), tables->alias);
 
228
      goto err;
 
229
    }
 
230
  }
 
231
 
 
232
  /*
 
233
    Save and reset the open_tables list so that open_tables() won't
 
234
    be able to access (or know about) the previous list. And on return
 
235
    from open_tables(), thd->open_tables will contain only the opened
 
236
    table.
 
237
 
 
238
    The thd->handler_tables list is kept as-is to avoid deadlocks if
 
239
    open_table(), called by open_tables(), needs to back-off because
 
240
    of a pending name-lock on the table being opened.
 
241
 
 
242
    See open_table() back-off comments for more details.
 
243
  */
 
244
  backup_open_tables= thd->open_tables;
 
245
  thd->open_tables= NULL;
 
246
 
 
247
  /*
 
248
    open_tables() will set 'tables->table' if successful.
 
249
    It must be NULL for a real open when calling open_tables().
 
250
  */
 
251
  DBUG_ASSERT(! tables->table);
 
252
 
 
253
  /* for now HANDLER can be used only for real TABLES */
 
254
  tables->required_type= FRMTYPE_TABLE;
 
255
  /*
 
256
    We use open_tables() here, rather than, say,
 
257
    open_ltable() or open_table() because we would like to be able
 
258
    to open a temporary table.
 
259
  */
 
260
  error= open_tables(thd, &tables, &counter, 0);
 
261
  if (thd->open_tables)
 
262
  {
 
263
    if (thd->open_tables->next)
 
264
    {
 
265
      /*
 
266
        We opened something that is more than a single table.
 
267
        This happens with MERGE engine. Don't try to link
 
268
        this mess into thd->handler_tables list, close it
 
269
        and report an error. We must do it right away
 
270
        because mysql_ha_close_table(), called down the road,
 
271
        can close a single table only.
 
272
      */
 
273
      close_thread_tables(thd);
 
274
      my_error(ER_ILLEGAL_HA, MYF(0), tables->alias);
 
275
      error= 1;
 
276
    }
 
277
    else
 
278
    {
 
279
      /* Merge the opened table into handler_tables list. */
 
280
      thd->open_tables->next= thd->handler_tables;
 
281
      thd->handler_tables= thd->open_tables;
 
282
    }
 
283
  }
 
284
 
 
285
  /* Restore the state. */
 
286
  thd->open_tables= backup_open_tables;
 
287
 
 
288
  if (error)
 
289
    goto err;
 
290
 
 
291
  /* There can be only one table in '*tables'. */
 
292
  if (! (tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER))
 
293
  {
 
294
    my_error(ER_ILLEGAL_HA, MYF(0), tables->alias);
 
295
    goto err;
 
296
  }
 
297
 
 
298
  if (! reopen)
 
299
  {
 
300
    /* copy the TABLE_LIST struct */
 
301
    dblen= strlen(tables->db) + 1;
 
302
    namelen= strlen(tables->table_name) + 1;
 
303
    aliaslen= strlen(tables->alias) + 1;
 
304
    if (!(my_multi_malloc(MYF(MY_WME),
 
305
                          &hash_tables, (uint) sizeof(*hash_tables),
 
306
                          &db, (uint) dblen,
 
307
                          &name, (uint) namelen,
 
308
                          &alias, (uint) aliaslen,
 
309
                          NullS)))
 
310
      goto err;
 
311
    /* structure copy */
 
312
    *hash_tables= *tables;
 
313
    hash_tables->db= db;
 
314
    hash_tables->table_name= name;
 
315
    hash_tables->alias= alias;
 
316
    memcpy(hash_tables->db, tables->db, dblen);
 
317
    memcpy(hash_tables->table_name, tables->table_name, namelen);
 
318
    memcpy(hash_tables->alias, tables->alias, aliaslen);
 
319
 
 
320
    /* add to hash */
 
321
    if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables))
 
322
      goto err;
 
323
  }
 
324
 
 
325
  /*
 
326
    If it's a temp table, don't reset table->query_id as the table is
 
327
    being used by this handler. Otherwise, no meaning at all.
 
328
  */
 
329
  tables->table->open_by_handler= 1;
 
330
 
 
331
  if (! reopen)
 
332
    my_ok(thd);
 
333
  DBUG_PRINT("exit",("OK"));
 
334
  DBUG_RETURN(FALSE);
 
335
 
 
336
err:
 
337
  if (hash_tables)
 
338
    my_free((char*) hash_tables, MYF(0));
 
339
  if (tables->table)
 
340
    mysql_ha_close_table(thd, tables, FALSE);
 
341
  DBUG_PRINT("exit",("ERROR"));
 
342
  DBUG_RETURN(TRUE);
 
343
}
 
344
 
 
345
 
 
346
/*
 
347
  Close a HANDLER table by alias or table name
 
348
 
 
349
  SYNOPSIS
 
350
    mysql_ha_close()
 
351
    thd                         Thread identifier.
 
352
    tables                      A list of tables with the first entry to close.
 
353
 
 
354
  DESCRIPTION
 
355
    Closes the table that is associated (on the handler tables hash) with the
 
356
    name (table->alias) of the specified table.
 
357
 
 
358
  RETURN
 
359
    FALSE ok
 
360
    TRUE  error
 
361
*/
 
362
 
 
363
bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
 
364
{
 
365
  TABLE_LIST    *hash_tables;
 
366
  DBUG_ENTER("mysql_ha_close");
 
367
  DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
 
368
                      tables->db, tables->table_name, tables->alias));
 
369
 
 
370
  if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
 
371
                                              (uchar*) tables->alias,
 
372
                                              strlen(tables->alias) + 1)))
 
373
  {
 
374
    mysql_ha_close_table(thd, hash_tables, FALSE);
 
375
    hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
 
376
  }
 
377
  else
 
378
  {
 
379
    my_error(ER_UNKNOWN_TABLE, MYF(0), tables->alias, "HANDLER");
 
380
    DBUG_PRINT("exit",("ERROR"));
 
381
    DBUG_RETURN(TRUE);
 
382
  }
 
383
 
 
384
  my_ok(thd);
 
385
  DBUG_PRINT("exit", ("OK"));
 
386
  DBUG_RETURN(FALSE);
 
387
}
 
388
 
 
389
 
 
390
/*
 
391
  Read from a HANDLER table.
 
392
 
 
393
  SYNOPSIS
 
394
    mysql_ha_read()
 
395
    thd                         Thread identifier.
 
396
    tables                      A list of tables with the first entry to read.
 
397
    mode
 
398
    keyname
 
399
    key_expr
 
400
    ha_rkey_mode
 
401
    cond
 
402
    select_limit_cnt
 
403
    offset_limit_cnt
 
404
 
 
405
  RETURN
 
406
    FALSE ok
 
407
    TRUE  error
 
408
*/
 
409
 
 
410
bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
 
411
                   enum enum_ha_read_modes mode, char *keyname,
 
412
                   List<Item> *key_expr,
 
413
                   enum ha_rkey_function ha_rkey_mode, Item *cond,
 
414
                   ha_rows select_limit_cnt, ha_rows offset_limit_cnt)
 
415
{
 
416
  TABLE_LIST    *hash_tables;
 
417
  TABLE         *table, *backup_open_tables;
 
418
  MYSQL_LOCK    *lock;
 
419
  List<Item>    list;
 
420
  Protocol      *protocol= thd->protocol;
 
421
  char          buff[MAX_FIELD_WIDTH];
 
422
  String        buffer(buff, sizeof(buff), system_charset_info);
 
423
  int           error, keyno= -1;
 
424
  uint          num_rows;
 
425
  uchar         *UNINIT_VAR(key);
 
426
  uint          UNINIT_VAR(key_len);
 
427
  bool          need_reopen;
 
428
  DBUG_ENTER("mysql_ha_read");
 
429
  DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
 
430
                      tables->db, tables->table_name, tables->alias));
 
431
 
 
432
  thd->lex->select_lex.context.resolve_in_table_list_only(tables);
 
433
  list.push_front(new Item_field(&thd->lex->select_lex.context,
 
434
                                 NULL, NULL, "*"));
 
435
  List_iterator<Item> it(list);
 
436
  it++;
 
437
 
 
438
retry:
 
439
  if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
 
440
                                              (uchar*) tables->alias,
 
441
                                              strlen(tables->alias) + 1)))
 
442
  {
 
443
    table= hash_tables->table;
 
444
    DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' table: 0x%lx",
 
445
                               hash_tables->db, hash_tables->table_name,
 
446
                               hash_tables->alias, (long) table));
 
447
    if (!table)
 
448
    {
 
449
      /*
 
450
        The handler table has been closed. Re-open it.
 
451
      */
 
452
      if (mysql_ha_open(thd, hash_tables, 1))
 
453
      {
 
454
        DBUG_PRINT("exit",("reopen failed"));
 
455
        goto err0;
 
456
      }
 
457
 
 
458
      table= hash_tables->table;
 
459
      DBUG_PRINT("info",("re-opened '%s'.'%s' as '%s' tab %p",
 
460
                         hash_tables->db, hash_tables->table_name,
 
461
                         hash_tables->alias, table));
 
462
    }
 
463
 
 
464
#if MYSQL_VERSION_ID < 40100
 
465
    if (*tables->db && strcmp(table->table_cache_key, tables->db))
 
466
    {
 
467
      DBUG_PRINT("info",("wrong db"));
 
468
      table= NULL;
 
469
    }
 
470
#endif
 
471
  }
 
472
  else
 
473
    table= NULL;
 
474
 
 
475
  if (!table)
 
476
  {
 
477
#if MYSQL_VERSION_ID < 40100
 
478
    char buff[MAX_DBKEY_LENGTH];
 
479
    if (*tables->db)
 
480
      strxnmov(buff, sizeof(buff)-1, tables->db, ".", tables->table_name,
 
481
               NullS);
 
482
    else
 
483
      strncpy(buff, tables->alias, sizeof(buff));
 
484
    my_error(ER_UNKNOWN_TABLE, MYF(0), buff, "HANDLER");
 
485
#else
 
486
    my_error(ER_UNKNOWN_TABLE, MYF(0), tables->alias, "HANDLER");
 
487
#endif
 
488
    goto err0;
 
489
  }
 
490
  tables->table=table;
 
491
 
 
492
  /* save open_tables state */
 
493
  backup_open_tables= thd->open_tables;
 
494
  /*
 
495
    mysql_lock_tables() needs thd->open_tables to be set correctly to
 
496
    be able to handle aborts properly. When the abort happens, it's
 
497
    safe to not protect thd->handler_tables because it won't close any
 
498
    tables.
 
499
  */
 
500
  thd->open_tables= thd->handler_tables;
 
501
 
 
502
  lock= mysql_lock_tables(thd, &tables->table, 1,
 
503
                          MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN, &need_reopen);
 
504
 
 
505
  /* restore previous context */
 
506
  thd->open_tables= backup_open_tables;
 
507
 
 
508
  if (need_reopen)
 
509
  {
 
510
    mysql_ha_close_table(thd, hash_tables, FALSE);
 
511
    /*
 
512
      The lock might have been aborted, we need to manually reset
 
513
      thd->some_tables_deleted because handler's tables are closed
 
514
      in a non-standard way. Otherwise we might loop indefinitely.
 
515
    */
 
516
    thd->some_tables_deleted= 0;
 
517
    goto retry;
 
518
  }
 
519
 
 
520
  if (!lock)
 
521
    goto err0; // mysql_lock_tables() printed error message already
 
522
 
 
523
  // Always read all columns
 
524
  tables->table->read_set= &tables->table->s->all_set;
 
525
 
 
526
  if (cond)
 
527
  {
 
528
    if (table->query_id != thd->query_id)
 
529
      cond->cleanup();                          // File was reopened
 
530
    if ((!cond->fixed &&
 
531
         cond->fix_fields(thd, &cond)) || cond->check_cols(1))
 
532
      goto err;
 
533
  }
 
534
 
 
535
  if (keyname)
 
536
  {
 
537
    if ((keyno=find_type(keyname, &table->s->keynames, 1+2)-1)<0)
 
538
    {
 
539
      my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), keyname, tables->alias);
 
540
      goto err;
 
541
    }
 
542
  }
 
543
 
 
544
  if (insert_fields(thd, &thd->lex->select_lex.context,
 
545
                    tables->db, tables->alias, &it, 0))
 
546
    goto err;
 
547
 
 
548
  protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
 
549
 
 
550
  /*
 
551
    In ::external_lock InnoDB resets the fields which tell it that
 
552
    the handle is used in the HANDLER interface. Tell it again that
 
553
    we are using it for HANDLER.
 
554
  */
 
555
 
 
556
  table->file->init_table_handle_for_HANDLER();
 
557
 
 
558
  for (num_rows=0; num_rows < select_limit_cnt; )
 
559
  {
 
560
    switch (mode) {
 
561
    case RNEXT:
 
562
      if (table->file->inited != handler::NONE)
 
563
      {
 
564
        error=keyname ?
 
565
          table->file->index_next(table->record[0]) :
 
566
          table->file->rnd_next(table->record[0]);
 
567
        break;
 
568
      }
 
569
      /* else fall through */
 
570
    case RFIRST:
 
571
      if (keyname)
 
572
      {
 
573
        table->file->ha_index_or_rnd_end();
 
574
        table->file->ha_index_init(keyno, 1);
 
575
        error= table->file->index_first(table->record[0]);
 
576
      }
 
577
      else
 
578
      {
 
579
        table->file->ha_index_or_rnd_end();
 
580
        if (!(error= table->file->ha_rnd_init(1)))
 
581
          error= table->file->rnd_next(table->record[0]);
 
582
      }
 
583
      mode=RNEXT;
 
584
      break;
 
585
    case RPREV:
 
586
      DBUG_ASSERT(keyname != 0);
 
587
      if (table->file->inited != handler::NONE)
 
588
      {
 
589
        error=table->file->index_prev(table->record[0]);
 
590
        break;
 
591
      }
 
592
      /* else fall through */
 
593
    case RLAST:
 
594
      DBUG_ASSERT(keyname != 0);
 
595
      table->file->ha_index_or_rnd_end();
 
596
      table->file->ha_index_init(keyno, 1);
 
597
      error= table->file->index_last(table->record[0]);
 
598
      mode=RPREV;
 
599
      break;
 
600
    case RNEXT_SAME:
 
601
      /* Continue scan on "(keypart1,keypart2,...)=(c1, c2, ...)  */
 
602
      DBUG_ASSERT(keyname != 0);
 
603
      error= table->file->index_next_same(table->record[0], key, key_len);
 
604
      break;
 
605
    case RKEY:
 
606
    {
 
607
      DBUG_ASSERT(keyname != 0);
 
608
      KEY *keyinfo=table->key_info+keyno;
 
609
      KEY_PART_INFO *key_part=keyinfo->key_part;
 
610
      if (key_expr->elements > keyinfo->key_parts)
 
611
      {
 
612
        my_error(ER_TOO_MANY_KEY_PARTS, MYF(0), keyinfo->key_parts);
 
613
        goto err;
 
614
      }
 
615
      List_iterator<Item> it_ke(*key_expr);
 
616
      Item *item;
 
617
      key_part_map keypart_map;
 
618
      for (keypart_map= key_len=0 ; (item=it_ke++) ; key_part++)
 
619
      {
 
620
        my_bitmap_map *old_map;
 
621
        // 'item' can be changed by fix_fields() call
 
622
        if ((!item->fixed &&
 
623
             item->fix_fields(thd, it_ke.ref())) ||
 
624
            (item= *it_ke.ref())->check_cols(1))
 
625
          goto err;
 
626
        if (item->used_tables() & ~RAND_TABLE_BIT)
 
627
        {
 
628
          my_error(ER_WRONG_ARGUMENTS,MYF(0),"HANDLER ... READ");
 
629
          goto err;
 
630
        }
 
631
        old_map= dbug_tmp_use_all_columns(table, table->write_set);
 
632
        (void) item->save_in_field(key_part->field, 1);
 
633
        dbug_tmp_restore_column_map(table->write_set, old_map);
 
634
        key_len+=key_part->store_length;
 
635
        keypart_map= (keypart_map << 1) | 1;
 
636
      }
 
637
 
 
638
      if (!(key= (uchar*) thd->calloc(ALIGN_SIZE(key_len))))
 
639
        goto err;
 
640
      table->file->ha_index_or_rnd_end();
 
641
      table->file->ha_index_init(keyno, 1);
 
642
      key_copy(key, table->record[0], table->key_info + keyno, key_len);
 
643
      error= table->file->index_read_map(table->record[0],
 
644
                                         key, keypart_map, ha_rkey_mode);
 
645
      mode=rkey_to_rnext[(int)ha_rkey_mode];
 
646
      break;
 
647
    }
 
648
    default:
 
649
      my_message(ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), MYF(0));
 
650
      goto err;
 
651
    }
 
652
 
 
653
    if (error)
 
654
    {
 
655
      if (error == HA_ERR_RECORD_DELETED)
 
656
        continue;
 
657
      if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
 
658
      {
 
659
        sql_print_error("mysql_ha_read: Got error %d when reading table '%s'",
 
660
                        error, tables->table_name);
 
661
        table->file->print_error(error,MYF(0));
 
662
        goto err;
 
663
      }
 
664
      goto ok;
 
665
    }
 
666
    if (cond && !cond->val_int())
 
667
      continue;
 
668
    if (num_rows >= offset_limit_cnt)
 
669
    {
 
670
      Item *item;
 
671
      protocol->prepare_for_resend();
 
672
      it.rewind();
 
673
      while ((item=it++))
 
674
      {
 
675
        if (item->send(thd->protocol, &buffer))
 
676
        {
 
677
          protocol->free();                             // Free used
 
678
          my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
 
679
          goto err;
 
680
        }
 
681
      }
 
682
      protocol->write();
 
683
    }
 
684
    num_rows++;
 
685
  }
 
686
ok:
 
687
  mysql_unlock_tables(thd,lock);
 
688
  my_eof(thd);
 
689
  DBUG_PRINT("exit",("OK"));
 
690
  DBUG_RETURN(FALSE);
 
691
 
 
692
err:
 
693
  mysql_unlock_tables(thd,lock);
 
694
err0:
 
695
  DBUG_PRINT("exit",("ERROR"));
 
696
  DBUG_RETURN(TRUE);
 
697
}
 
698
 
 
699
 
 
700
/**
 
701
  Scan the handler tables hash for matching tables.
 
702
 
 
703
  @param thd Thread identifier.
 
704
  @param tables The list of tables to remove.
 
705
 
 
706
  @return Pointer to head of linked list (TABLE_LIST::next_local) of matching
 
707
          TABLE_LIST elements from handler_tables_hash. Otherwise, NULL if no
 
708
          table was matched.
 
709
*/
 
710
 
 
711
static TABLE_LIST *mysql_ha_find(THD *thd, TABLE_LIST *tables)
 
712
{
 
713
  TABLE_LIST *hash_tables, *head= NULL, *first= tables;
 
714
  DBUG_ENTER("mysql_ha_find");
 
715
 
 
716
  /* search for all handlers with matching table names */
 
717
  for (uint i= 0; i < thd->handler_tables_hash.records; i++)
 
718
  {
 
719
    hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
 
720
    for (tables= first; tables; tables= tables->next_local)
 
721
    {
 
722
      if ((! *tables->db ||
 
723
          ! my_strcasecmp(&my_charset_latin1, hash_tables->db, tables->db)) &&
 
724
          ! my_strcasecmp(&my_charset_latin1, hash_tables->table_name,
 
725
                          tables->table_name))
 
726
        break;
 
727
    }
 
728
    if (tables)
 
729
    {
 
730
      hash_tables->next_local= head;
 
731
      head= hash_tables;
 
732
    }
 
733
  }
 
734
 
 
735
  DBUG_RETURN(head);
 
736
}
 
737
 
 
738
 
 
739
/**
 
740
  Remove matching tables from the HANDLER's hash table.
 
741
 
 
742
  @param thd Thread identifier.
 
743
  @param tables The list of tables to remove.
 
744
  @param is_locked If LOCK_open is locked.
 
745
 
 
746
  @note Broadcasts refresh if it closed a table with old version.
 
747
*/
 
748
 
 
749
void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked)
 
750
{
 
751
  TABLE_LIST *hash_tables, *next;
 
752
  DBUG_ENTER("mysql_ha_rm_tables");
 
753
 
 
754
  DBUG_ASSERT(tables);
 
755
 
 
756
  hash_tables= mysql_ha_find(thd, tables);
 
757
 
 
758
  while (hash_tables)
 
759
  {
 
760
    next= hash_tables->next_local;
 
761
    if (hash_tables->table)
 
762
      mysql_ha_close_table(thd, hash_tables, is_locked);
 
763
    hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
 
764
    hash_tables= next;
 
765
  }
 
766
 
 
767
  DBUG_VOID_RETURN;
 
768
}
 
769
 
 
770
 
 
771
/**
 
772
  Flush (close and mark for re-open) all tables that should be should
 
773
  be reopen.
 
774
 
 
775
  @param thd Thread identifier.
 
776
 
 
777
  @note Broadcasts refresh if it closed a table with old version.
 
778
*/
 
779
 
 
780
void mysql_ha_flush(THD *thd)
 
781
{
 
782
  TABLE_LIST *hash_tables;
 
783
  DBUG_ENTER("mysql_ha_flush");
 
784
 
 
785
  safe_mutex_assert_owner(&LOCK_open);
 
786
 
 
787
  for (uint i= 0; i < thd->handler_tables_hash.records; i++)
 
788
  {
 
789
    hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
 
790
    if (hash_tables->table && hash_tables->table->needs_reopen_or_name_lock())
 
791
      mysql_ha_close_table(thd, hash_tables, TRUE);
 
792
  }
 
793
 
 
794
  DBUG_VOID_RETURN;
 
795
}
 
796
 
 
797
 
 
798
/**
 
799
  Close all HANDLER's tables.
 
800
 
 
801
  @param thd Thread identifier.
 
802
 
 
803
  @note Broadcasts refresh if it closed a table with old version.
 
804
*/
 
805
 
 
806
void mysql_ha_cleanup(THD *thd)
 
807
{
 
808
  TABLE_LIST *hash_tables;
 
809
  DBUG_ENTER("mysql_ha_cleanup");
 
810
 
 
811
  for (uint i= 0; i < thd->handler_tables_hash.records; i++)
 
812
  {
 
813
    hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
 
814
    if (hash_tables->table)
 
815
      mysql_ha_close_table(thd, hash_tables, FALSE);
 
816
   }
 
817
 
 
818
  hash_free(&thd->handler_tables_hash);
 
819
 
 
820
  DBUG_VOID_RETURN;
 
821
}
 
822