handler.cc 101 KB
Newer Older
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 17 18 19
   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 */


/* Handler-calling-functions */

20
#ifdef USE_PRAGMA_IMPLEMENTATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
21 22 23 24
#pragma implementation				// gcc: Class implementation
#endif

#include "mysql_priv.h"
25
#include "rpl_filter.h"
bk@work.mysql.com's avatar
bk@work.mysql.com committed
26 27 28
#include "ha_heap.h"
#include "ha_myisam.h"
#include "ha_myisammrg.h"
29 30


31 32
#include <myisampack.h>
#include <errno.h>
33

34 35
#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
#define NDB_MAX_ATTRIBUTES_IN_TABLE 128
36 37
#include "ha_ndbcluster.h"
#endif
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
38

39 40
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
41
#endif
42

43 44 45
#ifdef WITH_INNOBASE_STORAGE_ENGINE
#include "ha_innodb.h"
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
46

47 48 49 50 51
/*
  While we have legacy_db_type, we have this array to
  check for dups and to find handlerton from legacy_db_type.
  Remove when legacy_db_type is finally gone
*/
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
52
st_plugin_int *hton2plugin[MAX_HA];
53

54
static handlerton *installed_htons[128];
55

lars@mysql.com's avatar
lars@mysql.com committed
56 57
#define BITMAP_STACKBUF_SIZE (128/8)

58
KEY_CREATE_INFO default_key_create_info= { HA_KEY_ALG_UNDEF, 0, {NullS,0} };
59

60
/* static functions defined in this file */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
61

62
static handler *create_default(TABLE_SHARE *table, MEM_ROOT *mem_root);
63

64
static SHOW_COMP_OPTION have_yes= SHOW_OPTION_YES;
65 66

/* number of entries in handlertons[] */
67
ulong total_ha= 0;
68
/* number of storage engines (from handlertons[]) that support 2pc */
69
ulong total_ha_2pc= 0;
70
/* size of savepoint storage area (see ha_init) */
71
ulong savepoint_alloc_size= 0;
72

acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
73
static const LEX_STRING sys_table_aliases[]=
74
{
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
75 76 77 78 79 80
  {(char*)STRING_WITH_LEN("INNOBASE")},  {(char*)STRING_WITH_LEN("INNODB")},
  {(char*)STRING_WITH_LEN("NDB")},       {(char*)STRING_WITH_LEN("NDBCLUSTER")},
  {(char*)STRING_WITH_LEN("BDB")},       {(char*)STRING_WITH_LEN("BERKELEYDB")},
  {(char*)STRING_WITH_LEN("HEAP")},      {(char*)STRING_WITH_LEN("MEMORY")},
  {(char*)STRING_WITH_LEN("MERGE")},     {(char*)STRING_WITH_LEN("MRG_MYISAM")},
  {NullS, 0}
81
};
82

bk@work.mysql.com's avatar
bk@work.mysql.com committed
83
const char *ha_row_type[] = {
84
  "", "FIXED", "DYNAMIC", "COMPRESSED", "REDUNDANT", "COMPACT", "?","?","?"
bk@work.mysql.com's avatar
bk@work.mysql.com committed
85 86
};

monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
87
const char *tx_isolation_names[] =
88 89 90
{ "READ-UNCOMMITTED", "READ-COMMITTED", "REPEATABLE-READ", "SERIALIZABLE",
  NullS};
TYPELIB tx_isolation_typelib= {array_elements(tx_isolation_names)-1,"",
91
			       tx_isolation_names, NULL};
bk@work.mysql.com's avatar
bk@work.mysql.com committed
92

93
static TYPELIB known_extensions= {0,"known_exts", NULL, NULL};
94
uint known_extensions_id= 0;
95

acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
96 97 98 99 100 101 102 103 104 105 106 107 108

/*
  Return the default storage engine handlerton for thread
  
  SYNOPSIS
    ha_default_handlerton(thd)
    thd         current thread
  
  RETURN
    pointer to handlerton
*/

handlerton *ha_default_handlerton(THD *thd)
109
{
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
  return (thd->variables.table_type != NULL) ?
          thd->variables.table_type :
          (global_system_variables.table_type != NULL ?
           global_system_variables.table_type : &myisam_hton);
}


/*
  Return the storage engine handlerton for the supplied name
  
  SYNOPSIS
    ha_resolve_by_name(thd, name)
    thd         current thread
    name        name of storage engine
  
  RETURN
    pointer to handlerton
*/

handlerton *ha_resolve_by_name(THD *thd, const LEX_STRING *name)
{
  const LEX_STRING *table_alias;
132
  st_plugin_int *plugin;
133

acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
134 135 136
redo:
  /* my_strnncoll is a macro and gcc doesn't do early expansion of macro */
  if (thd && !my_charset_latin1.coll->strnncoll(&my_charset_latin1,
137
                           (const uchar *)name->str, name->length,
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
138 139
                           (const uchar *)STRING_WITH_LEN("DEFAULT"), 0))
    return ha_default_handlerton(thd);
140

141
  if ((plugin= plugin_lock(name, MYSQL_STORAGE_ENGINE_PLUGIN)))
142
  {
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
143
    handlerton *hton= (handlerton *)plugin->data;
144 145 146
    if (!(hton->flags & HTON_NOT_USER_SELECTABLE))
      return hton;
    plugin_unlock(plugin);
147
  }
148

149
  /*
150
    We check for the historical aliases.
151
  */
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
152
  for (table_alias= sys_table_aliases; table_alias->str; table_alias+= 2)
153
  {
154
    if (!my_strnncoll(&my_charset_latin1,
155
                      (const uchar *)name->str, name->length,
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
156 157 158 159 160
                      (const uchar *)table_alias->str, table_alias->length))
    {
      name= table_alias + 1;
      goto redo;
    }
161
  }
162

163
  return NULL;
164
}
monty@mysql.com's avatar
monty@mysql.com committed
165 166


