myrg_open.c 16.6 KB
Newer Older
1
/* Copyright (C) 2000-2006 MySQL AB
2

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3 4
   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
5
   the Free Software Foundation; version 2 of the License.
6

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

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

16
/* open a MyISAM MERGE table */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
17

18
#include "myrg_def.h"
bk@work.mysql.com's avatar
bk@work.mysql.com committed
19 20 21 22 23 24
#include <stddef.h>
#include <errno.h>
#ifdef VMS
#include "mrg_static.c"
#endif

serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
25
/*
26
	open a MyISAM MERGE table
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
27 28
	if handle_locking is 0 then exit with error if some table is locked
	if handle_locking is 1 then wait if table is locked
bk@work.mysql.com's avatar
bk@work.mysql.com committed
29

30 31 32 33 34 35
        NOTE: This function is not used in the MySQL server. It is for
        MERGE use independent from MySQL. Currently there is some code
        duplication between myrg_open() and myrg_parent_open() +
        myrg_attach_children(). Please duplicate changes in these
        functions or make common sub-functions.
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
36

37
MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
38
{
39
  int save_errno,errpos=0;
40
  uint files= 0, i, dir_length, length, key_parts, min_keys= 0;
41
  ulonglong file_offset=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
42
  char name_buff[FN_REFLEN*2],buff[FN_REFLEN],*end;
43
  MYRG_INFO *m_info=0;
44 45
  File fd;
  IO_CACHE file;
46
  MI_INFO *isam=0;
47
  uint found_merge_insert_method= 0;
48
  size_t name_buff_length;
49
  my_bool bad_children= FALSE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
50 51
  DBUG_ENTER("myrg_open");

52 53
  LINT_INIT(key_parts);

54
  bzero((char*) &file,sizeof(file));
55 56
  if ((fd=my_open(fn_format(name_buff,name,"",MYRG_NAME_EXT,
                            MY_UNPACK_FILENAME|MY_APPEND_EXT),
57
		  O_RDONLY | O_SHARE,MYF(0))) < 0)
58 59 60
    goto err;
  errpos=1;
  if (init_io_cache(&file, fd, 4*IO_SIZE, READ_CACHE, 0, 0,
61
		    MYF(MY_WME | MY_NABP)))
62 63 64
    goto err;
  errpos=2;
  dir_length=dirname_part(name_buff, name, &name_buff_length);
65
  while ((length=my_b_gets(&file,buff,FN_REFLEN-1)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
66
  {
67
    if ((end=buff+length)[-1] == '\n')
bk@work.mysql.com's avatar
bk@work.mysql.com committed
68
      end[-1]='\0';
69
    if (buff[0] && buff[0] != '#')
bk@work.mysql.com's avatar
bk@work.mysql.com committed
70 71
      files++;
  }
72 73

  my_b_seek(&file, 0);
74
  while ((length=my_b_gets(&file,buff,FN_REFLEN-1)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
75
  {
76
    if ((end=buff+length)[-1] == '\n')
77
      *--end='\0';
78 79 80
    if (!buff[0])
      continue;		/* Skip empty lines */
    if (buff[0] == '#')
