WL#3749: MySQL Cluster: On-line DDL, new handler interface

Affects: Server-6.0   —   Status: Complete   —   Priority: Low

In WL#3348 Online DDL, add/drop column, several
steps are defined for performing an on-line ALTER TABLE.
In step 9:
9) Build anything (build index, build unique key, reorganise partitions,,,)
there is need for a new interface to the storage engines to perform the
actual alter table operation(s).
Also WL#3348 only discusses on-line ALTER TABLE, but there will always be
a need for the old copying alter table, and storage engines need to be able
to declare if they will be able to support an ALTER TABLE on-line, or not.
This WL defines what needs to be implemented to extend the current handler
Current implementation in mysql_alter_table for determining if an on-line
operation should be done
The current implementation (5.1) only provides support for on-line
add/drop index and uses four new handler methods, check_if_incompatible_data,
add_index, prepare_drop_index, and final_drop_index. This limited interface
must be changed to support a general on-line ALTER TABLE interface.

1. Checking if a storage engine supports an ALTER TABLE
The method compare_tables does most of the checking if an ALTER TABLE
can be done on-line, including calling the handler::check_if_incompatible_data
      table                     The original table.
      create_list               The fields for the new table.
      key_info_buffer           An array of KEY structs for the new indexes.
      key_count                 The number of elements in the array.
      create_info               Create options for the new table.
      alter_info                Alter options.
      order_num                 Number of order list elements.
      index_drop_buffer   OUT   An array of offsets into table->key_info.
      index_drop_count    OUT   The number of elements in the array.
      index_add_buffer    OUT   An array of offsets into key_info_buffer.
      index_add_count     OUT   The number of elements in the array.
      varchar                   Table contains varchar (undocumented !!)
   'table' (first argument) contains information of the original
    table, which includes all corresponding parts that the new
    table has in arguments create_list, key_list and create_info.

    By comparing the changes between the original and new table
    we can determine how much it has changed after ALTER TABLE
    and whether we need to make a copy of the table, or just change
    the .frm file.

    If there are no data changes, but index changes, 'index_drop_buffer'
    and/or 'index_add_buffer' are populated with offsets into
    table->key_info or key_info_buffer respectively for the indexes
    that need to be dropped and/or (re-)created.

    0                           No copy needed
    ALTER_TABLE_DATA_CHANGED    Data changes, copy needed
    ALTER_TABLE_INDEX_CHANGED   Index changes, copy might be needed
static uint compare_tables(TABLE *table, List<create_field> *create_list,
                           KEY *key_info_buffer, uint key_count,
                           HA_CREATE_INFO *create_info,
                           ALTER_INFO *alter_info, uint order_num,
                           uint *index_drop_buffer, uint *index_drop_count,
                           uint *index_add_buffer, uint *index_add_count,
                           bool varchar)

bool handler::check_if_incompatible_data(HA_CREATE_INFO *create_info,
			                 uint table_changes)

where table_changes is IS_EQUAL_NO(0), IS_EQUAL_YES(1), or
IS_EQUAL_PACK_LENGTH(2) reflects any changes to the table fields.
Returns one of

This call is not sufficient for a storage engine to analyze any ALTER
TABLE to determine if it is supported or not.

The current implementation is based on that handlers specify "capability"
flags in handlerton::alter_table_flags. These currently only define
add/drop indexes.

Re-factoring of mysql_alter_table

The general structure of compare tables can be re-used, but with only
creating a parameters needed to send to the handler on a following
call. Change compare_tables to:
      table                     The original table.
      create_list               The fields for the new table.
      key_info_buffer           An array of KEY structs for the new indexes.
      key_count                 The number of elements in the array.
      create_info               Create options for the new table.
      alter_info                Alter options.
      order_num                 Number of order list elements.
      alter_flags         OUT   Indicate what has changed
      alter_table_info    OUT   Data structures useful for online alter table

static uint compare_tables(TABLE *table, List<create_field> *create_list,
                           KEY *key_info_buffer, uint key_count,
                           HA_CREATE_INFO *old_create_info,
			   HA_CREATE_INFO *new_create_info.
                           ALTER_INFO *alter_info, uint order_num,
                           HA_ALTER_FLAGS *alter_flags,
                           HA_ALTER_INFO *alter_table_info);

Call new method after:
New method 
int handler::check_if_supported_alter(TABLE *altered_table,
                                      HA_CREATE_INFO *create_info,
                                      HA_ALTER_FLAGS alter_flags,
                                      HA_ALTER_INFO *alter_table_info);