167
const char *ha_get_storage_engine(enum legacy_db_type db_type)
168
{
169
  switch (db_type) {
170 171 172
  case DB_TYPE_DEFAULT:
    return "DEFAULT";
  default:
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
173 174
    if (db_type > DB_TYPE_UNKNOWN && db_type < DB_TYPE_DEFAULT &&
        installed_htons[db_type])
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
175
      return hton2plugin[installed_htons[db_type]->slot]->name.str;
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
176 177 178
    /* fall through */
  case DB_TYPE_UNKNOWN:
    return "UNKNOWN";
179 180 181
  }
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
182

183
static handler *create_default(TABLE_SHARE *table, MEM_ROOT *mem_root)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
184
{
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
185
  handlerton *hton= ha_default_handlerton(current_thd);
186
  return (hton && hton->create) ? hton->create(table, mem_root) : NULL;
187 188 189 190 191
}


handlerton *ha_resolve_by_legacy_type(THD *thd, enum legacy_db_type db_type)
{
192
  switch (db_type) {
193
  case DB_TYPE_DEFAULT:
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
194
    return ha_default_handlerton(thd);
195 196 197
  case DB_TYPE_UNKNOWN:
    return NULL;
  default:
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
198 199
    if (db_type > DB_TYPE_UNKNOWN && db_type < DB_TYPE_DEFAULT)
      return installed_htons[db_type];
200
    return NULL;
201
  }
202 203 204
}


205
/* Use other database handler if databasehandler is not compiled in */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
206

207
handlerton *ha_checktype(THD *thd, enum legacy_db_type database_type,
208
                          bool no_substitute, bool report_error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
209
{
210 211 212 213
  handlerton *hton= ha_resolve_by_legacy_type(thd, database_type);
  if (ha_storage_engine_is_enabled(hton))
    return hton;

214 215 216 217 218 219 220
  if (no_substitute)
  {
    if (report_error)
    {
      const char *engine_name= ha_get_storage_engine(database_type);
      my_error(ER_FEATURE_DISABLED,MYF(0),engine_name,engine_name);
    }
221
    return NULL;
222 223
  }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
224 225 226
  switch (database_type) {
#ifndef NO_HASH
  case DB_TYPE_HASH:
227
    return ha_resolve_by_legacy_type(thd, DB_TYPE_HASH);
228
#endif
229
  case DB_TYPE_MRG_ISAM:
230
    return ha_resolve_by_legacy_type(thd, DB_TYPE_MRG_MYISAM);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
231 232 233
  default:
    break;
  }
234

acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
235
  return ha_default_handlerton(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
236 237 238
} /* ha_checktype */


239
handler *get_new_handler(TABLE_SHARE *share, MEM_ROOT *alloc,
240
                         handlerton *db_type)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
241
{
242 243 244
  handler *file;
  DBUG_ENTER("get_new_handler");
  DBUG_PRINT("enter", ("alloc: 0x%lx", (long) alloc));
245

246
  if (db_type && db_type->state == SHOW_OPTION_YES && db_type->create)
247
  {
248 249 250
    if ((file= db_type->create(share, alloc)))
      file->init();
    DBUG_RETURN(file);
251
  }
252 253 254 255 256 257 258
  /*
    Try the default table type
    Here the call to current_thd() is ok as we call this function a lot of
    times but we enter this branch very seldom.
  */
  DBUG_RETURN(get_new_handler(share, alloc,
                              current_thd->variables.table_type));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
259 260
}

261

262
#ifdef WITH_PARTITION_STORAGE_ENGINE
263 264 265 266 267 268
handler *get_ha_partition(partition_info *part_info)
{
  ha_partition *partition;
  DBUG_ENTER("get_ha_partition");
  if ((partition= new ha_partition(part_info)))
  {
269
    if (partition->initialise_partition(current_thd->mem_root))
270 271 272 273
    {
      delete partition;
      partition= 0;
    }
274 275
    else
      partition->init();
276 277 278 279 280 281 282 283 284 285
  }
  else
  {
    my_error(ER_OUTOFMEMORY, MYF(0), sizeof(ha_partition));
  }
  DBUG_RETURN(((handler*) partition));
}
#endif


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 333 334 335 336
/*
  Register handler error messages for use with my_error().

  SYNOPSIS
    ha_init_errors()

  RETURN
    0           OK
    != 0        Error
*/

static int ha_init_errors(void)
{
#define SETMSG(nr, msg) errmsgs[(nr) - HA_ERR_FIRST]= (msg)
  const char    **errmsgs;

  /* Allocate a pointer array for the error message strings. */
  /* Zerofill it to avoid uninitialized gaps. */
  if (! (errmsgs= (const char**) my_malloc(HA_ERR_ERRORS * sizeof(char*),
                                           MYF(MY_WME | MY_ZEROFILL))))
    return 1;

  /* Set the dedicated error messages. */
  SETMSG(HA_ERR_KEY_NOT_FOUND,          ER(ER_KEY_NOT_FOUND));
  SETMSG(HA_ERR_FOUND_DUPP_KEY,         ER(ER_DUP_KEY));
  SETMSG(HA_ERR_RECORD_CHANGED,         "Update wich is recoverable");
  SETMSG(HA_ERR_WRONG_INDEX,            "Wrong index given to function");
  SETMSG(HA_ERR_CRASHED,                ER(ER_NOT_KEYFILE));
  SETMSG(HA_ERR_WRONG_IN_RECORD,        ER(ER_CRASHED_ON_USAGE));
  SETMSG(HA_ERR_OUT_OF_MEM,             "Table handler out of memory");
  SETMSG(HA_ERR_NOT_A_TABLE,            "Incorrect file format '%.64s'");
  SETMSG(HA_ERR_WRONG_COMMAND,          "Command not supported");
  SETMSG(HA_ERR_OLD_FILE,               ER(ER_OLD_KEYFILE));
  SETMSG(HA_ERR_NO_ACTIVE_RECORD,       "No record read in update");
  SETMSG(HA_ERR_RECORD_DELETED,         "Intern record deleted");
  SETMSG(HA_ERR_RECORD_FILE_FULL,       ER(ER_RECORD_FILE_FULL));
  SETMSG(HA_ERR_INDEX_FILE_FULL,        "No more room in index file '%.64s'");
  SETMSG(HA_ERR_END_OF_FILE,            "End in next/prev/first/last");
  SETMSG(HA_ERR_UNSUPPORTED,            ER(ER_ILLEGAL_HA));
  SETMSG(HA_ERR_TO_BIG_ROW,             "Too big row");
  SETMSG(HA_WRONG_CREATE_OPTION,        "Wrong create option");
  SETMSG(HA_ERR_FOUND_DUPP_UNIQUE,      ER(ER_DUP_UNIQUE));
  SETMSG(HA_ERR_UNKNOWN_CHARSET,        "Can't open charset");
  SETMSG(HA_ERR_WRONG_MRG_TABLE_DEF,    ER(ER_WRONG_MRG_TABLE));
  SETMSG(HA_ERR_CRASHED_ON_REPAIR,      ER(ER_CRASHED_ON_REPAIR));
  SETMSG(HA_ERR_CRASHED_ON_USAGE,       ER(ER_CRASHED_ON_USAGE));
  SETMSG(HA_ERR_LOCK_WAIT_TIMEOUT,      ER(ER_LOCK_WAIT_TIMEOUT));
  SETMSG(HA_ERR_LOCK_TABLE_FULL,        ER(ER_LOCK_TABLE_FULL));
  SETMSG(HA_ERR_READ_ONLY_TRANSACTION,  ER(ER_READ_ONLY_TRANSACTION));
  SETMSG(HA_ERR_LOCK_DEADLOCK,          ER(ER_LOCK_DEADLOCK));
  SETMSG(HA_ERR_CANNOT_ADD_FOREIGN,     ER(ER_CANNOT_ADD_FOREIGN));
337 338
  SETMSG(HA_ERR_NO_REFERENCED_ROW,      ER(ER_NO_REFERENCED_ROW_2));
  SETMSG(HA_ERR_ROW_IS_REFERENCED,      ER(ER_ROW_IS_REFERENCED_2));
339 340 341 342 343
  SETMSG(HA_ERR_NO_SAVEPOINT,           "No savepoint with that name");
  SETMSG(HA_ERR_NON_UNIQUE_BLOCK_SIZE,  "Non unique key block size");
  SETMSG(HA_ERR_NO_SUCH_TABLE,          "No such table: '%.64s'");
  SETMSG(HA_ERR_TABLE_EXIST,            ER(ER_TABLE_EXISTS_ERROR));
  SETMSG(HA_ERR_NO_CONNECTION,          "Could not connect to storage engine");
344
  SETMSG(HA_ERR_TABLE_DEF_CHANGED,      ER(ER_TABLE_DEF_CHANGED));
345
  SETMSG(HA_ERR_FOREIGN_DUPLICATE_KEY,  "FK constraint would lead to duplicate key");
346
  SETMSG(HA_ERR_TABLE_NEEDS_UPGRADE,    ER(ER_TABLE_NEEDS_UPGRADE));
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

  /* Register the error messages for use with my_error(). */
  return my_error_register(errmsgs, HA_ERR_FIRST, HA_ERR_LAST);
}


/*
  Unregister handler error messages.

  SYNOPSIS
    ha_finish_errors()

  RETURN
    0           OK
    != 0        Error
*/

static int ha_finish_errors(void)
{
  const char    **errmsgs;

  /* Allocate a pointer array for the error message strings. */
  if (! (errmsgs= my_error_unregister(HA_ERR_FIRST, HA_ERR_LAST)))
    return 1;
  my_free((gptr) errmsgs, MYF(0));
  return 0;
}
serg@serg.mylan's avatar
serg@serg.mylan committed
374

375

acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
376
int ha_finalize_handlerton(st_plugin_int *plugin)
377
{
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
378
  handlerton *hton= (handlerton *)plugin->data;
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
379 380 381 382 383 384 385 386 387 388
  DBUG_ENTER("ha_finalize_handlerton");

  switch (hton->state)
  {
  case SHOW_OPTION_NO:
  case SHOW_OPTION_DISABLED:
    break;
  case SHOW_OPTION_YES:
    if (installed_htons[hton->db_type] == hton)
      installed_htons[hton->db_type]= NULL;
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
389 390
    if (hton->panic && hton->panic(HA_PANIC_CLOSE))
      DBUG_RETURN(1);
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
391 392 393
    break;
  };
  DBUG_RETURN(0);
394
}
395

396

acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
397
int ha_initialize_handlerton(st_plugin_int *plugin)
398
{
399 400
  handlerton *hton= ((st_mysql_storage_engine *)plugin->plugin->info)->handlerton;
  DBUG_ENTER("ha_initialize_handlerton");
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
401

serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
402 403
  plugin->data= hton; // shortcut for the future

404 405 406 407
  /*
    the switch below and hton->state should be removed when
    command-line options for plugins will be implemented
  */
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
408
  switch (hton->state) {
409 410 411 412
  case SHOW_OPTION_NO:
    break;
  case SHOW_OPTION_YES:
    {
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
413
      uint tmp;
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
414
      /* now check the db_type for conflict */
415
      if (hton->db_type <= DB_TYPE_UNKNOWN ||
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
416 417 418 419
          hton->db_type >= DB_TYPE_DEFAULT ||
          installed_htons[hton->db_type])
      {
        int idx= (int) DB_TYPE_FIRST_DYNAMIC;
420

acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
421 422 423 424 425 426 427 428 429 430
        while (idx < (int) DB_TYPE_DEFAULT && installed_htons[idx])
          idx++;

        if (idx == (int) DB_TYPE_DEFAULT)
        {
          sql_print_warning("Too many storage engines!");
          DBUG_RETURN(1);
        }
        if (hton->db_type != DB_TYPE_UNKNOWN)
          sql_print_warning("Storage engine '%s' has conflicting typecode. "
431
                            "Assigning value %d.", plugin->plugin->name, idx);
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
432 433 434
        hton->db_type= (enum legacy_db_type) idx;
      }
      installed_htons[hton->db_type]= hton;
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
435
      tmp= hton->savepoint_offset;
436 437 438
      hton->savepoint_offset= savepoint_alloc_size;
      savepoint_alloc_size+= tmp;
      hton->slot= total_ha++;
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
439
      hton2plugin[hton->slot]=plugin;
440 441
      if (hton->prepare)
        total_ha_2pc++;
442 443 444 445 446 447 448 449 450 451
      break;
    }
    /* fall through */
  default:
    hton->state= SHOW_OPTION_DISABLED;
    break;
  }
  DBUG_RETURN(0);
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
452 453
int ha_init()
{
454
  int error= 0;
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
455 456
  DBUG_ENTER("ha_init");

457
  if (ha_init_errors())
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
458
    DBUG_RETURN(1);
serg@serg.mylan's avatar
serg@serg.mylan committed
459

460
  DBUG_ASSERT(total_ha < MAX_HA);
461 462 463 464 465 466
  /*
    Check if there is a transaction-capable storage engine besides the
    binary log (which is considered a transaction-capable storage engine in
    counting total_ha)
  */
  opt_using_transactions= total_ha>(ulong)opt_bin_log;
467
  savepoint_alloc_size+= sizeof(SAVEPOINT);
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
468
  DBUG_RETURN(error);
469 470
}

serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
471 472 473 474
/*
  close, flush or restart databases
  Ignore this for other databases than ours
*/
475

serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
476
static my_bool panic_handlerton(THD *unused1, st_plugin_int *plugin, void *arg)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
477
{
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
478
  handlerton *hton= (handlerton *)plugin->data;
479 480 481 482
  if (hton->state == SHOW_OPTION_YES && hton->panic)
    ((int*)arg)[0]|= hton->panic((enum ha_panic_function)((int*)arg)[1]);
  return FALSE;
}
483

484 485 486 487

int ha_panic(enum ha_panic_function flag)
{
  int error[2];
488

489 490
  error[0]= 0; error[1]= (int)flag;
  plugin_foreach(NULL, panic_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, error);
491

492 493 494
  if (flag == HA_PANIC_CLOSE && ha_finish_errors())
    error[0]= 1;
  return error[0];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
495 496
} /* ha_panic */

497 498 499
static my_bool dropdb_handlerton(THD *unused1, st_plugin_int *plugin,
                                 void *path)
{
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
500
  handlerton *hton= (handlerton *)plugin->data;
501 502 503 504 505 506
  if (hton->state == SHOW_OPTION_YES && hton->drop_database)
    hton->drop_database((char *)path);
  return FALSE;
}


507 508
void ha_drop_database(char* path)
{
509 510
  plugin_foreach(NULL, dropdb_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, path);
}
511

512 513 514 515

static my_bool closecon_handlerton(THD *thd, st_plugin_int *plugin,
                                   void *unused)
{
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
516 517 518 519 520
  handlerton *hton= (handlerton *)plugin->data;
  /*
    there's no need to rollback here as all transactions must
    be rolled back already
  */
521 522 523 524
  if (hton->state == SHOW_OPTION_YES && hton->close_connection &&
      thd->ha_data[hton->slot])
    hton->close_connection(thd);
  return FALSE;
525
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
526

527

528
/* don't bother to rollback here, it's done already */
529 530
void ha_close_connection(THD* thd)
{
531
  plugin_foreach(thd, closecon_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, 0);
532 533 534 535 536
}

/* ========================================================================
 ======================= TRANSACTIONS ===================================*/

serg@serg.mylan's avatar
serg@serg.mylan committed
537 538 539 540 541 542 543 544 545
/*
  Register a storage engine for a transaction

  DESCRIPTION
    Every storage engine MUST call this function when it starts
    a transaction or a statement (that is it must be called both for the
    "beginning of transaction" and "beginning of statement").
    Only storage engines registered for the transaction/statement
    will know when to commit/rollback it.
546 547 548 549 550

  NOTE
    trans_register_ha is idempotent - storage engine may register many
    times per transaction.

serg@serg.mylan's avatar
serg@serg.mylan committed
551
*/
552 553 554
void trans_register_ha(THD *thd, bool all, handlerton *ht_arg)
{
  THD_TRANS *trans;
555
  handlerton **ht;
serg@serg.mylan's avatar
serg@serg.mylan committed
556 557 558
  DBUG_ENTER("trans_register_ha");
  DBUG_PRINT("enter",("%s", all ? "all" : "stmt"));

559 560 561 562 563 564 565 566
  if (all)
  {
    trans= &thd->transaction.all;
    thd->server_status|= SERVER_STATUS_IN_TRANS;
  }
  else
    trans= &thd->transaction.stmt;

567
  for (ht=trans->ht; *ht; ht++)
568 569 570
    if (*ht == ht_arg)
      DBUG_VOID_RETURN;  /* already registered, return */

571
  trans->ht[trans->nht++]=ht_arg;
572
  DBUG_ASSERT(*ht == ht_arg);
573
  trans->no_2pc|=(ht_arg->prepare==0);
574 575
  if (thd->transaction.xid_state.xid.is_null())
    thd->transaction.xid_state.xid.set(thd->query_id);
serg@serg.mylan's avatar
serg@serg.mylan committed
576
  DBUG_VOID_RETURN;
577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
}

/*
  RETURN
      0  - ok
      1  - error, transaction was rolled back
*/
int ha_prepare(THD *thd)
{
  int error=0, all=1;
  THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
  handlerton **ht=trans->ht;
  DBUG_ENTER("ha_prepare");
#ifdef USING_TRANSACTIONS
  if (trans->nht)
  {
    for (; *ht; ht++)
    {
      int err;
      statistic_increment(thd->status_var.ha_prepare_count,&LOCK_status);
597
      if ((*ht)->prepare)
598
      {
599 600 601 602 603 604 605 606 607 608 609
        if ((err= (*(*ht)->prepare)(thd, all)))
        {
          my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
          ha_rollback_trans(thd, all);
          error=1;
          break;
        }
      }
      else
      {
        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
610
                            ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
611
                            hton2plugin[(*ht)->slot]->name.str);
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630
      }
    }
  }
#endif /* USING_TRANSACTIONS */
  DBUG_RETURN(error);
}

/*
  RETURN
      0  - ok
      1  - transaction was rolled back
      2  - error during commit, data may be inconsistent
*/
int ha_commit_trans(THD *thd, bool all)
{
  int error= 0, cookie= 0;
  THD_TRANS *trans= all ? &thd->transaction.all : &thd->transaction.stmt;
  bool is_real_trans= all || thd->transaction.all.nht == 0;
  handlerton **ht= trans->ht;
631
  my_xid xid= thd->transaction.xid_state.xid.get_my_xid();
632
  DBUG_ENTER("ha_commit_trans");
633

634
  if (thd->in_sub_stmt)
635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653
  {
    /*
      Since we don't support nested statement transactions in 5.0,
      we can't commit or rollback stmt transactions while we are inside
      stored functions or triggers. So we simply do nothing now.
      TODO: This should be fixed in later ( >= 5.1) releases.
    */
    if (!all)
      DBUG_RETURN(0);
    /*
      We assume that all statements which commit or rollback main transaction
      are prohibited inside of stored functions or triggers. So they should
      bail out with error even before ha_commit_trans() call. To be 100% safe
      let us throw error in non-debug builds.
    */
    DBUG_ASSERT(0);
    my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
    DBUG_RETURN(2);
  }
654 655 656
#ifdef USING_TRANSACTIONS
  if (trans->nht)
  {
657 658 659 660 661
    if (is_real_trans && wait_if_global_read_lock(thd, 0, 0))
    {
      ha_rollback_trans(thd, all);
      DBUG_RETURN(1);
    }
662
    DBUG_EXECUTE_IF("crash_commit_before", abort(););
663 664 665 666 667

    /* Close all cursors that can not survive COMMIT */
    if (is_real_trans)                          /* not a statement commit */
      thd->stmt_map.close_transient_cursors();

668 669 670 671 672 673 674 675
    if (!trans->no_2pc && trans->nht > 1)
    {
      for (; *ht && !error; ht++)
      {
        int err;
        if ((err= (*(*ht)->prepare)(thd, all)))
        {
          my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
676
          error= 1;
677 678 679
        }
        statistic_increment(thd->status_var.ha_prepare_count,&LOCK_status);
      }
680
      DBUG_EXECUTE_IF("crash_commit_after_prepare", abort(););
681 682
      if (error || (is_real_trans && xid &&
                    (error= !(cookie= tc_log->log(thd, xid)))))
683 684
      {
        ha_rollback_trans(thd, all);
685 686
        error= 1;
        goto end;
687
      }
688
      DBUG_EXECUTE_IF("crash_commit_after_log", abort(););
689 690
    }
    error=ha_commit_one_phase(thd, all) ? cookie ? 2 : 1 : 0;
691
    DBUG_EXECUTE_IF("crash_commit_before_unlog", abort(););
692
    if (cookie)
693
      tc_log->unlog(cookie, xid);
694
    DBUG_EXECUTE_IF("crash_commit_after", abort(););
695 696 697
end:
    if (is_real_trans)
      start_waiting_global_read_lock(thd);
698 699 700 701 702
  }
#endif /* USING_TRANSACTIONS */
  DBUG_RETURN(error);
}

703 704 705 706
/*
  NOTE - this function does not care about global read lock.
  A caller should.
*/
707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
int ha_commit_one_phase(THD *thd, bool all)
{
  int error=0;
  THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
  bool is_real_trans=all || thd->transaction.all.nht == 0;
  handlerton **ht=trans->ht;
  DBUG_ENTER("ha_commit_one_phase");
#ifdef USING_TRANSACTIONS
  if (trans->nht)
  {
    for (ht=trans->ht; *ht; ht++)
    {
      int err;
      if ((err= (*(*ht)->commit)(thd, all)))
      {
        my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
        error=1;
      }
      statistic_increment(thd->status_var.ha_commit_count,&LOCK_status);
      *ht= 0;
    }
    trans->nht=0;
    trans->no_2pc=0;
    if (is_real_trans)
731
      thd->transaction.xid_state.xid.null();
732 733 734 735 736
    if (all)
    {
#ifdef HAVE_QUERY_CACHE
      if (thd->transaction.changed_tables)
        query_cache.invalidate(thd->transaction.changed_tables);
737
#endif
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752
      thd->variables.tx_isolation=thd->session_tx_isolation;
      thd->transaction.cleanup();
    }
  }
#endif /* USING_TRANSACTIONS */
  DBUG_RETURN(error);
}


int ha_rollback_trans(THD *thd, bool all)
{
  int error=0;
  THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
  bool is_real_trans=all || thd->transaction.all.nht == 0;
  DBUG_ENTER("ha_rollback_trans");
753
  if (thd->in_sub_stmt)
754 755 756 757 758 759 760 761 762 763 764 765
  {
    /*
      If we are inside stored function or trigger we should not commit or
      rollback current statement transaction. See comment in ha_commit_trans()
      call for more information.
    */
    if (!all)
      DBUG_RETURN(0);
    DBUG_ASSERT(0);
    my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
    DBUG_RETURN(1);
  }
766 767 768
#ifdef USING_TRANSACTIONS
  if (trans->nht)
  {
769 770 771 772
    /* Close all cursors that can not survive ROLLBACK */
    if (is_real_trans)                          /* not a statement commit */
      thd->stmt_map.close_transient_cursors();

773 774 775 776 777 778 779 780 781 782 783 784 785 786
    for (handlerton **ht=trans->ht; *ht; ht++)
    {
      int err;
      if ((err= (*(*ht)->rollback)(thd, all)))
      { // cannot happen
        my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
        error=1;
      }
      statistic_increment(thd->status_var.ha_rollback_count,&LOCK_status);
      *ht= 0;
    }
    trans->nht=0;
    trans->no_2pc=0;
    if (is_real_trans)
787
      thd->transaction.xid_state.xid.null();
788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809
    if (all)
    {
      thd->variables.tx_isolation=thd->session_tx_isolation;
      thd->transaction.cleanup();
    }
  }
#endif /* USING_TRANSACTIONS */
  /*
    If a non-transactional table was updated, warn; don't warn if this is a
    slave thread (because when a slave thread executes a ROLLBACK, it has
    been read from the binary log, so it's 100% sure and normal to produce
    error ER_WARNING_NOT_COMPLETE_ROLLBACK. If we sent the warning to the
    slave SQL thread, it would not stop the thread but just be printed in
    the error log; but we don't want users to wonder why they have this
    message in the error log, so we don't send it.
  */
  if (is_real_trans && (thd->options & OPTION_STATUS_NO_TRANS_UPDATE) &&
      !thd->slave_thread)
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                 ER_WARNING_NOT_COMPLETE_ROLLBACK,
                 ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
  DBUG_RETURN(error);
810 811
}

812
/*
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
813
  This is used to commit or rollback a single statement depending on the value
814 815 816 817 818
  of error. Note that if the autocommit is on, then the following call inside
  InnoDB will commit or rollback the whole transaction (= the statement). The
  autocommit mechanism built into InnoDB is based on counting locks, but if
  the user has used LOCK TABLES then that mechanism does not know to do the
  commit.
819 820
*/

bk@work.mysql.com's avatar
bk@work.mysql.com committed
821 822 823
int ha_autocommit_or_rollback(THD *thd, int error)
{
  DBUG_ENTER("ha_autocommit_or_rollback");
824
#ifdef USING_TRANSACTIONS
825
  if (thd->transaction.stmt.nht)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
826
  {
827 828 829 830 831 832 833
    if (!error)
    {
      if (ha_commit_stmt(thd))
	error=1;
    }
    else
      (void) ha_rollback_stmt(thd);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
834

835
    thd->variables.tx_isolation=thd->session_tx_isolation;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
836 837 838 839 840
  }
#endif
  DBUG_RETURN(error);
}

monty@mysql.com's avatar
monty@mysql.com committed
841

842 843 844 845 846 847 848
struct xahton_st {
  XID *xid;
  int result;
};

static my_bool xacommit_handlerton(THD *unused1, st_plugin_int *plugin,
                                   void *arg)
849
{
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
850
  handlerton *hton= (handlerton *)plugin->data;
851 852 853 854 855 856 857
  if (hton->state == SHOW_OPTION_YES && hton->recover)
  {
    hton->commit_by_xid(((struct xahton_st *)arg)->xid);
    ((struct xahton_st *)arg)->result= 0;
  }
  return FALSE;
}
858

859 860 861
static my_bool xarollback_handlerton(THD *unused1, st_plugin_int *plugin,
                                     void *arg)
{
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
862
  handlerton *hton= (handlerton *)plugin->data;
863
  if (hton->state == SHOW_OPTION_YES && hton->recover)
monty@mysql.com's avatar
monty@mysql.com committed
864
  {
865 866
    hton->rollback_by_xid(((struct xahton_st *)arg)->xid);
    ((struct xahton_st *)arg)->result= 0;
monty@mysql.com's avatar
monty@mysql.com committed
867
  }
868 869 870 871 872 873 874 875 876
  return FALSE;
}


int ha_commit_or_rollback_by_xid(XID *xid, bool commit)
{
  struct xahton_st xaop;
  xaop.xid= xid;
  xaop.result= 1;
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
877

878 879 880 881
  plugin_foreach(NULL, commit ? xacommit_handlerton : xarollback_handlerton,
                 MYSQL_STORAGE_ENGINE_PLUGIN, &xaop);

  return xaop.result;
882
}
883

monty@mysql.com's avatar
monty@mysql.com committed
884

serg@serg.mylan's avatar
serg@serg.mylan committed
885 886 887 888 889 890 891 892 893 894
#ifndef DBUG_OFF
/* this does not need to be multi-byte safe or anything */
static char* xid_to_str(char *buf, XID *xid)
{
  int i;
  char *s=buf;
  *s++='\'';
  for (i=0; i < xid->gtrid_length+xid->bqual_length; i++)
  {
    uchar c=(uchar)xid->data[i];
895 896
    /* is_next_dig is set if next character is a number */
    bool is_next_dig= FALSE;
serg@serg.mylan's avatar
serg@serg.mylan committed
897 898
    if (i < XIDDATASIZE)
    {
899 900
      char ch= xid->data[i+1];
      is_next_dig= (ch >= '0' && ch <='9');
serg@serg.mylan's avatar
serg@serg.mylan committed
901
    }
serg@serg.mylan's avatar
serg@serg.mylan committed
902 903 904 905 906 907 908 909 910 911 912 913
    if (i == xid->gtrid_length)
    {
      *s++='\'';
      if (xid->bqual_length)
      {
        *s++='.';
        *s++='\'';
      }
    }
    if (c < 32 || c > 126)
    {
      *s++='\\';
914 915 916 917 918
      /*
        If next character is a number, write current character with
        3 octal numbers to ensure that the next number is not seen
        as part of the octal number
      */
serg@serg.mylan's avatar
serg@serg.mylan committed
919 920 921 922 923
      if (c > 077 || is_next_dig)
        *s++=_dig_vec_lower[c >> 6];
      if (c > 007 || is_next_dig)
        *s++=_dig_vec_lower[(c >> 3) & 7];
      *s++=_dig_vec_lower[c & 7];
serg@serg.mylan's avatar
serg@serg.mylan committed
924 925 926 927 928 929 930 931 932 933 934 935 936 937
    }
    else
    {
      if (c == '\'' || c == '\\')
        *s++='\\';
      *s++=c;
    }
  }
  *s++='\'';
  *s=0;
  return buf;
}
#endif

938 939
/*
  recover() step of xa
940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955

  NOTE
   there are three modes of operation:

   - automatic recover after a crash
     in this case commit_list != 0, tc_heuristic_recover==0
     all xids from commit_list are committed, others are rolled back

   - manual (heuristic) recover
     in this case commit_list==0, tc_heuristic_recover != 0
     DBA has explicitly specified that all prepared transactions should
     be committed (or rolled back).

   - no recovery (MySQL did not detect a crash)
     in this case commit_list==0, tc_heuristic_recover == 0
     there should be no prepared transactions in this case.
956
*/
957

958 959 960 961 962 963 964
struct xarecover_st
{
  int len, found_foreign_xids, found_my_xids;
  XID *list;
  HASH *commit_list;
  bool dry_run;
};
965

966 967 968
static my_bool xarecover_handlerton(THD *unused, st_plugin_int *plugin,
                                    void *arg)
{
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
969
  handlerton *hton= (handlerton *)plugin->data;
970 971
  struct xarecover_st *info= (struct xarecover_st *) arg;
  int got;
972

973
  if (hton->state == SHOW_OPTION_YES && hton->recover)
974
  {
975
    while ((got= hton->recover(info->list, info->len)) > 0 )
976
    {
serg@serg.mylan's avatar
serg@serg.mylan committed
977
      sql_print_information("Found %d prepared transaction(s) in %s",
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
978
                            got, hton2plugin[hton->slot]->name.str);
979 980
      for (int i=0; i < got; i ++)
      {
981
        my_xid x=info->list[i].get_my_xid();
982
        if (!x) // not "mine" - that is generated by external TM
983
        {
serg@serg.mylan's avatar
serg@serg.mylan committed
984 985
#ifndef DBUG_OFF
          char buf[XIDDATASIZE*4+6]; // see xid_to_str
986
          sql_print_information("ignore xid %s", xid_to_str(buf, info->list+i));
serg@serg.mylan's avatar
serg@serg.mylan committed
987
#endif
988 989
          xid_cache_insert(info->list+i, XA_PREPARED);
          info->found_foreign_xids++;
990 991
          continue;
        }
992
        if (info->dry_run)
993
        {
994
          info->found_my_xids++;
995
          continue;
996 997
        }
        // recovery mode
998 999
        if (info->commit_list ?
            hash_search(info->commit_list, (byte *)&x, sizeof(x)) != 0 :
1000
            tc_heuristic_recover == TC_HEURISTIC_RECOVER_COMMIT)
serg@serg.mylan's avatar
serg@serg.mylan committed
1001 1002 1003
        {
#ifndef DBUG_OFF
          char buf[XIDDATASIZE*4+6]; // see xid_to_str
1004
          sql_print_information("commit xid %s", xid_to_str(buf, info->list+i));
serg@serg.mylan's avatar
serg@serg.mylan committed
1005
#endif
1006
          hton->commit_by_xid(info->list+i);
serg@serg.mylan's avatar
serg@serg.mylan committed
1007
        }
1008
        else
serg@serg.mylan's avatar
serg@serg.mylan committed
1009 1010 1011
        {
#ifndef DBUG_OFF
          char buf[XIDDATASIZE*4+6]; // see xid_to_str
1012 1013
          sql_print_information("rollback xid %s",
                                xid_to_str(buf, info->list+i));
serg@serg.mylan's avatar
serg@serg.mylan committed
1014
#endif
1015
          hton->rollback_by_xid(info->list+i);
serg@serg.mylan's avatar
serg@serg.mylan committed
1016
        }
1017
      }
1018
      if (got < info->len)
1019
        break;
1020 1021
    }
  }
1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
  return FALSE;
}

int ha_recover(HASH *commit_list)
{
  struct xarecover_st info;
  DBUG_ENTER("ha_recover");
  info.found_foreign_xids= info.found_my_xids= 0;
  info.commit_list= commit_list;
  info.dry_run= (info.commit_list==0 && tc_heuristic_recover==0);
  info.list= NULL;

  /* commit_list and tc_heuristic_recover cannot be set both */
  DBUG_ASSERT(info.commit_list==0 || tc_heuristic_recover==0);
  /* if either is set, total_ha_2pc must be set too */
  DBUG_ASSERT(info.dry_run || total_ha_2pc>(ulong)opt_bin_log);

  if (total_ha_2pc <= (ulong)opt_bin_log)
    DBUG_RETURN(0);

  if (info.commit_list)
    sql_print_information("Starting crash recovery...");

#ifndef WILL_BE_DELETED_LATER
  /*
    for now, only InnoDB supports 2pc. It means we can always safely
    rollback all pending transactions, without risking inconsistent data
  */
  DBUG_ASSERT(total_ha_2pc == (ulong) opt_bin_log+1); // only InnoDB and binlog
  tc_heuristic_recover= TC_HEURISTIC_RECOVER_ROLLBACK; // forcing ROLLBACK
  info.dry_run=FALSE;
#endif

  for (info.len= MAX_XID_LIST_SIZE ; 
       info.list==0 && info.len > MIN_XID_LIST_SIZE; info.len/=2)
  {
    info.list=(XID *)my_malloc(info.len*sizeof(XID), MYF(0));
  }
  if (!info.list)
  {
    sql_print_error(ER(ER_OUTOFMEMORY), info.len*sizeof(XID));
    DBUG_RETURN(1);
  }

  plugin_foreach(NULL, xarecover_handlerton, 
                 MYSQL_STORAGE_ENGINE_PLUGIN, &info);

  my_free((gptr)info.list, MYF(0));
  if (info.found_foreign_xids)
    sql_print_warning("Found %d prepared XA transactions", 
                      info.found_foreign_xids);
  if (info.dry_run && info.found_my_xids)
1074 1075 1076 1077 1078 1079 1080
  {
    sql_print_error("Found %d prepared transactions! It means that mysqld was "
                    "not shut down properly last time and critical recovery "
                    "information (last binlog or %s file) was manually deleted "
                    "after a crash. You have to start mysqld with "
                    "--tc-heuristic-recover switch to commit or rollback "
                    "pending transactions.",
1081
                    info.found_my_xids, opt_tc_log_file);
1082 1083
    DBUG_RETURN(1);
  }
1084
  if (info.commit_list)
serg@serg.mylan's avatar
serg@serg.mylan committed
1085
    sql_print_information("Crash recovery finished.");
1086
  DBUG_RETURN(0);
1087
}
1088

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1089
/*
1090
  return the list of XID's to a client, the same way SHOW commands do
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1091

1092 1093 1094 1095
  NOTE
    I didn't find in XA specs that an RM cannot return the same XID twice,
    so mysql_xa_recover does not filter XID's to ensure uniqueness.
    It can be easily fixed later, if necessary.
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1096
*/
1097
bool mysql_xa_recover(THD *thd)
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1098
{
1099 1100
  List<Item> field_list;
  Protocol *protocol= thd->protocol;
1101 1102
  int i=0;
  XID_STATE *xs;
1103 1104 1105 1106 1107 1108 1109 1110 1111 1112
  DBUG_ENTER("mysql_xa_recover");

  field_list.push_back(new Item_int("formatID",0,11));
  field_list.push_back(new Item_int("gtrid_length",0,11));
  field_list.push_back(new Item_int("bqual_length",0,11));
  field_list.push_back(new Item_empty_string("data",XIDDATASIZE));

  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
    DBUG_RETURN(1);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1113

1114
  pthread_mutex_lock(&LOCK_xid_cache);
monty@mishka.local's avatar
monty@mishka.local committed
1115
  while ((xs= (XID_STATE*)hash_element(&xid_cache, i++)))
1116
  {
1117
    if (xs->xa_state==XA_PREPARED)
1118
    {
1119 1120 1121 1122 1123 1124 1125
      protocol->prepare_for_resend();
      protocol->store_longlong((longlong)xs->xid.formatID, FALSE);
      protocol->store_longlong((longlong)xs->xid.gtrid_length, FALSE);
      protocol->store_longlong((longlong)xs->xid.bqual_length, FALSE);
      protocol->store(xs->xid.data, xs->xid.gtrid_length+xs->xid.bqual_length,
                      &my_charset_bin);
      if (protocol->write())
1126
      {
1127 1128
        pthread_mutex_unlock(&LOCK_xid_cache);
        DBUG_RETURN(1);
1129 1130
      }
    }
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1131
  }
1132

1133
  pthread_mutex_unlock(&LOCK_xid_cache);
1134
  send_eof(thd);
1135
  DBUG_RETURN(0);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1136
}
1137

1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154
/*
  This function should be called when MySQL sends rows of a SELECT result set
  or the EOF mark to the client. It releases a possible adaptive hash index
  S-latch held by thd in InnoDB and also releases a possible InnoDB query
  FIFO ticket to enter InnoDB. To save CPU time, InnoDB allows a thd to
  keep them over several calls of the InnoDB handler interface when a join
  is executed. But when we let the control to pass to the client they have
  to be released because if the application program uses mysql_use_result(),
  it may deadlock on the S-latch if the application on another connection
  performs another SQL query. In MySQL-4.1 this is even more important because
  there a connection can have several SELECT queries open at the same time.

  arguments:
  thd:           the thread handle of the current connection
  return value:  always 0
*/

1155 1156 1157
static my_bool release_temporary_latches(THD *thd, st_plugin_int *plugin,
                                 void *unused)
{
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1158
  handlerton *hton= (handlerton *)plugin->data;
1159 1160 1161 1162 1163 1164 1165 1166

  if (hton->state == SHOW_OPTION_YES && hton->release_temporary_latches)
    hton->release_temporary_latches(thd);

  return FALSE;
}


1167 1168
int ha_release_temporary_latches(THD *thd)
{
1169 1170 1171
  plugin_foreach(thd, release_temporary_latches, MYSQL_STORAGE_ENGINE_PLUGIN, 
                 NULL);

1172
  return 0;
1173 1174
}

1175
int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1176 1177
{
  int error=0;
1178 1179
  THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction.stmt :
                                        &thd->transaction.all);
1180 1181
  handlerton **ht=trans->ht, **end_ht;
  DBUG_ENTER("ha_rollback_to_savepoint");
1182

1183 1184 1185 1186 1187 1188 1189 1190 1191 1192
  trans->nht=sv->nht;
  trans->no_2pc=0;
  end_ht=ht+sv->nht;
  /*
    rolling back to savepoint in all storage engines that were part of the
    transaction when the savepoint was set
  */
  for (; ht < end_ht; ht++)
  {
    int err;
1193
    DBUG_ASSERT((*ht)->savepoint_set != 0);
1194 1195 1196 1197
    if ((err= (*(*ht)->savepoint_rollback)(thd, (byte *)(sv+1)+(*ht)->savepoint_offset)))
    { // cannot happen
      my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
      error=1;
1198
    }
1199 1200
    statistic_increment(thd->status_var.ha_savepoint_rollback_count,
                        &LOCK_status);
1201
    trans->no_2pc|=(*ht)->prepare == 0;
1202
  }
1203 1204 1205 1206 1207
  /*
    rolling back the transaction in all storage engines that were not part of
    the transaction when the savepoint was set
  */
  for (; *ht ; ht++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1208
  {
1209
    int err;
1210
    if ((err= (*(*ht)->rollback)(thd, !thd->in_sub_stmt)))
1211 1212 1213
    { // cannot happen
      my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
      error=1;
guilhem@mysql.com's avatar
guilhem@mysql.com committed
1214
    }
1215 1216
    statistic_increment(thd->status_var.ha_rollback_count,&LOCK_status);
    *ht=0; // keep it conveniently zero-filled
1217
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1218 1219 1220
  DBUG_RETURN(error);
}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1221
/*
1222 1223 1224
  note, that according to the sql standard (ISO/IEC 9075-2:2003)
  section "4.33.4 SQL-statements and transaction states",
  SAVEPOINT is *not* transaction-initiating SQL-statement
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1225 1226
*/

1227
int ha_savepoint(THD *thd, SAVEPOINT *sv)
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1228 1229
{
  int error=0;
1230 1231
  THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction.stmt :
                                        &thd->transaction.all);
1232 1233
  handlerton **ht=trans->ht;
  DBUG_ENTER("ha_savepoint");
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1234
#ifdef USING_TRANSACTIONS
1235
  for (; *ht; ht++)
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1236
  {
1237 1238
    int err;
    if (! (*ht)->savepoint_set)
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1239
    {
1240
      my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "SAVEPOINT");
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1241
      error=1;
1242
      break;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1243
    }
1244 1245 1246
    if ((err= (*(*ht)->savepoint_set)(thd, (byte *)(sv+1)+(*ht)->savepoint_offset)))
    { // cannot happen
      my_error(ER_GET_ERRNO, MYF(0), err);
1247 1248
      error=1;
    }
1249
    statistic_increment(thd->status_var.ha_savepoint_count,&LOCK_status);
1250
  }
1251
  sv->nht=trans->nht;
1252 1253 1254 1255
#endif /* USING_TRANSACTIONS */
  DBUG_RETURN(error);
}

1256
int ha_release_savepoint(THD *thd, SAVEPOINT *sv)
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1257 1258
{
  int error=0;
1259 1260 1261
  THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction.stmt :
                                        &thd->transaction.all);
  handlerton **ht=trans->ht, **end_ht;
1262 1263 1264 1265
  DBUG_ENTER("ha_release_savepoint");

  end_ht=ht+sv->nht;
  for (; ht < end_ht; ht++)
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1266
  {
1267 1268 1269 1270 1271 1272 1273
    int err;
    if (!(*ht)->savepoint_release)
      continue;
    if ((err= (*(*ht)->savepoint_release)(thd, (byte *)(sv+1)+(*ht)->savepoint_offset)))
    { // cannot happen
      my_error(ER_GET_ERRNO, MYF(0), err);
      error=1;
guilhem@mysql.com's avatar
guilhem@mysql.com committed
1274
    }
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1275 1276 1277 1278
  }
  DBUG_RETURN(error);
}

1279

1280 1281 1282
static my_bool snapshot_handlerton(THD *thd, st_plugin_int *plugin,
                                   void *arg)
{
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1283
  handlerton *hton= (handlerton *)plugin->data;
1284 1285 1286 1287 1288 1289 1290 1291 1292
  if (hton->state == SHOW_OPTION_YES &&
      hton->start_consistent_snapshot)
  {
    hton->start_consistent_snapshot(thd);
    *((bool *)arg)= false;
  }
  return FALSE;
}

1293 1294
int ha_start_consistent_snapshot(THD *thd)
{
1295 1296
  bool warn= true;

1297 1298
  plugin_foreach(thd, snapshot_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, &warn);

1299 1300 1301 1302
  /*
    Same idea as when one wants to CREATE TABLE in one engine which does not
    exist:
  */
1303 1304 1305 1306
  if (warn)
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
                 "This MySQL server does not support any "
                 "consistent-read capable storage engine");
1307 1308 1309 1310
  return 0;
}


1311 1312
static my_bool flush_handlerton(THD *thd, st_plugin_int *plugin,
                                void *arg)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1313
{
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1314
  handlerton *hton= (handlerton *)plugin->data;
1315 1316 1317 1318 1319
  if (hton->state == SHOW_OPTION_YES && hton->flush_logs && hton->flush_logs())
    return TRUE;
  return FALSE;
}

1320

1321 1322 1323
bool ha_flush_logs(handlerton *db_type)
{
  if (db_type == NULL)
1324
  {
1325 1326 1327
    if (plugin_foreach(NULL, flush_handlerton,
                          MYSQL_STORAGE_ENGINE_PLUGIN, 0))
      return TRUE;
1328
  }
1329 1330 1331 1332 1333 1334 1335
  else
  {
    if (db_type->state != SHOW_OPTION_YES ||
        (db_type->flush_logs && db_type->flush_logs()))
      return TRUE;
  }
  return FALSE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1336 1337
}

1338 1339 1340 1341
/*
  This should return ENOENT if the file doesn't exists.
  The .frm file will be deleted only if we return 0 or ENOENT
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1342

1343
int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
1344
                    const char *db, const char *alias, bool generate_warning)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1345
{
1346
  handler *file;
1347
  char tmp_path[FN_REFLEN];
1348 1349 1350 1351 1352 1353 1354 1355
  int error;
  TABLE dummy_table;
  TABLE_SHARE dummy_share;
  DBUG_ENTER("ha_delete_table");

  bzero((char*) &dummy_table, sizeof(dummy_table));
  bzero((char*) &dummy_share, sizeof(dummy_share));
  dummy_table.s= &dummy_share;
1356 1357

  /* DB_TYPE_UNKNOWN is used in ALTER TABLE when renaming only .frm files */
1358
  if (table_type == NULL ||
1359
      ! (file=get_new_handler(&dummy_share, thd->mem_root, table_type)))
1360
    DBUG_RETURN(ENOENT);
1361

1362
  if (lower_case_table_names == 2 && !(file->ha_table_flags() & HA_FILE_BASED))
1363 1364 1365
  {
    /* Ensure that table handler get path in lower case */
    strmov(tmp_path, path);
1366
    my_casedn_str(files_charset_info, tmp_path);
1367 1368
    path= tmp_path;
  }
1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391
  if ((error= file->delete_table(path)) && generate_warning)
  {
    /*
      Because file->print_error() use my_error() to generate the error message
      we must store the error state in thd, reset it and restore it to
      be able to get hold of the error message.
      (We should in the future either rewrite handler::print_error() or make
      a nice method of this.
    */
    bool query_error= thd->query_error;
    sp_rcontext *spcont= thd->spcont;
    SELECT_LEX *current_select= thd->lex->current_select;
    char buff[sizeof(thd->net.last_error)];
    char new_error[sizeof(thd->net.last_error)];
    int last_errno= thd->net.last_errno;

    strmake(buff, thd->net.last_error, sizeof(buff)-1);
    thd->query_error= 0;
    thd->spcont= 0;
    thd->lex->current_select= 0;
    thd->net.last_error[0]= 0;

    /* Fill up strucutures that print_error may need */
1392 1393 1394 1395 1396 1397
    dummy_share.path.str= (char*) path;
    dummy_share.path.length= strlen(path);
    dummy_share.db.str= (char*) db;
    dummy_share.db.length= strlen(db);
    dummy_share.table_name.str= (char*) alias;
    dummy_share.table_name.length= strlen(alias);
1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410
    dummy_table.alias= alias;

    file->print_error(error, 0);
    strmake(new_error, thd->net.last_error, sizeof(buff)-1);

    /* restore thd */
    thd->query_error= query_error;
    thd->spcont= spcont;
    thd->lex->current_select= current_select;
    thd->net.last_errno= last_errno;
    strmake(thd->net.last_error, buff, sizeof(buff)-1);
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, error, new_error);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1411
  delete file;
1412
  DBUG_RETURN(error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1413
}
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1414

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1415 1416 1417 1418
/****************************************************************************
** General handler functions
****************************************************************************/

1419 1420 1421 1422 1423 1424

void handler::ha_statistic_increment(ulong SSV::*offset) const
{
  statistic_increment(table->in_use->status_var.*offset, &LOCK_status);
}

1425 1426 1427 1428 1429 1430 1431
/*
  Open database-handler.

  IMPLEMENTATION
    Try O_RDONLY if cannot open as O_RDWR
    Don't wait for locks if not HA_OPEN_WAIT_IF_LOCKED is set
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1432

1433 1434
int handler::ha_open(TABLE *table_arg, const char *name, int mode,
                     int test_if_locked)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1435 1436
{
  int error;
1437
  DBUG_ENTER("handler::ha_open");
1438 1439 1440 1441 1442 1443 1444
  DBUG_PRINT("enter",
             ("name: %s  db_type: %d  db_stat: %d  mode: %d  lock_test: %d",
              name, table_share->db_type, table_arg->db_stat, mode,
              test_if_locked));

  table= table_arg;
  DBUG_ASSERT(table->s == table_share);
1445
  DBUG_ASSERT(alloc_root_inited(&table->mem_root));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457

  if ((error=open(name,mode,test_if_locked)))
  {
    if ((error == EACCES || error == EROFS) && mode == O_RDWR &&
	(table->db_stat & HA_TRY_READ_ONLY))
    {
      table->db_stat|=HA_READ_ONLY;
      error=open(name,O_RDONLY,test_if_locked);
    }
  }
  if (error)
  {
1458
    my_errno= error;                            /* Safeguard */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1459 1460 1461 1462
    DBUG_PRINT("error",("error: %d  errno: %d",error,errno));
  }
  else
  {
1463
    if (table->s->db_options_in_use & HA_OPTION_READ_ONLY_DATA)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1464
      table->db_stat|=HA_READ_ONLY;
1465 1466
    (void) extra(HA_EXTRA_NO_READCHECK);	// Not needed in SQL

1467
    if (!(ref= (byte*) alloc_root(&table->mem_root, ALIGN_SIZE(ref_length)*2)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1468 1469 1470 1471 1472
    {
      close();
      error=HA_ERR_OUT_OF_MEM;
    }
    else
1473 1474
      dup_ref=ref+ALIGN_SIZE(ref_length);
    cached_table_flags= table_flags();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1475 1476 1477 1478
  }
  DBUG_RETURN(error);
}

1479

1480 1481 1482
/*
  Read first row (only) from a table
  This is never called for InnoDB or BDB tables, as these table types
1483
  has the HA_STATS_RECORDS_IS_EXACT set.
1484
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1485

1486
int handler::read_first_row(byte * buf, uint primary_key)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1487 1488
{
  register int error;
1489
  DBUG_ENTER("handler::read_first_row");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1490

1491 1492
  statistic_increment(table->in_use->status_var.ha_read_first_count,
                      &LOCK_status);
1493 1494 1495 1496

  /*
    If there is very few deleted rows in the table, find the first row by
    scanning the table.
1497
    TODO remove the test for HA_READ_ORDER
1498
  */
1499
  if (stats.deleted < 10 || primary_key >= MAX_KEY ||
1500
      !(index_flags(primary_key, 0, 0) & HA_READ_ORDER))
1501
  {
1502
    (void) ha_rnd_init(1);
1503
    while ((error= rnd_next(buf)) == HA_ERR_RECORD_DELETED) ;
1504
    (void) ha_rnd_end();
1505 1506 1507 1508
  }
  else
  {
    /* Find the first row through the primary key */
1509
    (void) ha_index_init(primary_key, 0);
1510
    error=index_first(buf);
1511
    (void) ha_index_end();
1512
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1513 1514 1515
  DBUG_RETURN(error);
}

1516
/*
1517
  Generate the next auto-increment number based on increment and offset
serg@serg.mylan's avatar
serg@serg.mylan committed
1518

1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536
  In most cases increment= offset= 1, in which case we get:
  1,2,3,4,5,...
  If increment=10 and offset=5 and previous number is 1, we get:
  1,5,15,25,35,...
*/

inline ulonglong
next_insert_id(ulonglong nr,struct system_variables *variables)
{
  nr= (((nr+ variables->auto_increment_increment -
         variables->auto_increment_offset)) /
       (ulonglong) variables->auto_increment_increment);
  return (nr* (ulonglong) variables->auto_increment_increment +
          variables->auto_increment_offset);
}


/*
1537 1538 1539
  Update the auto_increment field if necessary

  SYNOPSIS
1540
    update_auto_increment()
1541 1542 1543 1544 1545 1546 1547 1548 1549

  RETURN
    0	ok
    1 	get_auto_increment() was called and returned ~(ulonglong) 0
    

  IMPLEMENTATION

    Updates columns with type NEXT_NUMBER if:
1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568

  - If column value is set to NULL (in which case
    auto_increment_field_not_null is 0)
  - If column is set to 0 and (sql_mode & MODE_NO_AUTO_VALUE_ON_ZERO) is not
    set. In the future we will only set NEXT_NUMBER fields if one sets them
    to NULL (or they are not included in the insert list).


  There are two different cases when the above is true:

  - thd->next_insert_id == 0  (This is the normal case)
    In this case we set the set the column for the first row to the value
    next_insert_id(get_auto_increment(column))) which is normally
    max-used-column-value +1.

    We call get_auto_increment() only for the first row in a multi-row
    statement. For the following rows we generate new numbers based on the
    last used number.

1569 1570 1571
  - thd->next_insert_id != 0.  This happens when we have read an Intvar event
    of type INSERT_ID_EVENT from the binary log or when one has used SET
    INSERT_ID=#.
1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586

    In this case we will set the column to the value of next_insert_id.
    The next row will be given the id
    next_insert_id(next_insert_id)

    The idea is that generated auto_increment values are predictable and
    independent of the column values in the table.  This is needed to be
    able to replicate into a table that already has rows with a higher
    auto-increment value than the one that is inserted.

    After we have already generated an auto-increment number and the user
    inserts a column with a higher value than the last used one, we will
    start counting from the inserted value.

    thd->next_insert_id is cleared after it's been used for a statement.
1587 1588 1589 1590 1591 1592 1593 1594

   TODO

    Replace all references to "next number" or NEXT_NUMBER to
    "auto_increment", everywhere (see below: there is
    table->auto_increment_field_not_null, and there also exists
    table->next_number_field, it's not consistent).

1595
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1596

1597 1598 1599 1600
#define AUTO_INC_DEFAULT_NB_ROWS 1 // Some prefer 1024 here
#define AUTO_INC_DEFAULT_NB_MAX_BITS 16
#define AUTO_INC_DEFAULT_NB_MAX ((1 << AUTO_INC_DEFAULT_NB_MAX_BITS) - 1)

1601
bool handler::update_auto_increment()
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1602
{
1603 1604 1605
  ulonglong nr;
  THD *thd= table->in_use;
  struct system_variables *variables= &thd->variables;
monty@mysql.com's avatar
monty@mysql.com committed
1606
  bool auto_increment_field_not_null;
1607
  bool result= 0;
1608
  DBUG_ENTER("handler::update_auto_increment");
1609 1610 1611 1612 1613 1614

  /*
    We must save the previous value to be able to restore it if the
    row was not inserted
  */
  thd->prev_insert_id= thd->next_insert_id;
monty@mysql.com's avatar
monty@mysql.com committed
1615
  auto_increment_field_not_null= table->auto_increment_field_not_null;
1616
  table->auto_increment_field_not_null= FALSE; // to reset for next row
1617 1618

  if ((nr= table->next_number_field->val_int()) != 0 ||
monty@mysql.com's avatar
monty@mysql.com committed
1619
      auto_increment_field_not_null &&
1620
      thd->variables.sql_mode & MODE_NO_AUTO_VALUE_ON_ZERO)
1621
  {
1622 1623 1624 1625
    /*
      The user did specify a value for the auto_inc column, we don't generate
      a new value, write it down.
    */
1626
    auto_increment_column_changed=0;
1627

1628 1629 1630 1631 1632 1633
    /*
      Update next_insert_id if we had already generated a value in this
      statement (case of INSERT VALUES(null),(3763),(null):
      the last NULL needs to insert 3764, not the value of the first NULL plus
      1).
    */
1634 1635 1636 1637 1638 1639 1640 1641 1642
    if (thd->clear_next_insert_id && nr >= thd->next_insert_id)
    {
      if (variables->auto_increment_increment != 1)
        nr= next_insert_id(nr, variables);
      else
        nr++;
      thd->next_insert_id= nr;
      DBUG_PRINT("info",("next_insert_id: %lu", (ulong) nr));
    }
1643
    DBUG_RETURN(0);
1644
  }
1645 1646
  if (!(nr= thd->next_insert_id))
  {
1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687
    ulonglong nb_desired_values= 1, nb_reserved_values;
#ifdef TO_BE_ENABLED_SOON
    /*
      Reserved intervals will be stored in "THD::auto_inc_intervals".
      handler::estimation_rows_to_insert will be the argument passed by
      handler::ha_start_bulk_insert().
    */
    uint estimation_known= test(estimation_rows_to_insert > 0);
    uint nb_already_reserved_intervals= thd->auto_inc_intervals.nb_elements();
    /*
      If an estimation was given to the engine:
      - use it.
      - if we already reserved numbers, it means the estimation was
        not accurate, then we'll reserve 2*AUTO_INC_DEFAULT_NB_VALUES the 2nd
        time, twice that the 3rd time etc.
      If no estimation was given, use those increasing defaults from the
      start, starting from AUTO_INC_DEFAULT_NB_VALUES.
      Don't go beyond a max to not reserve "way too much" (because reservation
      means potentially losing unused values).
    */
    if (nb_already_reserved_intervals == 0 && estimation_known)
      nb_desired_values= estimation_rows_to_insert;
    else /* go with the increasing defaults */
    {
      /* avoid overflow in formula, with this if() */
      if (nb_already_reserved_intervals <= AUTO_INC_DEFAULT_NB_MAX_BITS)
      {
        nb_desired_values= AUTO_INC_DEFAULT_NB_VALUES * 
          (1 << nb_already_reserved_intervals);
        set_if_smaller(nb_desired_values, AUTO_INC_DEFAULT_NB_MAX);
      }
      else
        nb_desired_values= AUTO_INC_DEFAULT_NB_MAX;
    }
#endif
    /* This call ignores all its parameters but nr, currently */
    get_auto_increment(variables->auto_increment_offset,
                       variables->auto_increment_increment,
                       nb_desired_values, &nr,
                       &nb_reserved_values);
    if (nr == ~(ulonglong) 0)
1688 1689
      result= 1;                                // Mark failure

1690 1691 1692 1693
    /*
      That should not be needed when engines actually use offset and increment
      above.
    */
1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708
    if (variables->auto_increment_increment != 1)
      nr= next_insert_id(nr-1, variables);
    /*
      Update next row based on the found value. This way we don't have to
      call the handler for every generated auto-increment value on a
      multi-row statement
    */
    thd->next_insert_id= nr;
  }

  DBUG_PRINT("info",("auto_increment: %lu", (ulong) nr));

  /* Mark that we should clear next_insert_id before next stmt */
  thd->clear_next_insert_id= 1;

1709
  if (!table->next_number_field->store((longlong) nr, TRUE))
1710
    thd->insert_id((ulonglong) nr);
1711 1712
  else
    thd->insert_id(table->next_number_field->val_int());
1713 1714 1715 1716 1717 1718

  /*
    We can't set next_insert_id if the auto-increment key is not the
    first key part, as there is no guarantee that the first parts will be in
    sequence
  */
1719
  if (!table->s->next_number_key_offset)
1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731
  {
    /*
      Set next insert id to point to next auto-increment value to be able to
      handle multi-row statements
      This works even if auto_increment_increment > 1
    */
    thd->next_insert_id= next_insert_id(nr, variables);
  }
  else
    thd->next_insert_id= 0;

  /* Mark that we generated a new value */
1732
  auto_increment_column_changed=1;
1733
  DBUG_RETURN(result);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1734 1735
}