bk@work.mysql.com's avatar
bk@work.mysql.com committed
81
    {
82
      if (!strncmp(buff+1,"INSERT_METHOD=",14))
83 84
      {			/* Lookup insert method */
	int tmp=find_type(buff+15,&merge_insert_method,2);
85
	found_merge_insert_method = (uint) (tmp >= 0 ? tmp : 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
86
      }
87 88
      continue;		/* Skip comments */
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
89

90
    if (!has_path(buff))
91 92 93 94 95
    {
      VOID(strmake(name_buff+dir_length,buff,
                   sizeof(name_buff)-1-dir_length));
      VOID(cleanup_dirname(buff,name_buff));
    }
96 97
    else
      fn_format(buff, buff, "", "", 0);
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
98
    if (!(isam=mi_open(buff,mode,(handle_locking?HA_OPEN_WAIT_IF_LOCKED:0))))
99
    {
100 101 102
      if (handle_locking & HA_OPEN_FOR_REPAIR)
      {
        myrg_print_wrong_table(buff);
103
        bad_children= TRUE;
104 105
        continue;
      }
106
      goto bad_children;
107
    }
108
    if (!m_info)                                /* First file */
109 110 111 112 113 114 115
    {
      key_parts=isam->s->base.key_parts;
      if (!(m_info= (MYRG_INFO*) my_malloc(sizeof(MYRG_INFO) +
                                           files*sizeof(MYRG_TABLE) +
                                           key_parts*sizeof(long),
                                           MYF(MY_WME|MY_ZEROFILL))))
        goto err;
116 117 118 119 120
      DBUG_ASSERT(files);
      m_info->open_tables=(MYRG_TABLE *) (m_info+1);
      m_info->rec_per_key_part=(ulong *) (m_info->open_tables+files);
      m_info->tables= files;
      files= 0;
121
      m_info->reclength=isam->s->base.reclength;
122
      min_keys= isam->s->base.keys;
123 124
      errpos=3;
    }
125 126 127
    m_info->open_tables[files].table= isam;
    m_info->open_tables[files].file_offset=(my_off_t) file_offset;
    file_offset+=isam->state->data_file_length;
128
    files++;
serg@serg.mylan's avatar
serg@serg.mylan committed
129
    if (m_info->reclength != isam->s->base.reclength)
130
    {
131 132 133
      if (handle_locking & HA_OPEN_FOR_REPAIR)
      {
        myrg_print_wrong_table(buff);
134
        bad_children= TRUE;
135 136
        continue;
      }
137
      goto bad_children;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
138
    }
139 140 141 142
    m_info->options|= isam->s->options;
    m_info->records+= isam->state->records;
    m_info->del+= isam->state->del;
    m_info->data_file_length+= isam->state->data_file_length;
143 144
    if (min_keys > isam->s->base.keys)
      min_keys= isam->s->base.keys;
145
    for (i=0; i < key_parts; i++)
146 147
      m_info->rec_per_key_part[i]+= (isam->s->state.rec_per_key_part[i] /
                                     m_info->tables);
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
148
  }
149

150 151
  if (bad_children)
    goto bad_children;
serg@serg.mylan's avatar
serg@serg.mylan committed
152
  if (!m_info && !(m_info= (MYRG_INFO*) my_malloc(sizeof(MYRG_INFO),
153
                                                  MYF(MY_WME | MY_ZEROFILL))))
serg@serg.mylan's avatar
serg@serg.mylan committed
154
    goto err;
155 156
  /* Don't mark table readonly, for ALTER TABLE ... UNION=(...) to work */
  m_info->options&= ~(HA_OPTION_COMPRESS_RECORD | HA_OPTION_READ_ONLY_DATA);
157
  m_info->merge_insert_method= found_merge_insert_method;
158

bk@work.mysql.com's avatar
bk@work.mysql.com committed
159 160 161 162 163
  if (sizeof(my_off_t) == 4 && file_offset > (ulonglong) (ulong) ~0L)
  {
    my_errno=HA_ERR_RECORD_FILE_FULL;
    goto err;
  }
164
  m_info->keys= min_keys;
165
  bzero((char*) &m_info->by_key,sizeof(m_info->by_key));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
166

167
  /* this works ok if the table list is empty */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
168 169
  m_info->end_table=m_info->open_tables+files;
  m_info->last_used_table=m_info->open_tables;
170
  m_info->children_attached= TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
171

172 173
  VOID(my_close(fd,MYF(0)));
  end_io_cache(&file);
174
  VOID(pthread_mutex_init(&m_info->mutex, MY_MUTEX_INIT_FAST));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
175 176 177 178 179 180
  m_info->open_list.data=(void*) m_info;
  pthread_mutex_lock(&THR_LOCK_open);
  myrg_open_list=list_add(myrg_open_list,&m_info->open_list);
  pthread_mutex_unlock(&THR_LOCK_open);
  DBUG_RETURN(m_info);

181 182
bad_children:
  my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
183 184 185
err:
  save_errno=my_errno;
  switch (errpos) {
186 187
  case 3:
    while (files)
188
      (void) mi_close(m_info->open_tables[--files].table);
189 190
    my_free((char*) m_info,MYF(0));
    /* Fall through */
191 192 193
  case 2:
    end_io_cache(&file);
    /* Fall through */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
194
  case 1:
195
    VOID(my_close(fd,MYF(0)));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
196 197 198 199
  }
  my_errno=save_errno;
  DBUG_RETURN (NULL);
}
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332