(with default implementation calling handler::check_if_incompatible_data with 
create_info, table_changes and returning  HA_ALTER_SUPPORTED_WAIT_LOCK if
handler::check_if_incompatible_data will be marked as depricated

HA_ALTER_FLAGS is defined by:
#define HA_ADD_INDEX                  (1L << 0)
#define HA_DROP_INDEX                 (1L << 1)
#define HA_ALTER_INDEX                (1L << 2)
#define HA_RENAME_INDEX               (1L << 3)
#define HA_ADD_UNIQUE_INDEX           (1L << 4)
#define HA_DROP_UNIQUE_INDEX          (1L << 5)
#define HA_ALTER_UNIQUE_INDEX         (1L << 6)
#define HA_RENAME_UNIQUE_INDEX        (1L << 7)
#define HA_ADD_PK_INDEX               (1L << 8)
#define HA_DROP_PK_INDEX              (1L << 9)
#define HA_ALTER_PK_INDEX             (1L << 10)
#define HA_ADD_COLUMN                 (1L << 11)
#define HA_DROP_COLUMN                (1L << 12)
#define HA_ALTER_COLUMN_NAME          (1L << 13)
#define HA_ALTER_COLUMN_TYPE          (1L << 14)
#define HA_ALTER_COLUMN_ORDER         (1L << 15)
#define HA_ADD_FOREIGN_KEY            (1L << 17)
#define HA_DROP_FOREIGN_KEY           (1L << 18)
#define HA_ALTER_FOREIGN_KEY          (1L << 19)
#define HA_ADD_CONSTRAINT             (1L << 20)
#define HA_ADD_PARTITION              (1L << 21)
#define HA_DROP_PARTITION             (1L << 22)
#define HA_COALESCE_PARTITION         (1L << 23)
#define HA_REORGANIZE_PARTITION       (1L << 24)
#define HA_CHANGE_CHARACTER_SET       (1L << 25)
#define HA_RENAME_TABLE               (1L << 28)

HA_ALTER_INFO is defined as
typedef struct st_ha_alter_information
  KEY  *key_info_buffer;
  uint key_count;
  uint index_drop_count;
  uint *index_drop_buffer;
  uint index_add_count;
  uint *index_add_buffer;

where more data structures can be added for different online operations,
currently only add/drop index and add attribute has been studied.

handler::check_if_supported_alter returns one of
#define HA_ALTER_ERROR                  -1
#define HA_ALTER_NOT_SUPPORTED           2

The handlerton::alter_table_flags will not be needed anymore, since
handlers will declare their capabilities dynamically through the
handler::check_if_supported_alter() call. Instead a table_flag,
HA_ONLINE_ALTER will be added, that specifies if a handler supports
any kind of on-line alter table at all.

The calculation of offsets to keys added and dropped should be moved out
of compare_tables and into an utility function that could be called from
prepare_alter_table (defined below), since these are actually only needed
if operation is to be done on-line. Note also the a table object representing
the new table definition will here be created earlier.

Current Handler Interface for Online CREATE/DROP INDEX

int handler::add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys)

int handler::prepare_drop_index(TABLE *table_arg, uint *key_num,
                                 uint num_of_keys)
int handler::final_drop_index(TABLE *table_arg)

These methods are only handling one of many possible on-line operations,
and adding methods for all of them will make the handler interface huge.
Adding general alter methods is needed. 

New Handler Interface

Two new methods are needed (will replace the above add/drop index methods)
that will support all possible on-line ALTER TABLE operations.

int handler::alter_table_phase1(TABLE *altered_table,
                                HA_CREATE_INFO *create_info,
                                HA_ALTER_FLAGS alter_flags,
                                HA_ALTER_INFO *alter_table_info)

Prepares for the alter table, such as re-numbering indexes at drop index, 
adds indexes and new attributes (if added last). Operations should not be
visible to applications using the old table definition, hence can done without
locking and can easily be rolled back.

int handler::alter_table_phase2(TABLE *altered_table,
                                HA_CREATE_INFO *create_info,
                                HA_ALTER_FLAGS alter_flags,
                                HA_ALTER_INFO *alter_table_info)

Performs the structural changes from alter table command that affect
ongoing transactions using this table, such as dropping indexes and 
dropping and reordering (not adding new attributes last) attributes.
Requires locking the table (if check_if_supported_alter returned

Error handling:
These methods should not fail, if they still do something unexpected must have
happened and the complete ALTER TABLE should fail.

Note that these will be called regardless if there need to be any change done 
by the handler, not all changes need to be two phase and most handlers don't
need to do anything during column rename or change to compatible type.