1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749
/*
  restore_auto_increment

  In case of error on write, we restore the last used next_insert_id value
  because the previous value was not used.
*/

void handler::restore_auto_increment()
{
  THD *thd= table->in_use;
  if (thd->next_insert_id)
    thd->next_insert_id= thd->prev_insert_id;
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1750

1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773
/*
  MySQL signal that it changed the column bitmap

  USAGE
    This is for handlers that needs to setup their own column bitmaps.
    Normally the handler should set up their own column bitmaps in
    index_init() or rnd_init() and in any column_bitmaps_signal() call after
    this.

    The handler is allowd to do changes to the bitmap after a index_init or
    rnd_init() call is made as after this, MySQL will not use the bitmap
    for any program logic checking.
*/

void handler::column_bitmaps_signal()
{
  DBUG_ENTER("column_bitmaps_signal");
  DBUG_PRINT("info", ("read_set: 0x%lx  write_set: 0x%lx", table->read_set,
                      table->write_set));
  DBUG_VOID_RETURN;
}


1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795
/*
  Reserves an interval of auto_increment values from the handler.

  SYNOPSIS
    get_auto_increment()
    offset              
    increment
    nb_desired_values   how many values we want
    first_value         (OUT) the first value reserved by the handler
    nb_reserved_values  (OUT) how many values the handler reserved

  offset and increment means that we want values to be of the form
  offset + N * increment, where N>=0 is integer.
  If the function sets *first_value to ~(ulonglong)0 it means an error.
  If the function sets *nb_reserved_values to ULONGLONG_MAX it means it has
  reserved to "positive infinite".
*/

void handler::get_auto_increment(ulonglong offset, ulonglong increment,
                                 ulonglong nb_desired_values,
                                 ulonglong *first_value,
                                 ulonglong *nb_reserved_values)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1796
{
1797
  ulonglong nr;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1798
  int error;
1799

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1800
  (void) extra(HA_EXTRA_KEYREAD);
1801 1802 1803
  table->mark_columns_used_by_index_no_reset(table->s->next_number_index,
                                        table->read_set);
  column_bitmaps_signal();
1804
  index_init(table->s->next_number_index, 1);
1805
  if (!table->s->next_number_key_offset)
1806 1807
  {						// Autoincrement at key-start
    error=index_last(table->record[1]);
1808 1809 1810 1811 1812 1813
    /*
      MySQL implicitely assumes such method does locking (as MySQL decides to
      use nr+increment without checking again with the handler, in
      handler::update_auto_increment()), so reserves to infinite.
    */
    *nb_reserved_values= ULONGLONG_MAX;
1814 1815 1816 1817
  }
  else
  {
    byte key[MAX_KEY_LENGTH];
1818
    key_copy(key, table->record[0],
1819 1820 1821 1822
             table->key_info + table->s->next_number_index,
             table->s->next_number_key_offset);
    error= index_read(table->record[1], key, table->s->next_number_key_offset,
                      HA_READ_PREFIX_LAST);
1823 1824 1825 1826 1827 1828 1829
    /*
      MySQL needs to call us for next row: assume we are inserting ("a",null)
      here, we return 3, and next this statement will want to insert
      ("b",null): there is no reason why ("b",3+1) would be the good row to
      insert: maybe it already exists, maybe 3+1 is too large...
    */
    *nb_reserved_values= 1;
1830 1831
  }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1832 1833 1834
  if (error)
    nr=1;
  else
1835 1836
    nr= ((ulonglong) table->next_number_field->
         val_int_offset(table->s->rec_buff_length)+1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1837
  index_end();
1838
  (void) extra(HA_EXTRA_NO_KEYREAD);
1839
  *first_value= nr;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1840 1841
}

1842

1843
void handler::print_keydup_error(uint key_nr, const char *msg)
1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860
{
  /* Write the duplicated key in the error message */
  char key[MAX_KEY_LENGTH];
  String str(key,sizeof(key),system_charset_info);
  /* Table is opened and defined at this point */
  key_unpack(&str,table,(uint) key_nr);
  uint max_length=MYSQL_ERRMSG_SIZE-(uint) strlen(msg);
  if (str.length() >= max_length)
  {
    str.length(max_length-4);
    str.append(STRING_WITH_LEN("..."));
  }
  my_printf_error(ER_DUP_ENTRY, msg,
                  MYF(0), str.c_ptr(), table->key_info[key_nr].name);
}


1861 1862 1863
/*
  Print error that we got from handler function

1864
  NOTE
1865 1866 1867 1868 1869
   In case of delete table it's only safe to use the following parts of
   the 'table' structure:
     table->s->path
     table->alias
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1870 1871 1872

void handler::print_error(int error, myf errflag)
{
1873
  DBUG_ENTER("handler::print_error");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1874 1875 1876 1877
  DBUG_PRINT("enter",("error: %d",error));

  int textno=ER_GET_ERRNO;
  switch (error) {
1878 1879 1880
  case EACCES:
    textno=ER_OPEN_AS_READONLY;
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891
  case EAGAIN:
    textno=ER_FILE_USED;
    break;
  case ENOENT:
    textno=ER_FILE_NOT_FOUND;
    break;
  case HA_ERR_KEY_NOT_FOUND:
  case HA_ERR_NO_ACTIVE_RECORD:
  case HA_ERR_END_OF_FILE:
    textno=ER_KEY_NOT_FOUND;
    break;
1892
  case HA_ERR_WRONG_MRG_TABLE_DEF:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1893 1894 1895 1896 1897 1898 1899
    textno=ER_WRONG_MRG_TABLE;
    break;
  case HA_ERR_FOUND_DUPP_KEY:
  {
    uint key_nr=get_dup_key(error);
    if ((int) key_nr >= 0)
    {
1900
      print_keydup_error(key_nr, ER(ER_DUP_ENTRY));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1901 1902 1903 1904 1905
      DBUG_VOID_RETURN;
    }
    textno=ER_DUP_KEY;
    break;
  }
1906 1907 1908 1909 1910
  case HA_ERR_FOREIGN_DUPLICATE_KEY:
  {
    uint key_nr= get_dup_key(error);
    if ((int) key_nr >= 0)
    {
1911
      uint max_length;
1912 1913 1914 1915 1916
      /* Write the key in the error message */
      char key[MAX_KEY_LENGTH];
      String str(key,sizeof(key),system_charset_info);
      /* Table is opened and defined at this point */
      key_unpack(&str,table,(uint) key_nr);
1917 1918
      max_length= (MYSQL_ERRMSG_SIZE-
                   (uint) strlen(ER(ER_FOREIGN_DUPLICATE_KEY)));
1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930
      if (str.length() >= max_length)
      {
        str.length(max_length-4);
        str.append(STRING_WITH_LEN("..."));
      }
      my_error(ER_FOREIGN_DUPLICATE_KEY, MYF(0), table_share->table_name.str,
        str.c_ptr(), key_nr+1);
      DBUG_VOID_RETURN;
    }
    textno= ER_DUP_KEY;
    break;
  }