/**
  @brief Open parent table of a MyISAM MERGE table.

  @detail Open MERGE meta file to get the table name paths for the child
    tables. Count the children. Allocate and initialize MYRG_INFO
    structure. Call a callback function for each child table.

  @param[in]    parent_name     merge table name path as "database/table"
  @param[in]    callback        function to call for each child table
  @param[in]    callback_param  data pointer to give to the callback

  @return MYRG_INFO pointer
    @retval     != NULL         OK
    @retval     NULL            Error

  @note: Currently there is some code duplication between myrg_open()
    and myrg_parent_open() + myrg_attach_children(). Please duplicate
    changes in these functions or make common sub-functions.
*/

MYRG_INFO *myrg_parent_open(const char *parent_name,
                            int (*callback)(void*, const char*),
                            void *callback_param)
{
  MYRG_INFO *m_info;
  int       rc;
  int       errpos;
  int       save_errno;
  int       insert_method;
  uint      length;
  uint      dir_length;
  uint      child_count;
  size_t    name_buff_length;
  File      fd;
  IO_CACHE  file_cache;
  char      parent_name_buff[FN_REFLEN * 2];
  char      child_name_buff[FN_REFLEN];
  DBUG_ENTER("myrg_parent_open");

  rc= 1;
  errpos= 0;
  bzero((char*) &file_cache, sizeof(file_cache));

  /* Open MERGE meta file. */
  if ((fd= my_open(fn_format(parent_name_buff, parent_name, "", MYRG_NAME_EXT,
                             MY_UNPACK_FILENAME|MY_APPEND_EXT),
                   O_RDONLY | O_SHARE, MYF(0))) < 0)
    goto err; /* purecov: inspected */
  errpos= 1;

  if (init_io_cache(&file_cache, fd, 4 * IO_SIZE, READ_CACHE, 0, 0,
                    MYF(MY_WME | MY_NABP)))
    goto err; /* purecov: inspected */
  errpos= 2;

  /* Count children. Determine insert method. */
  child_count= 0;
  insert_method= 0;
  while ((length= my_b_gets(&file_cache, child_name_buff, FN_REFLEN - 1)))
  {
    /* Remove line terminator. */
    if (child_name_buff[length - 1] == '\n')
      child_name_buff[--length]= '\0';

    /* Skip empty lines. */
    if (!child_name_buff[0])
      continue; /* purecov: inspected */

    /* Skip comments, but evaluate insert method. */
    if (child_name_buff[0] == '#')
    {
      if (!strncmp(child_name_buff + 1, "INSERT_METHOD=", 14))
      {
        /* Compare buffer with global methods list: merge_insert_method. */
        insert_method= find_type(child_name_buff + 15,
                                 &merge_insert_method, 2);
      }
      continue;
    }

    /* Count the child. */
    child_count++;
  }

  /* Allocate MERGE parent table structure. */
  if (!(m_info= (MYRG_INFO*) my_malloc(sizeof(MYRG_INFO) +
                                       child_count * sizeof(MYRG_TABLE),
                                       MYF(MY_WME | MY_ZEROFILL))))
    goto err; /* purecov: inspected */
  errpos= 3;
  m_info->open_tables= (MYRG_TABLE*) (m_info + 1);
  m_info->tables= child_count;
  m_info->merge_insert_method= insert_method > 0 ? insert_method : 0;
  /* This works even if the table list is empty. */
  m_info->end_table= m_info->open_tables + child_count;
  if (!child_count)
  {
    /* Do not attach/detach an empty child list. */
    m_info->children_attached= TRUE;
  }

  /* Call callback for each child. */
  dir_length= dirname_part(parent_name_buff, parent_name, &name_buff_length);
  my_b_seek(&file_cache, 0);
  while ((length= my_b_gets(&file_cache, child_name_buff, FN_REFLEN - 1)))
  {
    /* Remove line terminator. */
    if (child_name_buff[length - 1] == '\n')
      child_name_buff[--length]= '\0';

    /* Skip empty lines and comments. */
    if (!child_name_buff[0] || (child_name_buff[0] == '#'))
      continue;

    if (!has_path(child_name_buff))
    {
      VOID(strmake(parent_name_buff + dir_length, child_name_buff,
                   sizeof(parent_name_buff) - 1 - dir_length));
      VOID(cleanup_dirname(child_name_buff, parent_name_buff));
    }
    else
      fn_format(child_name_buff, child_name_buff, "", "", 0);
    DBUG_PRINT("info", ("child: '%s'", child_name_buff));

    /* Callback registers child with handler table. */
    if ((rc= (*callback)(callback_param, child_name_buff)))
      goto err; /* purecov: inspected */
  }

  end_io_cache(&file_cache);
  VOID(my_close(fd, MYF(0)));
333
  VOID(pthread_mutex_init(&m_info->mutex, MY_MUTEX_INIT_FAST));
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395

  m_info->open_list.data= (void*) m_info;
  pthread_mutex_lock(&THR_LOCK_open);
  myrg_open_list= list_add(myrg_open_list, &m_info->open_list);
  pthread_mutex_unlock(&THR_LOCK_open);

  DBUG_RETURN(m_info);

  /* purecov: begin inspected */
 err:
  save_errno= my_errno;
  switch (errpos) {
  case 3:
    my_free((char*) m_info, MYF(0));
    /* Fall through */
  case 2:
    end_io_cache(&file_cache);
    /* Fall through */
  case 1:
    VOID(my_close(fd, MYF(0)));
  }
  my_errno= save_errno;
  DBUG_RETURN (NULL);
  /* purecov: end */
}