1931 1932
  case HA_ERR_NULL_IN_SPATIAL:
    textno= ER_UNKNOWN_ERROR;
1933
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1934 1935 1936 1937 1938 1939 1940 1941 1942
  case HA_ERR_FOUND_DUPP_UNIQUE:
    textno=ER_DUP_UNIQUE;
    break;
  case HA_ERR_RECORD_CHANGED:
    textno=ER_CHECKREAD;
    break;
  case HA_ERR_CRASHED:
    textno=ER_NOT_KEYFILE;
    break;
1943 1944 1945
  case HA_ERR_WRONG_IN_RECORD:
    textno= ER_CRASHED_ON_USAGE;
    break;
1946 1947 1948
  case HA_ERR_CRASHED_ON_USAGE:
    textno=ER_CRASHED_ON_USAGE;
    break;
1949 1950 1951
  case HA_ERR_NOT_A_TABLE:
    textno= error;
    break;
1952 1953 1954
  case HA_ERR_CRASHED_ON_REPAIR:
    textno=ER_CRASHED_ON_REPAIR;
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1955
  case HA_ERR_OUT_OF_MEM:
1956 1957
    textno=ER_OUT_OF_RESOURCES;
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1958 1959 1960 1961 1962 1963 1964 1965 1966 1967
  case HA_ERR_WRONG_COMMAND:
    textno=ER_ILLEGAL_HA;
    break;
  case HA_ERR_OLD_FILE:
    textno=ER_OLD_KEYFILE;
    break;
  case HA_ERR_UNSUPPORTED:
    textno=ER_UNSUPPORTED_EXTENSION;
    break;
  case HA_ERR_RECORD_FILE_FULL:
1968
  case HA_ERR_INDEX_FILE_FULL:
1969
    textno=ER_RECORD_FILE_FULL;
1970
    break;
1971 1972 1973 1974 1975 1976
  case HA_ERR_LOCK_WAIT_TIMEOUT:
    textno=ER_LOCK_WAIT_TIMEOUT;
    break;
  case HA_ERR_LOCK_TABLE_FULL:
    textno=ER_LOCK_TABLE_FULL;
    break;
1977 1978 1979
  case HA_ERR_LOCK_DEADLOCK:
    textno=ER_LOCK_DEADLOCK;
    break;
1980 1981 1982
  case HA_ERR_READ_ONLY_TRANSACTION:
    textno=ER_READ_ONLY_TRANSACTION;
    break;
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
1983 1984 1985 1986
  case HA_ERR_CANNOT_ADD_FOREIGN:
    textno=ER_CANNOT_ADD_FOREIGN;
    break;
  case HA_ERR_ROW_IS_REFERENCED:
1987 1988 1989 1990 1991 1992
  {
    String str;
    get_error_message(error, &str);
    my_error(ER_ROW_IS_REFERENCED_2, MYF(0), str.c_ptr_safe());
    DBUG_VOID_RETURN;
  }
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
1993
  case HA_ERR_NO_REFERENCED_ROW:
1994 1995 1996 1997 1998 1999
  {
    String str;
    get_error_message(error, &str);
    my_error(ER_NO_REFERENCED_ROW_2, MYF(0), str.c_ptr_safe());
    DBUG_VOID_RETURN;
  }
2000 2001 2002
  case HA_ERR_TABLE_DEF_CHANGED:
    textno=ER_TABLE_DEF_CHANGED;
    break;
2003
  case HA_ERR_NO_SUCH_TABLE:
2004 2005
    my_error(ER_NO_SUCH_TABLE, MYF(0), table_share->db.str,
             table_share->table_name.str);
2006
    break;
2007 2008 2009
  case HA_ERR_RBR_LOGGING_FAILED:
    textno= ER_BINLOG_ROW_LOGGING_FAILED;
    break;
2010 2011 2012 2013 2014 2015 2016 2017 2018
  case HA_ERR_DROP_INDEX_FK:
  {
    const char *ptr= "???";
    uint key_nr= get_dup_key(error);
    if ((int) key_nr >= 0)
      ptr= table->key_info[key_nr].name;
    my_error(ER_DROP_INDEX_FK, MYF(0), ptr);
    DBUG_VOID_RETURN;
  }
2019 2020 2021
  case HA_ERR_TABLE_NEEDS_UPGRADE:
    textno=ER_TABLE_NEEDS_UPGRADE;
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2022 2023
  default:
    {
2024 2025 2026
      /* The error was "unknown" to this function.
	 Ask handler if it has got a message for this error */
      bool temporary= FALSE;
2027 2028 2029
      String str;
      temporary= get_error_message(error, &str);
      if (!str.is_empty())
2030
      {
2031
	const char* engine= table_type();
2032
	if (temporary)
2033
	  my_error(ER_GET_TEMPORARY_ERRMSG, MYF(0), error, str.ptr(), engine);
2034
	else
2035
	  my_error(ER_GET_ERRMSG, MYF(0), error, str.ptr(), engine);
2036
      }
2037
      else
2038
	my_error(ER_GET_ERRNO,errflag,error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2039 2040 2041
      DBUG_VOID_RETURN;
    }
  }
2042
  my_error(textno, errflag, table_share->table_name.str, error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2043 2044 2045
  DBUG_VOID_RETURN;
}

2046

2047
/*
2048
   Return an error message specific to this handler
2049

2050
   SYNOPSIS
2051 2052
   error        error code previously returned by handler
   buf          Pointer to String where to add error message
serg@serg.mylan's avatar
serg@serg.mylan committed
2053

2054
   Returns true if this is a temporary error
2055 2056
 */

2057
bool handler::get_error_message(int error, String* buf)
2058
{
2059
  return FALSE;
2060 2061 2062
}


2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123
int handler::ha_check_for_upgrade(HA_CHECK_OPT *check_opt)
{
  KEY *keyinfo, *keyend;
  KEY_PART_INFO *keypart, *keypartend;

  if (!table->s->mysql_version)
  {
    /* check for blob-in-key error */
    keyinfo= table->key_info;
    keyend= table->key_info + table->s->keys;
    for (; keyinfo < keyend; keyinfo++)
    {
      keypart= keyinfo->key_part;
      keypartend= keypart + keyinfo->key_parts;
      for (; keypart < keypartend; keypart++)
      {
        if (!keypart->fieldnr)
          continue;
        Field *field= table->field[keypart->fieldnr-1];
        if (field->type() == FIELD_TYPE_BLOB)
        {
          if (check_opt->sql_flags & TT_FOR_UPGRADE)
            check_opt->flags= T_MEDIUM;
          return HA_ADMIN_NEEDS_CHECK;
        }
      }
    }
  }
  return check_for_upgrade(check_opt);
}


int handler::check_old_types()
{
  Field** field;

  if (!table->s->mysql_version)
  {
    /* check for bad DECIMAL field */
    for (field= table->field; (*field); field++)
    {
      if ((*field)->type() == FIELD_TYPE_NEWDECIMAL)
      {
        return HA_ADMIN_NEEDS_ALTER;
      }
    }
  }
  return 0;
}


static bool update_frm_version(TABLE *table, bool needs_lock)
{
  char path[FN_REFLEN];
  File file;
  int result= 1;
  DBUG_ENTER("update_frm_version");

  if (table->s->mysql_version != MYSQL_VERSION_ID)
    DBUG_RETURN(0);

2124
  strxmov(path, table->s->normalized_path.str, reg_ext, NullS);
2125 2126 2127 2128 2129 2130 2131

  if (needs_lock)
    pthread_mutex_lock(&LOCK_open);

  if ((file= my_open(path, O_RDWR|O_BINARY, MYF(MY_WME))) >= 0)
  {
    uchar version[4];
2132 2133
    char *key= table->s->table_cache_key.str;
    uint key_length= table->s->table_cache_key.length;
2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156
    TABLE *entry;
    HASH_SEARCH_STATE state;

    int4store(version, MYSQL_VERSION_ID);

    if ((result= my_pwrite(file,(byte*) version,4,51L,MYF_RW)))
      goto err;

    for (entry=(TABLE*) hash_first(&open_cache,(byte*) key,key_length, &state);
         entry;
         entry= (TABLE*) hash_next(&open_cache,(byte*) key,key_length, &state))
      entry->s->mysql_version= MYSQL_VERSION_ID;
  }
err:
  if (file >= 0)
    VOID(my_close(file,MYF(MY_WME)));
  if (needs_lock)
    pthread_mutex_unlock(&LOCK_open);
  DBUG_RETURN(result);
}



2157
/* Return key if error because of duplicated keys */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2158 2159 2160

uint handler::get_dup_key(int error)
{
2161
  DBUG_ENTER("handler::get_dup_key");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2162
  table->file->errkey  = (uint) -1;
2163 2164 2165
  if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOREIGN_DUPLICATE_KEY ||
      error == HA_ERR_FOUND_DUPP_UNIQUE || error == HA_ERR_NULL_IN_SPATIAL ||
      error == HA_ERR_DROP_INDEX_FK)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2166 2167 2168 2169
    info(HA_STATUS_ERRKEY | HA_STATUS_NO_LOCK);
  DBUG_RETURN(table->file->errkey);
}

2170

2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183
/*
  Delete all files with extension from bas_ext()

  SYNOPSIS
    delete_table()
    name		Base name of table

  NOTES
    We assume that the handler may return more extensions than
    was actually used for the file.

  RETURN
    0   If we successfully deleted at least one file from base_ext and
serg@serg.mylan's avatar
serg@serg.mylan committed
2184
	didn't get any other errors than ENOENT
2185
    #   Error
2186 2187
*/

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2188 2189
int handler::delete_table(const char *name)
{
2190 2191
  int error= 0;
  int enoent_or_zero= ENOENT;                   // Error if no file was deleted
2192
  char buff[FN_REFLEN];
2193

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2194 2195
  for (const char **ext=bas_ext(); *ext ; ext++)
  {
2196
    fn_format(buff, name, "", *ext, MY_UNPACK_FILENAME|MY_APPEND_EXT);
2197
    if (my_delete_with_symlink(buff, MYF(0)))
2198
    {
2199
      if ((error= my_errno) != ENOENT)
2200 2201
	break;
    }
2202
    else
2203
      enoent_or_zero= 0;                        // No error for ENOENT
2204
    error= enoent_or_zero;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2205
  }
2206
  return error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2207 2208 2209 2210 2211
}


int handler::rename_table(const char * from, const char * to)
{
2212 2213
  int error= 0;
  for (const char **ext= bas_ext(); *ext ; ext++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2214
  {
2215 2216 2217 2218 2219 2220
    if (rename_file_ext(from, to, *ext))
    {
      if ((error=my_errno) != ENOENT)
	break;
      error= 0;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2221
  }
2222
  return error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2223 2224
}

2225 2226 2227 2228 2229 2230 2231 2232

void handler::drop_table(const char *name)
{
  close();
  delete_table(name);
}


2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282
/*
   Performs checks upon the table.

   SYNOPSIS
   check()
   thd                thread doing CHECK TABLE operation
   check_opt          options from the parser

   NOTES

   RETURN
   HA_ADMIN_OK                 Successful upgrade
   HA_ADMIN_NEEDS_UPGRADE      Table has structures requiring upgrade
   HA_ADMIN_NEEDS_ALTER        Table has structures requiring ALTER TABLE
   HA_ADMIN_NOT_IMPLEMENTED
*/

int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt)
{
  int error;

  if ((table->s->mysql_version >= MYSQL_VERSION_ID) &&
      (check_opt->sql_flags & TT_FOR_UPGRADE))
    return 0;

  if (table->s->mysql_version < MYSQL_VERSION_ID)
  {
    if ((error= check_old_types()))
      return error;
    error= ha_check_for_upgrade(check_opt);
    if (error && (error != HA_ADMIN_NEEDS_CHECK))
      return error;
    if (!error && (check_opt->sql_flags & TT_FOR_UPGRADE))
      return 0;
  }
  if ((error= check(thd, check_opt)))
    return error;
  return update_frm_version(table, 0);
}


int handler::ha_repair(THD* thd, HA_CHECK_OPT* check_opt)
{
  int result;
  if ((result= repair(thd, check_opt)))
    return result;
  return update_frm_version(table, 0);
}


2283
/*
2284 2285 2286 2287 2288 2289
  Tell the storage engine that it is allowed to "disable transaction" in the
  handler. It is a hint that ACID is not required - it is used in NDB for
  ALTER TABLE, for example, when data are copied to temporary table.
  A storage engine may treat this hint any way it likes. NDB for example
  starts to commit every now and then automatically.
  This hint can be safely ignored.
2290
*/
2291

2292
int ha_enable_transaction(THD *thd, bool on)
2293 2294 2295
{
  int error=0;

2296 2297
  DBUG_ENTER("ha_enable_transaction");
  thd->transaction.on= on;
serg@serg.mylan's avatar
serg@serg.mylan committed
2298
  if (on)
2299 2300 2301 2302 2303 2304 2305
  {
    /*
      Now all storage engines should have transaction handling enabled.
      But some may have it enabled all the time - "disabling" transactions
      is an optimization hint that storage engine is free to ignore.
      So, let's commit an open transaction (if any) now.
    */
2306 2307
    if (!(error= ha_commit_stmt(thd)))
      error= end_trans(thd, COMMIT);
2308
  }
2309 2310 2311
  DBUG_RETURN(error);
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2312 2313 2314 2315 2316
int handler::index_next_same(byte *buf, const byte *key, uint keylen)
{
  int error;
  if (!(error=index_next(buf)))
  {
2317
    if (key_cmp_if_same(table, key, active_index, keylen))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2318 2319 2320 2321 2322 2323 2324 2325 2326
    {
      table->status=STATUS_NOT_FOUND;
      error=HA_ERR_END_OF_FILE;
    }
  }
  return error;
}


2327 2328
void handler::get_dynamic_partition_info(PARTITION_INFO *stat_info,
                                         uint part_id)
2329 2330 2331
{
  info(HA_STATUS_CONST | HA_STATUS_TIME | HA_STATUS_VARIABLE |
       HA_STATUS_NO_LOCK);
2332 2333 2334 2335 2336 2337 2338 2339 2340 2341
  stat_info->records=              stats.records;
  stat_info->mean_rec_length=      stats.mean_rec_length;
  stat_info->data_file_length=     stats.data_file_length;
  stat_info->max_data_file_length= stats.max_data_file_length;
  stat_info->index_file_length=    stats.index_file_length;
  stat_info->delete_length=        stats.delete_length;
  stat_info->create_time=          stats.create_time;
  stat_info->update_time=          stats.update_time;
  stat_info->check_time=           stats.check_time;
  stat_info->check_sum=            0;
2342
  if (table_flags() & (ulong) HA_HAS_CHECKSUM)
2343
    stat_info->check_sum= checksum();
2344 2345 2346 2347
  return;
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
2348 2349 2350 2351
/****************************************************************************
** Some general functions that isn't in the handler class
****************************************************************************/

2352
/*
2353
  Initiates table-file and calls appropriate database-creator
2354 2355 2356

  NOTES
    We must have a write lock on LOCK_open to be sure no other thread
2357
    interferes with table
2358 2359 2360 2361
    
  RETURN
   0  ok
   1  error
2362
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2363

2364 2365 2366
int ha_create_table(THD *thd, const char *path,
                    const char *db, const char *table_name,
                    HA_CREATE_INFO *create_info,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2367 2368
		    bool update_create_info)
{
2369
  int error= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2370
  TABLE table;
2371
  char name_buff[FN_REFLEN];
2372 2373
  const char *name;
  TABLE_SHARE share;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2374
  DBUG_ENTER("ha_create_table");
2375 2376 2377
  
  init_tmp_table_share(&share, db, 0, table_name, path);
  if (open_table_def(thd, &share, 0) ||
2378 2379
      open_table_from_share(thd, &share, "", 0, (uint) READ_ALL, 0, &table,
                            TRUE))
2380
    goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2381 2382 2383

  if (update_create_info)
    update_create_info_from_table(create_info, &table);
2384 2385

  name= share.path.str;
2386
  if (lower_case_table_names == 2 &&
2387
      !(table.file->ha_table_flags() & HA_FILE_BASED))
2388 2389 2390
  {
    /* Ensure that handler gets name in lower case */
    strmov(name_buff, name);
2391
    my_casedn_str(files_charset_info, name_buff);
2392 2393 2394
    name= name_buff;
  }

2395 2396
  error= table.file->create(name, &table, create_info);
  VOID(closefrm(&table, 0));
2397
  if (error)
2398 2399 2400 2401 2402 2403
  {
    strxmov(name_buff, db, ".", table_name, NullS);
    my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), name_buff, error);
  }
err:
  free_table_share(&share);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2404 2405 2406
  DBUG_RETURN(error != 0);
}

2407
/*
2408 2409 2410 2411
  Try to discover table from engine

  NOTES
    If found, write the frm file to disk.
2412

2413
  RETURN VALUES:
2414 2415 2416
  -1    Table did not exists
   0    Table created ok
   > 0  Error, table existed but could not be created
2417 2418 2419

*/

2420
int ha_create_table_from_engine(THD* thd, const char *db, const char *name)
2421
{
2422 2423 2424
  int error;
  const void *frmblob;
  uint frmlen;
2425 2426 2427
  char path[FN_REFLEN];
  HA_CREATE_INFO create_info;
  TABLE table;
2428
  TABLE_SHARE share;
2429
  DBUG_ENTER("ha_create_table_from_engine");
monty@mishka.local's avatar
monty@mishka.local committed
2430
  DBUG_PRINT("enter", ("name '%s'.'%s'", db, name));
2431 2432 2433

  bzero((char*) &create_info,sizeof(create_info));
  if ((error= ha_discover(thd, db, name, &frmblob, &frmlen)))
2434
  {
monty@mishka.local's avatar
monty@mishka.local committed
2435
    /* Table could not be discovered and thus not created */
2436
    DBUG_RETURN(error);
2437 2438
  }

2439
  /*
2440 2441
    Table exists in handler and could be discovered
    frmblob and frmlen are set, write the frm to disk
2442
  */
2443

2444
  (void)strxnmov(path,FN_REFLEN-1,mysql_data_home,"/",db,"/",name,NullS);
2445
  // Save the frm file
monty@mishka.local's avatar
monty@mishka.local committed
2446 2447 2448
  error= writefrm(path, frmblob, frmlen);
  my_free((char*) frmblob, MYF(0));
  if (error)
2449
    DBUG_RETURN(2);
2450

2451 2452 2453 2454 2455
  init_tmp_table_share(&share, db, 0, name, path);
  if (open_table_def(thd, &share, 0))
  {
    DBUG_RETURN(3);
  }
2456
  if (open_table_from_share(thd, &share, "" ,0, 0, 0, &table, FALSE))
2457 2458
  {
    free_table_share(&share);
2459
    DBUG_RETURN(3);
2460
  }
2461

2462
  update_create_info_from_table(&create_info, &table);
2463
  create_info.table_options|= HA_OPTION_CREATE_FROM_ENGINE;
2464

2465
  if (lower_case_table_names == 2 &&
2466
      !(table.file->ha_table_flags() & HA_FILE_BASED))
2467 2468 2469
  {
    /* Ensure that handler gets name in lower case */
    my_casedn_str(files_charset_info, path);
2470
  }
2471
  error=table.file->create(path,&table,&create_info);
2472
  VOID(closefrm(&table, 1));
2473

2474
  DBUG_RETURN(error != 0);
2475 2476
}

2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494
void st_ha_check_opt::init()
{
  flags= sql_flags= 0;
  sort_buffer_size = current_thd->variables.myisam_sort_buff_size;
}


/*****************************************************************************
  Key cache handling.

  This code is only relevant for ISAM/MyISAM tables

  key_cache->cache may be 0 only in the case where a key cache is not
  initialized or when we where not able to init the key cache in a previous
  call to ha_init_key_cache() (probably out of memory)
*****************************************************************************/

/* Init a key cache if it has not been initied before */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2495

2496

2497
int ha_init_key_cache(const char *name, KEY_CACHE *key_cache)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2498
{
2499 2500
  DBUG_ENTER("ha_init_key_cache");

2501
  if (!key_cache->key_cache_inited)
2502 2503
  {
    pthread_mutex_lock(&LOCK_global_system_variables);
2504 2505 2506 2507
    long tmp_buff_size= (long) key_cache->param_buff_size;
    long tmp_block_size= (long) key_cache->param_block_size;
    uint division_limit= key_cache->param_division_limit;
    uint age_threshold=  key_cache->param_age_threshold;
2508
    pthread_mutex_unlock(&LOCK_global_system_variables);
2509
    DBUG_RETURN(!init_key_cache(key_cache,
2510 2511
				tmp_block_size,
				tmp_buff_size,
2512
				division_limit, age_threshold));
2513
  }
2514
  DBUG_RETURN(0);
2515 2516
}

2517 2518 2519

/* Resize key cache */

2520
int ha_resize_key_cache(KEY_CACHE *key_cache)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2521
{
2522 2523
  DBUG_ENTER("ha_resize_key_cache");

2524
  if (key_cache->key_cache_inited)
2525 2526
  {
    pthread_mutex_lock(&LOCK_global_system_variables);
2527 2528 2529 2530
    long tmp_buff_size= (long) key_cache->param_buff_size;
    long tmp_block_size= (long) key_cache->param_block_size;
    uint division_limit= key_cache->param_division_limit;
    uint age_threshold=  key_cache->param_age_threshold;
2531
    pthread_mutex_unlock(&LOCK_global_system_variables);
2532 2533 2534
    DBUG_RETURN(!resize_key_cache(key_cache, tmp_block_size,
				  tmp_buff_size,
				  division_limit, age_threshold));
2535
  }
2536
  DBUG_RETURN(0);
2537 2538
}

2539 2540 2541

/* Change parameters for key cache (like size) */

2542
int ha_change_key_cache_param(KEY_CACHE *key_cache)
2543
{
2544 2545 2546 2547 2548 2549 2550 2551
  if (key_cache->key_cache_inited)
  {
    pthread_mutex_lock(&LOCK_global_system_variables);
    uint division_limit= key_cache->param_division_limit;
    uint age_threshold=  key_cache->param_age_threshold;
    pthread_mutex_unlock(&LOCK_global_system_variables);
    change_key_cache_param(key_cache, division_limit, age_threshold);
  }
2552 2553
  return 0;
}
2554

2555 2556
/* Free memory allocated by a key cache */