/**
  @brief Attach children to a MyISAM MERGE parent table.

  @detail Call a callback function for each child table.
    The callback returns the MyISAM table handle of the child table.
    Check table definition match.

  @param[in]    m_info          MERGE parent table structure
  @param[in]    handle_locking  if contains HA_OPEN_FOR_REPAIR, warn about
                                incompatible child tables, but continue
  @param[in]    callback        function to call for each child table
  @param[in]    callback_param  data pointer to give to the callback

  @return status
    @retval     0               OK
    @retval     != 0            Error

  @note: Currently there is some code duplication between myrg_open()
    and myrg_parent_open() + myrg_attach_children(). Please duplicate
    changes in these functions or make common sub-functions.
*/

int myrg_attach_children(MYRG_INFO *m_info, int handle_locking,
                         MI_INFO *(*callback)(void*),
                         void *callback_param)
{
  ulonglong  file_offset;
  MI_INFO    *myisam;
  int        rc;
  int        errpos;
  int        save_errno;
  uint       idx;
  uint       child_nr;
  uint       key_parts;
  uint       min_keys;
396
  my_bool    bad_children= FALSE;
397 398 399
  DBUG_ENTER("myrg_attach_children");
  DBUG_PRINT("myrg", ("handle_locking: %d", handle_locking));

400 401 402 403 404 405 406 407
  /*
    This function can be called while another thread is trying to abort
    locks of this MERGE table. If the processor reorders instructions or
    write to memory, 'children_attached' could be set before
    'open_tables' has all the pointers to the children. Use of a mutex
    here and in ha_myisammrg::store_lock() forces consistent data.
  */
  pthread_mutex_lock(&m_info->mutex);
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
  rc= 1;
  errpos= 0;
  file_offset= 0;
  LINT_INIT(key_parts);
  min_keys= 0;
  child_nr= 0;
  while ((myisam= (*callback)(callback_param)))
  {
    DBUG_PRINT("myrg", ("child_nr: %u  table: '%s'",
                        child_nr, myisam->filename));
    DBUG_ASSERT(child_nr < m_info->tables);

    /* Special handling when the first child is attached. */
    if (!child_nr)
    {
      m_info->reclength= myisam->s->base.reclength;
      min_keys=  myisam->s->base.keys;
      key_parts= myisam->s->base.key_parts;
      if (!m_info->rec_per_key_part)
      {
        if(!(m_info->rec_per_key_part= (ulong*)
429
             my_malloc(key_parts * sizeof(long), MYF(MY_WME))))
430 431 432
          goto err; /* purecov: inspected */
        errpos= 1;
      }
433
      bzero((char*) m_info->rec_per_key_part, key_parts * sizeof(long));
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
    }

    /* Add MyISAM table info. */
    m_info->open_tables[child_nr].table= myisam;
    m_info->open_tables[child_nr].file_offset= (my_off_t) file_offset;
    file_offset+= myisam->state->data_file_length;

    /* Check table definition match. */
    if (m_info->reclength != myisam->s->base.reclength)
    {
      DBUG_PRINT("error", ("definition mismatch table: '%s'  repair: %d",
                           myisam->filename,
                           (handle_locking & HA_OPEN_FOR_REPAIR)));
      if (handle_locking & HA_OPEN_FOR_REPAIR)
      {
        myrg_print_wrong_table(myisam->filename);
450
        bad_children= TRUE;
451 452
        continue;
      }
453
      goto bad_children;
454 455 456 457 458 459 460 461 462 463 464 465 466 467
    }

    m_info->options|= myisam->s->options;
    m_info->records+= myisam->state->records;
    m_info->del+= myisam->state->del;
    m_info->data_file_length+= myisam->state->data_file_length;
    if (min_keys > myisam->s->base.keys)
      min_keys= myisam->s->base.keys; /* purecov: inspected */
    for (idx= 0; idx < key_parts; idx++)
      m_info->rec_per_key_part[idx]+= (myisam->s->state.rec_per_key_part[idx] /
                                       m_info->tables);
    child_nr++;
  }

468 469 470
  if (bad_children)
    goto bad_children;
  /* Note: callback() resets my_errno, so it is safe to check it here */
471 472 473 474 475 476 477 478 479 480 481 482
  if (my_errno == HA_ERR_WRONG_MRG_TABLE_DEF)
    goto err;
  if (sizeof(my_off_t) == 4 && file_offset > (ulonglong) (ulong) ~0L)
  {
    my_errno= HA_ERR_RECORD_FILE_FULL;
    goto err;
  }
  /* Don't mark table readonly, for ALTER TABLE ... UNION=(...) to work */
  m_info->options&= ~(HA_OPTION_COMPRESS_RECORD | HA_OPTION_READ_ONLY_DATA);
  m_info->keys= min_keys;
  m_info->last_used_table= m_info->open_tables;
  m_info->children_attached= TRUE;
483
  pthread_mutex_unlock(&m_info->mutex);
484 485
  DBUG_RETURN(0);

486 487
bad_children:
  my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
488 489 490 491 492 493 494
err:
  save_errno= my_errno;
  switch (errpos) {
  case 1:
    my_free((char*) m_info->rec_per_key_part, MYF(0));
    m_info->rec_per_key_part= NULL;
  }
495
  pthread_mutex_unlock(&m_info->mutex);
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
  my_errno= save_errno;
  DBUG_RETURN(1);
}


/**
  @brief Detach children from a MyISAM MERGE parent table.

  @param[in]    m_info          MERGE parent table structure

  @note Detach must not touch the children in any way.
    They may have been closed at ths point already.
    All references to the children should be removed.

  @return status
    @retval     0               OK
*/

int myrg_detach_children(MYRG_INFO *m_info)
{
  DBUG_ENTER("myrg_detach_children");
517 518
  /* For symmetry with myrg_attach_children() we use the mutex here. */
  pthread_mutex_lock(&m_info->mutex);
519 520 521 522 523 524 525 526 527 528
  if (m_info->tables)
  {
    /* Do not attach/detach an empty child list. */
    m_info->children_attached= FALSE;
    bzero((char*) m_info->open_tables, m_info->tables * sizeof(MYRG_TABLE));
  }
  m_info->records= 0;
  m_info->del= 0;
  m_info->data_file_length= 0;
  m_info->options= 0;
529
  pthread_mutex_unlock(&m_info->mutex);
530 531 532
  DBUG_RETURN(0);
}