2557
int ha_end_key_cache(KEY_CACHE *key_cache)
2558
{
2559
  end_key_cache(key_cache, 1);		// Can never fail
2560
  return 0;
2561
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2562

2563
/* Move all tables from one key cache to another one */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2564

2565 2566
int ha_change_key_cache(KEY_CACHE *old_key_cache,
			KEY_CACHE *new_key_cache)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2567
{
2568 2569
  mi_change_key_cache(old_key_cache, new_key_cache);
  return 0;
2570
}
2571 2572


2573 2574
/*
  Try to discover one table from handler(s)
2575 2576

  RETURN
2577 2578 2579
   -1  : Table did not exists
    0  : OK. In this case *frmblob and *frmlen are set
    >0 : error.  frmblob and frmlen may not be set
2580 2581
*/

2582 2583
int ha_discover(THD *thd, const char *db, const char *name,
		const void **frmblob, uint *frmlen)
2584
{
2585
  int error= -1; // Table does not exist in any handler
2586
  DBUG_ENTER("ha_discover");
2587
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
2588 2589
  if (is_prefix(name,tmp_file_prefix)) /* skip temporary tables */
    DBUG_RETURN(error);
2590
#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
2591
  if (have_ndbcluster == SHOW_OPTION_YES)
2592
    error= ndbcluster_discover(thd, db, name, frmblob, frmlen);
2593 2594
#endif
  if (!error)
2595
    statistic_increment(thd->status_var.ha_discover_count,&LOCK_status);
2596 2597 2598 2599
  DBUG_RETURN(error);
}


2600
/*
2601
  Call this function in order to give the handler the possibility 
2602 2603
  to ask engine if there are any new tables that should be written to disk 
  or any dropped tables that need to be removed from disk
2604 2605
*/

2606 2607
int
ha_find_files(THD *thd,const char *db,const char *path,
2608
	      const char *wild, bool dir, List<char> *files)
2609 2610
{
  int error= 0;
2611 2612 2613
  DBUG_ENTER("ha_find_files");
  DBUG_PRINT("enter", ("db: %s, path: %s, wild: %s, dir: %d", 
		       db, path, wild, dir));
2614
#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
2615
  if (have_ndbcluster == SHOW_OPTION_YES)
2616
    error= ndbcluster_find_files(thd, db, path, wild, dir, files);
2617 2618 2619 2620
#endif
  DBUG_RETURN(error);
}

2621

2622 2623 2624 2625 2626 2627 2628 2629 2630
/*
  Ask handler if the table exists in engine

  RETURN
    0                   Table does not exist
    1                   Table exists
    #                   Error code

 */
2631
int ha_table_exists_in_engine(THD* thd, const char* db, const char* name)
2632
{
2633 2634
  int error= 0;
  DBUG_ENTER("ha_table_exists_in_engine");
2635
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
2636
#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
2637
  if (have_ndbcluster == SHOW_OPTION_YES)
2638
    error= ndbcluster_table_exists_in_engine(thd, db, name);
2639
#endif
2640
  DBUG_PRINT("exit", ("error: %d", error));
2641 2642 2643
  DBUG_RETURN(error);
}

tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669
#ifdef HAVE_NDB_BINLOG
/*
  TODO: change this into a dynamic struct
  List<handlerton> does not work as
  1. binlog_end is called when MEM_ROOT is gone
  2. cannot work with thd MEM_ROOT as memory should be freed
*/
#define MAX_HTON_LIST_ST 63
struct hton_list_st
{
  handlerton *hton[MAX_HTON_LIST_ST];
  uint sz;
};

struct binlog_func_st
{
  enum_binlog_func fn;
  void *arg;
};

/*
  Listing handlertons first to avoid recursive calls and deadlock
*/
static my_bool binlog_func_list(THD *thd, st_plugin_int *plugin, void *arg)
{
  hton_list_st *hton_list= (hton_list_st *)arg;
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2670
  handlerton *hton= (handlerton *)plugin->data;
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728
  if (hton->state == SHOW_OPTION_YES && hton->binlog_func)
  {
    uint sz= hton_list->sz;
    if (sz == MAX_HTON_LIST_ST-1)
    {
      /* list full */
      return FALSE;
    }
    hton_list->hton[sz]= hton;
    hton_list->sz= sz+1;
  }
  return FALSE;
}

static my_bool binlog_func_foreach(THD *thd, binlog_func_st *bfn)
{
  handlerton *hton;
  hton_list_st hton_list;
  hton_list.sz= 0;
  plugin_foreach(thd, binlog_func_list,
                 MYSQL_STORAGE_ENGINE_PLUGIN, &hton_list);

  uint i= 0, sz= hton_list.sz;
  while(i < sz)
    hton_list.hton[i++]->binlog_func(thd, bfn->fn, bfn->arg);
  return FALSE;
}

int ha_reset_logs(THD *thd)
{
  binlog_func_st bfn= {BFN_RESET_LOGS, 0};
  binlog_func_foreach(thd, &bfn);
  return 0;
}

void ha_reset_slave(THD* thd)
{
  binlog_func_st bfn= {BFN_RESET_SLAVE, 0};
  binlog_func_foreach(thd, &bfn);
}

void ha_binlog_wait(THD* thd)
{
  binlog_func_st bfn= {BFN_BINLOG_WAIT, 0};
  binlog_func_foreach(thd, &bfn);
}

int ha_binlog_end(THD* thd)
{
  binlog_func_st bfn= {BFN_BINLOG_END, 0};
  binlog_func_foreach(thd, &bfn);
  return 0;
}

int ha_binlog_index_purge_file(THD *thd, const char *file)
{
  binlog_func_st bfn= {BFN_BINLOG_PURGE_FILE, (void *)file};
  binlog_func_foreach(thd, &bfn);
knielsen@mysql.com's avatar
knielsen@mysql.com committed
2729
  return 0;
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740
}

struct binlog_log_query_st
{
  enum_binlog_command binlog_command;
  const char *query;
  uint query_length;
  const char *db;
  const char *table_name;
};

2741 2742 2743
static my_bool binlog_log_query_handlerton2(THD *thd,
                                            const handlerton *hton,
                                            void *args)
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755
{
  struct binlog_log_query_st *b= (struct binlog_log_query_st*)args;
  if (hton->state == SHOW_OPTION_YES && hton->binlog_log_query)
    hton->binlog_log_query(thd,
                           b->binlog_command,
                           b->query,
                           b->query_length,
                           b->db,
                           b->table_name);
  return FALSE;
}

2756 2757 2758 2759
static my_bool binlog_log_query_handlerton(THD *thd,
                                           st_plugin_int *plugin,
                                           void *args)
{
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2760
  return binlog_log_query_handlerton2(thd, (const handlerton *)plugin->data, args);
2761 2762 2763 2764
}

void ha_binlog_log_query(THD *thd, const handlerton *hton,
                         enum_binlog_command binlog_command,
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
2765 2766 2767 2768 2769 2770 2771 2772 2773
                         const char *query, uint query_length,
                         const char *db, const char *table_name)
{
  struct binlog_log_query_st b;
  b.binlog_command= binlog_command;
  b.query= query;
  b.query_length= query_length;
  b.db= db;
  b.table_name= table_name;
2774 2775 2776 2777 2778
  if (hton == 0)
    plugin_foreach(thd, binlog_log_query_handlerton,
                   MYSQL_STORAGE_ENGINE_PLUGIN, &b);
  else
    binlog_log_query_handlerton2(thd, hton, &b);
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
2779 2780
}
#endif
2781

ingo@mysql.com's avatar
ingo@mysql.com committed
2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815
/*
  Read the first row of a multi-range set.

  SYNOPSIS
    read_multi_range_first()
    found_range_p       Returns a pointer to the element in 'ranges' that
                        corresponds to the returned row.
    ranges              An array of KEY_MULTI_RANGE range descriptions.
    range_count         Number of ranges in 'ranges'.
    sorted		If result should be sorted per key.
    buffer              A HANDLER_BUFFER for internal handler usage.

  NOTES
    Record is read into table->record[0].
    *found_range_p returns a valid value only if read_multi_range_first()
    returns 0.
    Sorting is done within each range. If you want an overall sort, enter
    'ranges' with sorted ranges.

  RETURN
    0			OK, found a row
    HA_ERR_END_OF_FILE	No rows in range
    #			Error code
*/

int handler::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
                                    KEY_MULTI_RANGE *ranges, uint range_count,
                                    bool sorted, HANDLER_BUFFER *buffer)
{
  int result= HA_ERR_END_OF_FILE;
  DBUG_ENTER("handler::read_multi_range_first");
  multi_range_sorted= sorted;
  multi_range_buffer= buffer;

2816 2817 2818
  table->mark_columns_used_by_index_no_reset(active_index, table->read_set);
  table->column_bitmaps_set(table->read_set, table->write_set);

ingo@mysql.com's avatar
ingo@mysql.com committed
2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909
  for (multi_range_curr= ranges, multi_range_end= ranges + range_count;
       multi_range_curr < multi_range_end;
       multi_range_curr++)
  {
    result= read_range_first(multi_range_curr->start_key.length ?
                             &multi_range_curr->start_key : 0,
                             multi_range_curr->end_key.length ?
                             &multi_range_curr->end_key : 0,
                             test(multi_range_curr->range_flag & EQ_RANGE),
                             multi_range_sorted);
    if (result != HA_ERR_END_OF_FILE)
      break;
  }

  *found_range_p= multi_range_curr;
  DBUG_PRINT("exit",("result %d", result));
  DBUG_RETURN(result);
}


/*
  Read the next row of a multi-range set.

  SYNOPSIS
    read_multi_range_next()
    found_range_p       Returns a pointer to the element in 'ranges' that
                        corresponds to the returned row.

  NOTES
    Record is read into table->record[0].
    *found_range_p returns a valid value only if read_multi_range_next()
    returns 0.

  RETURN
    0			OK, found a row
    HA_ERR_END_OF_FILE	No (more) rows in range
    #			Error code
*/

int handler::read_multi_range_next(KEY_MULTI_RANGE **found_range_p)
{
  int result;
  DBUG_ENTER("handler::read_multi_range_next");

  /* We should not be called after the last call returned EOF. */
  DBUG_ASSERT(multi_range_curr < multi_range_end);

  do
  {
    /* Save a call if there can be only one row in range. */
    if (multi_range_curr->range_flag != (UNIQUE_RANGE | EQ_RANGE))
    {
      result= read_range_next();

      /* On success or non-EOF errors jump to the end. */
      if (result != HA_ERR_END_OF_FILE)
        break;
    }
    else
    {
      /*
        We need to set this for the last range only, but checking this
        condition is more expensive than just setting the result code.
      */
      result= HA_ERR_END_OF_FILE;
    }

    /* Try the next range(s) until one matches a record. */
    for (multi_range_curr++;
         multi_range_curr < multi_range_end;
         multi_range_curr++)
    {
      result= read_range_first(multi_range_curr->start_key.length ?
                               &multi_range_curr->start_key : 0,
                               multi_range_curr->end_key.length ?
                               &multi_range_curr->end_key : 0,
                               test(multi_range_curr->range_flag & EQ_RANGE),
                               multi_range_sorted);
      if (result != HA_ERR_END_OF_FILE)
        break;
    }
  }
  while ((result == HA_ERR_END_OF_FILE) &&
         (multi_range_curr < multi_range_end));

  *found_range_p= multi_range_curr;
  DBUG_PRINT("exit",("handler::read_multi_range_next: result %d", result));
  DBUG_RETURN(result);
}


2910 2911 2912 2913 2914 2915 2916 2917
/*
  Read first row between two ranges.
  Store ranges for future calls to read_range_next

  SYNOPSIS
    read_range_first()
    start_key		Start key. Is 0 if no min range
    end_key		End key.  Is 0 if no max range
2918
    eq_range_arg	Set to 1 if start_key == end_key		
2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931
    sorted		Set to 1 if result should be sorted per key

  NOTES
    Record is read into table->record[0]

  RETURN
    0			Found row
    HA_ERR_END_OF_FILE	No rows in range
    #			Error code
*/

int handler::read_range_first(const key_range *start_key,
			      const key_range *end_key,
2932
			      bool eq_range_arg, bool sorted)
2933 2934 2935 2936
{
  int result;
  DBUG_ENTER("handler::read_range_first");

2937
  eq_range= eq_range_arg;
2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955
  end_range= 0;
  if (end_key)
  {
    end_range= &save_end_range;
    save_end_range= *end_key;
    key_compare_result_on_equal= ((end_key->flag == HA_READ_BEFORE_KEY) ? 1 :
				  (end_key->flag == HA_READ_AFTER_KEY) ? -1 : 0);
  }
  range_key_part= table->key_info[active_index].key_part;

  if (!start_key)			// Read first record
    result= index_first(table->record[0]);
  else
    result= index_read(table->record[0],
		       start_key->key,
		       start_key->length,
		       start_key->flag);
  if (result)
2956 2957 2958
    DBUG_RETURN((result == HA_ERR_KEY_NOT_FOUND) 
		? HA_ERR_END_OF_FILE
		: result);
2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978

  DBUG_RETURN (compare_key(end_range) <= 0 ? 0 : HA_ERR_END_OF_FILE);
}


/*
  Read next row between two ranges.

  SYNOPSIS
    read_range_next()

  NOTES
    Record is read into table->record[0]

  RETURN
    0			Found row
    HA_ERR_END_OF_FILE	No rows in range
    #			Error code
*/

2979
int handler::read_range_next()
2980 2981 2982 2983 2984
{
  int result;
  DBUG_ENTER("handler::read_range_next");

  if (eq_range)
2985 2986 2987 2988 2989 2990 2991
  {
    /* We trust that index_next_same always gives a row in range */
    DBUG_RETURN(index_next_same(table->record[0],
                                end_range->key,
                                end_range->length));
  }
  result= index_next(table->record[0]);
2992 2993 2994 2995 2996 2997 2998
  if (result)
    DBUG_RETURN(result);
  DBUG_RETURN(compare_key(end_range) <= 0 ? 0 : HA_ERR_END_OF_FILE);
}


/*
2999
  Compare if found key (in row) is over max-value
3000 3001 3002

  SYNOPSIS
    compare_key
3003
    range		range to compare to row. May be 0 for no range
3004 3005
 
  NOTES
3006
    See key.cc::key_cmp() for details
3007 3008

  RETURN
3009 3010
    The return value is SIGN(key_in_row - range_key):

3011 3012 3013 3014 3015 3016 3017
    0			Key is equal to range or 'range' == 0 (no range)
   -1			Key is less than range
    1			Key is larger than range
*/

int handler::compare_key(key_range *range)
{
3018
  int cmp;
3019 3020
  if (!range)
    return 0;					// No max range
3021 3022 3023 3024
  cmp= key_cmp(range_key_part, range->key, range->length);
  if (!cmp)
    cmp= key_compare_result_on_equal;
  return cmp;
3025
}
3026 3027 3028 3029

int handler::index_read_idx(byte * buf, uint index, const byte * key,
			     uint key_len, enum ha_rkey_function find_flag)
{
3030
  int error= ha_index_init(index, 0);
3031 3032 3033 3034 3035 3036 3037
  if (!error)
    error= index_read(buf, key, key_len, find_flag);
  if (!error)
    error= ha_index_end();
  return error;
}

3038

3039 3040 3041 3042 3043
/*
  Returns a list of all known extensions.

  SYNOPSIS
    ha_known_exts()
3044

3045 3046
  NOTES
    No mutexes, worst case race is a minor surplus memory allocation
3047 3048
    We have to recreate the extension map if mysqld is restarted (for example
    within libmysqld)
3049 3050 3051 3052

  RETURN VALUE
    pointer		pointer to TYPELIB structure
*/
3053

3054 3055 3056 3057
static my_bool exts_handlerton(THD *unused, st_plugin_int *plugin,
                               void *arg)
{
  List<char> *found_exts= (List<char> *) arg;
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
3058
  handlerton *hton= (handlerton *)plugin->data;
3059 3060
  handler *file;
  if (hton->state == SHOW_OPTION_YES && hton->create &&
3061
      (file= hton->create((TABLE_SHARE*) 0, current_thd->mem_root)))
3062 3063 3064
  {
    List_iterator_fast<char> it(*found_exts);
    const char **ext, *old_ext;
3065

3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082
    for (ext= file->bas_ext(); *ext; ext++)
    {
      while ((old_ext= it++))
      {
        if (!strcmp(old_ext, *ext))
	  break;
      }
      if (!old_ext)
        found_exts->push_back((char *) *ext);

      it.rewind();
    }
    delete file;
  }
  return FALSE;
}

3083 3084
TYPELIB *ha_known_exts(void)
{
3085
  MEM_ROOT *mem_root= current_thd->mem_root;
3086
  if (!known_extensions.type_names || mysys_usage_id != known_extensions_id)
3087 3088
  {
    List<char> found_exts;
3089 3090 3091
    const char **ext, *old_ext;

    known_extensions_id= mysys_usage_id;
3092
    found_exts.push_back((char*) triggers_file_ext);
3093
    found_exts.push_back((char*) trigname_file_ext);
3094 3095

    plugin_foreach(NULL, exts_handlerton,
3096 3097
                   MYSQL_STORAGE_ENGINE_PLUGIN, &found_exts);

3098 3099 3100
    ext= (const char **) my_once_alloc(sizeof(char *)*
                                       (found_exts.elements+1),
                                       MYF(MY_WME | MY_FAE));
3101

3102
    DBUG_ASSERT(ext != 0);
3103 3104
    known_extensions.count= found_exts.elements;
    known_extensions.type_names= ext;
3105

3106
    List_iterator_fast<char> it(found_exts);
3107 3108 3109
    while ((old_ext= it++))
      *ext++= old_ext;
    *ext= 0;
3110 3111 3112
  }
  return &known_extensions;
}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3113

3114

3115 3116 3117
static bool stat_print(THD *thd, const char *type, uint type_len,
                       const char *file, uint file_len,
                       const char *status, uint status_len)
3118 3119 3120
{
  Protocol *protocol= thd->protocol;
  protocol->prepare_for_resend();
3121 3122 3123
  protocol->store(type, type_len, system_charset_info);
  protocol->store(file, file_len, system_charset_info);
  protocol->store(status, status_len, system_charset_info);
3124 3125 3126 3127 3128
  if (protocol->write())
    return TRUE;
  return FALSE;
}

3129 3130 3131 3132 3133

static my_bool showstat_handlerton(THD *thd, st_plugin_int *plugin,
                                   void *arg)
{
  enum ha_stat_type stat= *(enum ha_stat_type *) arg;
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
3134
  handlerton *hton= (handlerton *)plugin->data;
3135 3136 3137 3138 3139 3140 3141
  if (hton->state == SHOW_OPTION_YES && hton->show_status &&
      hton->show_status(thd, stat_print, stat))
    return TRUE;
  return FALSE;
}

bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
3142 3143 3144
{
  List<Item> field_list;
  Protocol *protocol= thd->protocol;
3145
  bool result;
3146 3147 3148 3149 3150 3151 3152 3153 3154

  field_list.push_back(new Item_empty_string("Type",10));
  field_list.push_back(new Item_empty_string("Name",FN_REFLEN));
  field_list.push_back(new Item_empty_string("Status",10));

  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
    return TRUE;

3155
  if (db_type == NULL)
3156
  {
3157
    result= plugin_foreach(thd, showstat_handlerton,
3158 3159 3160 3161 3162
                           MYSQL_STORAGE_ENGINE_PLUGIN, &stat);
  }
  else
  {
    if (db_type->state != SHOW_OPTION_YES)
3163
    {
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
3164 3165
      const LEX_STRING *name=&hton2plugin[db_type->slot]->name;
      result= stat_print(thd, name->str, name->length,
3166
                         "", 0, "DISABLED", 8) ? 1 : 0;
3167
    }
3168
    else
3169
      result= db_type->show_status &&
3170
              db_type->show_status(thd, stat_print, stat) ? 1 : 0;
3171 3172
  }

3173 3174 3175
  if (!result)
    send_eof(thd);
  return result;
3176 3177
}

3178 3179 3180 3181 3182
/*
  Function to check if the conditions for row-based binlogging is
  correct for the table.

  A row in the given table should be replicated if:
3183
  - Row-based replication is enabled in the current thread
3184
  - The binlog is enabled
3185 3186 3187
  - It is not a temporary table
  - The binary log is open
  - The database the table resides in shall be binlogged (binlog_*_db rules)
3188
  - table is not mysql.event
3189 3190 3191
*/

#ifdef HAVE_ROW_BASED_REPLICATION
3192 3193 3194 3195
/* The Sun compiler cannot instantiate the template below if this is
   declared static, but it works by putting it into an anonymous
   namespace. */
namespace {
3196 3197 3198 3199 3200
  struct st_table_data {
    char const *db;
    char const *name;
  };

3201
  static int table_name_compare(void const *a, void const *b)
3202 3203 3204 3205 3206 3207 3208 3209 3210
  {
    st_table_data const *x = (st_table_data const*) a;
    st_table_data const *y = (st_table_data const*) b;

    /* Doing lexical compare in order (db,name) */
    int const res= strcmp(x->db, y->db);
    return res != 0 ? res : strcmp(x->name, y->name);
  }

3211
  bool check_table_binlog_row_based(THD *thd, TABLE *table)
3212
  {
3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231
    static st_table_data const ignore[] = {
      { "mysql", "event" },
      { "mysql", "general_log" },
      { "mysql", "slow_log" }
    };

    my_size_t const ignore_size = sizeof(ignore)/sizeof(*ignore);
    st_table_data const item = { table->s->db.str, table->s->table_name.str };

    if (table->s->cached_row_logging_check == -1)
      table->s->cached_row_logging_check=
        (table->s->tmp_table == NO_TMP_TABLE) &&
        binlog_filter->db_ok(table->s->db.str) &&
        bsearch(&item, ignore, ignore_size,
                sizeof(st_table_data), table_name_compare) == NULL;

    DBUG_ASSERT(table->s->cached_row_logging_check == 0 ||
                table->s->cached_row_logging_check == 1);

3232 3233 3234 3235
    return (thd->current_stmt_binlog_row_based &&
            (thd->options & OPTION_BIN_LOG) &&
            mysql_bin_log.is_open() &&
            table->s->cached_row_logging_check);
3236
  }
3237 3238
}

3239
/*
3240 3241 3242
   Write table maps for all (manually or automatically) locked tables
   to the binary log.

3243 3244 3245 3246 3247 3248 3249 3250 3251
   SYNOPSIS
     write_locked_table_maps()
       thd     Pointer to THD structure

   DESCRIPTION
       This function will generate and write table maps for all tables
       that are locked by the thread 'thd'.  Either manually locked
       (stored in THD::locked_tables) and automatically locked (stored
       in THD::lock) are considered.
3252
   
3253 3254 3255 3256 3257 3258 3259
   RETURN VALUE
       0   All OK
       1   Failed to write all table maps

   SEE ALSO
       THD::lock
       THD::locked_tables
3260
 */
3261
namespace
3262
{
3263
  int write_locked_table_maps(THD *thd)
3264
  {
3265
    DBUG_ENTER("write_locked_table_maps");
3266 3267
    DBUG_PRINT("enter", ("thd=%p, thd->lock=%p, thd->locked_tables=%p, thd->extra_lock",
                         thd, thd->lock, thd->locked_tables, thd->extra_lock));
3268 3269

    if (thd->get_binlog_table_maps() == 0)
3270
    {
3271 3272 3273 3274
      MYSQL_LOCK *const locks[] = {
        thd->extra_lock, thd->lock, thd->locked_tables
      };
      for (my_ptrdiff_t i= 0 ; i < sizeof(locks)/sizeof(*locks) ; ++i )
3275
      {
3276 3277 3278 3279 3280 3281 3282 3283
        MYSQL_LOCK const *const lock= locks[i];
        if (lock == NULL)
          continue;

        TABLE **const end_ptr= lock->table + lock->table_count;
        for (TABLE **table_ptr= lock->table ; 
             table_ptr != end_ptr ;
             ++table_ptr)
3284
        {
3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298
          TABLE *const table= *table_ptr;
          DBUG_PRINT("info", ("Checking table %s", table->s->table_name));
          if (table->current_lock == F_WRLCK &&
              check_table_binlog_row_based(thd, table))
          {
            int const has_trans= table->file->has_transactions();
            int const error= thd->binlog_write_table_map(table, has_trans);
            /*
              If an error occurs, it is the responsibility of the caller to
              roll back the transaction.
            */
            if (unlikely(error))
              DBUG_RETURN(1);
          }
3299
        }
3300 3301
      }
    }
3302
    DBUG_RETURN(0);
3303
  }
3304

3305 3306 3307 3308
  template<class RowsEventT> int
  binlog_log_row(TABLE* table,
                 const byte *before_record,
                 const byte *after_record)
3309
  {
3310 3311 3312 3313
    if (table->file->is_injective())
      return 0;
    bool error= 0;
    THD *const thd= table->in_use;
3314

3315
    if (check_table_binlog_row_based(thd, table))
3316
    {
3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331
      MY_BITMAP cols;
      /* Potential buffer on the stack for the bitmap */
      uint32 bitbuf[BITMAP_STACKBUF_SIZE/sizeof(uint32)];
      uint n_fields= table->s->fields;
      my_bool use_bitbuf= n_fields <= sizeof(bitbuf)*8;

      /*
        If there are no table maps written to the binary log, this is
        the first row handled in this statement. In that case, we need
        to write table maps for all locked tables to the binary log.
      */
      if (likely(!(error= bitmap_init(&cols,
                                      use_bitbuf ? bitbuf : NULL,
                                      (n_fields + 7) & ~7UL,
                                      false))))
3332
      {
3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343
        bitmap_set_all(&cols);
        if (likely(!(error= write_locked_table_maps(thd))))
        {
          error=
            RowsEventT::binlog_row_logging_function(thd, table,
                                                    table->file->has_transactions(),
                                                    &cols, table->s->fields,
                                                    before_record, after_record);
        }
        if (!use_bitbuf)
          bitmap_free(&cols);
3344
      }
3345
    }
3346
    return error ? HA_ERR_RBR_LOGGING_FAILED : 0;
3347 3348
  }

3349 3350 3351 3352
  /*
    Instantiate the versions we need for the above template function,
    because we have -fno-implicit-template as compiling option.
  */
3353

3354 3355
  template int
  binlog_log_row<Write_rows_log_event>(TABLE *, const byte *, const byte *);
3356

3357 3358
  template int
  binlog_log_row<Delete_rows_log_event>(TABLE *, const byte *, const byte *);
3359

3360 3361 3362
  template int
  binlog_log_row<Update_rows_log_event>(TABLE *, const byte *, const byte *);
}
3363 3364 3365

#endif /* HAVE_ROW_BASED_REPLICATION */

3366
int handler::ha_external_lock(THD *thd, int lock_type)
3367
{
3368
  DBUG_ENTER("handler::ha_external_lock");
3369
  int error;
3370
  if (unlikely(error= external_lock(thd, lock_type)))
3371 3372
    DBUG_RETURN(error);
  DBUG_RETURN(0);
3373 3374
}

3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396

/*
  Check handler usage and reset state of file to after 'open'
*/

int handler::ha_reset()
{
  DBUG_ENTER("ha_reset");
  /* Check that we have called all proper delallocation functions */
  DBUG_ASSERT((byte*) table->def_read_set.bitmap +
              table->s->column_bitmap_size ==
              (char*) table->def_write_set.bitmap);
  DBUG_ASSERT(bitmap_is_set_all(&table->s->all_set));
  DBUG_ASSERT(table->key_read == 0);
  /* ensure that ha_index_end / ha_rnd_end has been called */
  DBUG_ASSERT(inited == NONE);
  /* Free cache used by filesort */
  free_io_cache(table);
  DBUG_RETURN(reset());
}


3397 3398 3399
int handler::ha_write_row(byte *buf)
{
  int error;
3400 3401
  if (unlikely(error= write_row(buf)))
    return error;
3402
#ifdef HAVE_ROW_BASED_REPLICATION
3403 3404
  if (unlikely(error= binlog_log_row<Write_rows_log_event>(table, 0, buf)))
    return error;
3405
#endif
3406
  return 0;
3407 3408 3409 3410 3411
}

int handler::ha_update_row(const byte *old_data, byte *new_data)
{
  int error;
3412 3413 3414 3415 3416 3417 3418

  /*
    Some storage engines require that the new record is in record[0]
    (and the old record is in record[1]).
   */
  DBUG_ASSERT(new_data == table->record[0]);

3419 3420
  if (unlikely(error= update_row(old_data, new_data)))
    return error;
3421
#ifdef HAVE_ROW_BASED_REPLICATION
3422 3423
  if (unlikely(error= binlog_log_row<Update_rows_log_event>(table, old_data, new_data)))
    return error;
3424
#endif
3425
  return 0;
3426 3427 3428 3429 3430
}

int handler::ha_delete_row(const byte *buf)
{
  int error;
3431 3432
  if (unlikely(error= delete_row(buf)))
    return error;
3433
#ifdef HAVE_ROW_BASED_REPLICATION
3434 3435
  if (unlikely(error= binlog_log_row<Delete_rows_log_event>(table, buf, 0)))
    return error;
3436
#endif
3437
  return 0;
3438
}
3439

3440

3441

3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452
/*
  use_hidden_primary_key() is called in case of an update/delete when
  (table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
  but we don't have a primary key
*/

void handler::use_hidden_primary_key()
{
  /* fallback to use all columns in the table to identify row */
  table->use_all_columns();
}
3453 3454


3455 3456 3457 3458
/*
  Dummy function which accept information about log files which is not need
  by handlers
*/
3459

3460 3461 3462 3463 3464 3465 3466
void signal_log_not_needed(struct handlerton, char *log_file)
{
  DBUG_ENTER("signal_log_not_needed");
  DBUG_PRINT("enter", ("logfile '%s'", log_file));
  DBUG_VOID_RETURN;
}

3467

3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519
#ifdef TRANS_LOG_MGM_EXAMPLE_CODE
/*
  Example of transaction log management functions based on assumption that logs
  placed into a directory
*/
#include <my_dir.h>
#include <my_sys.h>
int example_of_iterator_using_for_logs_cleanup(handlerton *hton)
{
  void *buffer;
  int res= 1;
  struct handler_iterator iterator;
  struct handler_log_file_data data;

  if (!hton->create_iterator)
    return 1; /* iterator creator is not supported */

  if ((*hton->create_iterator)(HA_TRANSACTLOG_ITERATOR, &iterator) !=
      HA_ITERATOR_OK)
  {
    /* error during creation of log iterator or iterator is not supported */
    return 1;
  }
  while((*iterator.next)(&iterator, (void*)&data) == 0)
  {
    printf("%s\n", data.filename.str);
    if (data.status == HA_LOG_STATUS_FREE &&
        my_delete(data.filename.str, MYF(MY_WME)))
      goto err;
  }
  res= 0;
err:
  (*iterator.destroy)(&iterator);
  return res;
}


/*
  Here we should get info from handler where it save logs but here is
  just example, so we use constant.
  IMHO FN_ROOTDIR ("/") is safe enough for example, because nobody has
  rights on it except root and it consist of directories only at lest for
  *nix (sorry, can't find windows-safe solution here, but it is only example).
*/
#define fl_dir FN_ROOTDIR


/*
  Dummy function to return log status should be replaced by function which
  really detect the log status and check that the file is a log of this
  handler.
*/
3520

3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627
enum log_status fl_get_log_status(char *log)
{
  MY_STAT stat_buff;
  if (my_stat(log, &stat_buff, MYF(0)))
    return HA_LOG_STATUS_INUSE;
  return HA_LOG_STATUS_NOSUCHLOG;
}


struct fl_buff
{
  LEX_STRING *names;
  enum log_status *statuses;
  uint32 entries;
  uint32 current;
};


int fl_log_iterator_next(struct handler_iterator *iterator,
                          void *iterator_object)
{
  struct fl_buff *buff= (struct fl_buff *)iterator->buffer;
  struct handler_log_file_data *data=
    (struct handler_log_file_data *) iterator_object;
  if (buff->current >= buff->entries)
    return 1;
  data->filename= buff->names[buff->current];
  data->status= buff->statuses[buff->current];
  buff->current++;
  return 0;
}


void fl_log_iterator_destroy(struct handler_iterator *iterator)
{
  my_free((gptr)iterator->buffer, MYF(MY_ALLOW_ZERO_PTR));
}


/*
  returns buffer, to be assigned in handler_iterator struct
*/
enum handler_create_iterator_result
fl_log_iterator_buffer_init(struct handler_iterator *iterator)
{
  MY_DIR *dirp;
  struct fl_buff *buff;
  char *name_ptr;
  byte *ptr;
  FILEINFO *file;
  uint32 i;

  /* to be able to make my_free without crash in case of error */
  iterator->buffer= 0;

  if (!(dirp = my_dir(fl_dir, MYF(0))))
  {
    return HA_ITERATOR_ERROR;
  }
  if ((ptr= (byte*)my_malloc(ALIGN_SIZE(sizeof(fl_buff)) +
                             ((ALIGN_SIZE(sizeof(LEX_STRING)) +
                               sizeof(enum log_status) +
                               + FN_REFLEN) *
                              (uint) dirp->number_off_files),
                             MYF(0))) == 0)
  {
    return HA_ITERATOR_ERROR;
  }
  buff= (struct fl_buff *)ptr;
  buff->entries= buff->current= 0;
  ptr= ptr + (ALIGN_SIZE(sizeof(fl_buff)));
  buff->names= (LEX_STRING*) (ptr);
  ptr= ptr + ((ALIGN_SIZE(sizeof(LEX_STRING)) *
               (uint) dirp->number_off_files));
  buff->statuses= (enum log_status *)(ptr);
  name_ptr= (char *)(ptr + (sizeof(enum log_status) *
                            (uint) dirp->number_off_files));
  for (i=0 ; i < (uint) dirp->number_off_files  ; i++)
  {
    enum log_status st;
    file= dirp->dir_entry + i;
    if ((file->name[0] == '.' &&
         ((file->name[1] == '.' && file->name[2] == '\0') ||
            file->name[1] == '\0')))
      continue;
    if ((st= fl_get_log_status(file->name)) == HA_LOG_STATUS_NOSUCHLOG)
      continue;
    name_ptr= strxnmov(buff->names[buff->entries].str= name_ptr,
                       FN_REFLEN, fl_dir, file->name, NullS);
    buff->names[buff->entries].length= (name_ptr -
                                        buff->names[buff->entries].str) - 1;
    buff->statuses[buff->entries]= st;
    buff->entries++;
  }

  iterator->buffer= buff;
  iterator->next= &fl_log_iterator_next;
  iterator->destroy= &fl_log_iterator_destroy;
  return HA_ITERATOR_OK;
}


/* An example of a iterator creator */
enum handler_create_iterator_result
fl_create_iterator(enum handler_iterator_type type,
                   struct handler_iterator *iterator)
{
3628
  switch(type) {
3629 3630 3631 3632 3633 3634 3635
  case HA_TRANSACTLOG_ITERATOR:
    return fl_log_iterator_buffer_init(iterator);
  default:
    return HA_ITERATOR_UNSUPPORTED;
  }
}
#endif /*TRANS_LOG_MGM_EXAMPLE_CODE*/