From 17883b2f0d2b08743925a7e92017e77fabbacd81 Mon Sep 17 00:00:00 2001 From: Rich Prohaska Date: Tue, 17 Jul 2012 15:44:16 +0000 Subject: [PATCH] refs #5258 move handlerton alter table common functions to a separate file git-svn-id: file:///svn/mysql/tokudb-engine/tokudb-engine@45810 c7de825b-a66e-492c-adef-691d508d4ae1 --- storage/tokudb/ha_tokudb_alter_51.cc | 739 +--------------------- storage/tokudb/ha_tokudb_alter_56.cc | 767 ++--------------------- storage/tokudb/ha_tokudb_alter_common.cc | 679 ++++++++++++++++++++ 3 files changed, 724 insertions(+), 1461 deletions(-) create mode 100644 storage/tokudb/ha_tokudb_alter_common.cc diff --git a/storage/tokudb/ha_tokudb_alter_51.cc b/storage/tokudb/ha_tokudb_alter_51.cc index e1c13380ab2..c09bc0233f0 100644 --- a/storage/tokudb/ha_tokudb_alter_51.cc +++ b/storage/tokudb/ha_tokudb_alter_51.cc @@ -106,226 +106,7 @@ ha_tokudb::final_drop_index(TABLE *table_arg) { #if defined(HA_GENERAL_ONLINE) -// -// MySQL sets the null_bit as a number that you can bit-wise AND a byte to -// to evaluate whether a field is NULL or not. This value is a power of 2, from -// 2^0 to 2^7. We return the position of the bit within the byte, which is -// lg null_bit -// -static inline u_int32_t -get_null_bit_position(u_int32_t null_bit) { - u_int32_t retval = 0; - switch(null_bit) { - case (1): - retval = 0; - break; - case (2): - retval = 1; - break; - case (4): - retval = 2; - break; - case (8): - retval = 3; - break; - case (16): - retval = 4; - break; - case (32): - retval = 5; - break; - case (64): - retval = 6; - break; - case (128): - retval = 7; - break; - default: - assert(false); - } - return retval; -} - -// -// returns the index of the null bit of field. -// -static inline u_int32_t -get_overall_null_bit_position(TABLE* table, Field* field) { - u_int32_t offset = get_null_offset(table, field); - u_int32_t null_bit = field->null_bit; - return offset*8 + get_null_bit_position(null_bit); -} - -static bool -are_null_bits_in_order(TABLE* table) { - u_int32_t curr_null_pos = 0; - bool first = true; - bool retval = true; - for (uint i = 0; i < table->s->fields; i++) { - Field* curr_field = table->field[i]; - bool nullable = (curr_field->null_bit != 0); - if (nullable) { - u_int32_t pos = get_overall_null_bit_position( - table, - curr_field - ); - if (!first && pos != curr_null_pos+1){ - retval = false; - break; - } - first = false; - curr_null_pos = pos; - } - } - return retval; -} - -static u_int32_t -get_first_null_bit_pos(TABLE* table) { - u_int32_t table_pos = 0; - for (uint i = 0; i < table->s->fields; i++) { - Field* curr_field = table->field[i]; - bool nullable = (curr_field->null_bit != 0); - if (nullable) { - table_pos = get_overall_null_bit_position( - table, - curr_field - ); - break; - } - } - return table_pos; -} - -#if 0 -static bool -is_column_default_null(TABLE* src_table, u_int32_t field_index) { - Field* curr_field = src_table->field[field_index]; - bool is_null_default = false; - bool nullable = curr_field->null_bit != 0; - if (nullable) { - u_int32_t null_bit_position = get_overall_null_bit_position(src_table, curr_field); - is_null_default = is_overall_null_position_set( - src_table->s->default_values, - null_bit_position - ); - } - return is_null_default; -} -#endif - -static bool -tables_have_same_keys(TABLE* table, TABLE* altered_table, bool print_error, bool check_field_index) { - bool retval; - if (table->s->keys != altered_table->s->keys) { - if (print_error) { - sql_print_error("tables have different number of keys"); - } - retval = false; - goto cleanup; - } - if (table->s->primary_key != altered_table->s->primary_key) { - if (print_error) { - sql_print_error( - "Tables have different primary keys, %d %d", - table->s->primary_key, - altered_table->s->primary_key - ); - } - retval = false; - goto cleanup; - } - for (u_int32_t i=0; i < table->s->keys; i++) { - KEY* curr_orig_key = &table->key_info[i]; - KEY* curr_altered_key = &altered_table->key_info[i]; - if (strcmp(curr_orig_key->name, curr_altered_key->name)) { - if (print_error) { - sql_print_error( - "key %d has different name, %s %s", - i, - curr_orig_key->name, - curr_altered_key->name - ); - } - retval = false; - goto cleanup; - } - if (((curr_orig_key->flags & HA_CLUSTERING) == 0) != ((curr_altered_key->flags & HA_CLUSTERING) == 0)) { - if (print_error) { - sql_print_error( - "keys disagree on if they are clustering, %d, %d", - curr_orig_key->key_parts, - curr_altered_key->key_parts - ); - } - retval = false; - goto cleanup; - } - if (((curr_orig_key->flags & HA_NOSAME) == 0) != ((curr_altered_key->flags & HA_NOSAME) == 0)) { - if (print_error) { - sql_print_error( - "keys disagree on if they are unique, %d, %d", - curr_orig_key->key_parts, - curr_altered_key->key_parts - ); - } - retval = false; - goto cleanup; - } - if (curr_orig_key->key_parts != curr_altered_key->key_parts) { - if (print_error) { - sql_print_error( - "keys have different number of parts, %d, %d", - curr_orig_key->key_parts, - curr_altered_key->key_parts - ); - } - retval = false; - goto cleanup; - } - // - // now verify that each field in the key is the same - // - for (u_int32_t j = 0; j < curr_orig_key->key_parts; j++) { - KEY_PART_INFO* curr_orig_part = &curr_orig_key->key_part[j]; - KEY_PART_INFO* curr_altered_part = &curr_altered_key->key_part[j]; - Field* curr_orig_field = curr_orig_part->field; - Field* curr_altered_field = curr_altered_part->field; - if (curr_orig_part->length != curr_altered_part->length) { - if (print_error) { - sql_print_error( - "Key %s has different length at index %d", - curr_orig_key->name, - j - ); - } - retval = false; - goto cleanup; - } - bool are_fields_same; - are_fields_same = (check_field_index) ? - (curr_orig_part->fieldnr == curr_altered_part->fieldnr && - fields_are_same_type(curr_orig_field, curr_altered_field)) : - (are_two_fields_same(curr_orig_field,curr_altered_field)); - - if (!are_fields_same) { - if (print_error) { - sql_print_error( - "Key %s has different field at index %d", - curr_orig_key->name, - j - ); - } - retval = false; - goto cleanup; - } - } - } - - retval = true; -cleanup: - return retval; -} +#include "ha_tokudb_alter_common.cc" void ha_tokudb::print_alter_info( @@ -380,123 +161,8 @@ ha_tokudb::print_alter_info( printf("******\n"); } - -static int -find_changed_columns( - u_int32_t* changed_columns, - u_int32_t* num_changed_columns, - TABLE* smaller_table, - TABLE* bigger_table - ) -{ - uint curr_new_col_index = 0; - uint i = 0; - int retval; - u_int32_t curr_num_changed_columns=0; - assert(bigger_table->s->fields > smaller_table->s->fields); - for (i = 0; i < smaller_table->s->fields; i++, curr_new_col_index++) { - if (curr_new_col_index >= bigger_table->s->fields) { - sql_print_error("error in determining changed columns"); - retval = 1; - goto cleanup; - } - Field* curr_field_in_new = bigger_table->field[curr_new_col_index]; - Field* curr_field_in_orig = smaller_table->field[i]; - while (!fields_have_same_name(curr_field_in_orig, curr_field_in_new)) { - changed_columns[curr_num_changed_columns] = curr_new_col_index; - curr_num_changed_columns++; - curr_new_col_index++; - curr_field_in_new = bigger_table->field[curr_new_col_index]; - if (curr_new_col_index >= bigger_table->s->fields) { - sql_print_error("error in determining changed columns"); - retval = 1; - goto cleanup; - } - } - // at this point, curr_field_in_orig and curr_field_in_new should be the same, let's verify - // make sure the two fields that have the same name are ok - if (!are_two_fields_same(curr_field_in_orig, curr_field_in_new)) { - sql_print_error( - "Two fields that were supposedly the same are not: \ - %s in original, %s in new", - curr_field_in_orig->field_name, - curr_field_in_new->field_name - ); - retval = 1; - goto cleanup; - } - } - for (i = curr_new_col_index; i < bigger_table->s->fields; i++) { - changed_columns[curr_num_changed_columns] = i; - curr_num_changed_columns++; - } - *num_changed_columns = curr_num_changed_columns; - retval = 0; -cleanup: - return retval; -} - static bool -column_rename_supported( - HA_ALTER_INFO* alter_info, - TABLE* orig_table, - TABLE* new_table - ) -{ - bool retval = false; - bool keys_same_for_cr; - uint num_fields_with_different_names = 0; - uint field_with_different_name = orig_table->s->fields; - if (orig_table->s->fields != new_table->s->fields) { - retval = false; - goto cleanup; - } - if (alter_info->contains_first_or_after) { - retval = false; - goto cleanup; - } - - for (uint i = 0; i < orig_table->s->fields; i++) { - Field* orig_field = orig_table->field[i]; - Field* new_field = new_table->field[i]; - if (!fields_are_same_type(orig_field, new_field)) { - retval = false; - goto cleanup; - } - if (!fields_have_same_name(orig_field, new_field)) { - num_fields_with_different_names++; - field_with_different_name = i; - } - } - // only allow one renamed field - if (num_fields_with_different_names != 1) { - retval = false; - goto cleanup; - } - assert(field_with_different_name < orig_table->s->fields); - // - // at this point, we have verified that the two tables have - // the same field types and with ONLY one field with a different name. - // We have also identified the field with the different name - // - // Now we need to check the indexes - // - keys_same_for_cr = tables_have_same_keys( - orig_table, - new_table, - false, - true - ); - if (!keys_same_for_cr) { - retval = false; - goto cleanup; - } - retval = true; -cleanup: - return retval; -} - -bool alter_has_other_flag_set(HA_ALTER_FLAGS* alter_flags, uint flag) { +alter_has_other_flag_set(HA_ALTER_FLAGS* alter_flags, uint flag) { bool retval = false; for (uint i = 0; i < HA_MAX_ALTER_FLAGS; i++) { if (i == flag) @@ -720,7 +386,7 @@ ha_tokudb::check_if_supported_alter(TABLE *altered_table, // has changed only its name. If we find anything to // the contrary, we don't allow it, also check indexes - bool cr_supported = column_rename_supported(alter_info, table, altered_table); + bool cr_supported = column_rename_supported(table, altered_table, alter_info->contains_first_or_after); if (cr_supported) { retval = HA_ALTER_SUPPORTED_WAIT_LOCK; } @@ -735,405 +401,6 @@ cleanup: DBUG_RETURN(retval); } -#define UP_COL_ADD_OR_DROP 0 - -#define COL_DROP 0xaa -#define COL_ADD 0xbb - -#define COL_FIXED 0xcc -#define COL_VAR 0xdd -#define COL_BLOB 0xee - - - -#define STATIC_ROW_MUTATOR_SIZE 1+8+2+8+8+8 - -/* -how much space do I need for the mutators? -static stuff first: -1 - UP_COL_ADD_OR_DROP -8 - old null, new null -2 - old num_offset, new num_offset -8 - old fixed_field size, new fixed_field_size -8 - old and new length of offsets -8 - old and new starting null bit position -TOTAL: 27 - -dynamic stuff: -4 - number of columns -for each column: -1 - add or drop -1 - is nullable -4 - if nullable, position -1 - if add, whether default is null or not -1 - if fixed, var, or not - for fixed, entire default - for var, 4 bytes length, then entire default - for blob, nothing -So, an upperbound is 4 + num_fields(12) + all default stuff - -static blob stuff: -4 - num blobs -1 byte for each num blobs in old table -So, an upperbound is 4 + kc_info->num_blobs - -dynamic blob stuff: -for each blob added: -1 - state if we are adding or dropping -4 - blob index -if add, 1 len bytes - at most, 4 0's -So, upperbound is num_blobs(1+4+1+4) = num_columns*10 -*/ -static u_int32_t -fill_static_row_mutator( - uchar* buf, - TABLE* orig_table, - TABLE* altered_table, - KEY_AND_COL_INFO* orig_kc_info, - KEY_AND_COL_INFO* altered_kc_info, - u_int32_t keynr - ) -{ - // - // start packing extra - // - uchar* pos = buf; - // says what the operation is - pos[0] = UP_COL_ADD_OR_DROP; - pos++; - - // - // null byte information - // - memcpy(pos, &orig_table->s->null_bytes, sizeof(orig_table->s->null_bytes)); - pos += sizeof(orig_table->s->null_bytes); - memcpy(pos, &altered_table->s->null_bytes, sizeof(orig_table->s->null_bytes)); - pos += sizeof(altered_table->s->null_bytes); - - // - // num_offset_bytes - // - assert(orig_kc_info->num_offset_bytes <= 2); - pos[0] = orig_kc_info->num_offset_bytes; - pos++; - assert(altered_kc_info->num_offset_bytes <= 2); - pos[0] = altered_kc_info->num_offset_bytes; - pos++; - - // - // size of fixed fields - // - u_int32_t fixed_field_size = orig_kc_info->mcp_info[keynr].fixed_field_size; - memcpy(pos, &fixed_field_size, sizeof(fixed_field_size)); - pos += sizeof(fixed_field_size); - fixed_field_size = altered_kc_info->mcp_info[keynr].fixed_field_size; - memcpy(pos, &fixed_field_size, sizeof(fixed_field_size)); - pos += sizeof(fixed_field_size); - - // - // length of offsets - // - u_int32_t len_of_offsets = orig_kc_info->mcp_info[keynr].len_of_offsets; - memcpy(pos, &len_of_offsets, sizeof(len_of_offsets)); - pos += sizeof(len_of_offsets); - len_of_offsets = altered_kc_info->mcp_info[keynr].len_of_offsets; - memcpy(pos, &len_of_offsets, sizeof(len_of_offsets)); - pos += sizeof(len_of_offsets); - - u_int32_t orig_start_null_pos = get_first_null_bit_pos(orig_table); - memcpy(pos, &orig_start_null_pos, sizeof(orig_start_null_pos)); - pos += sizeof(orig_start_null_pos); - u_int32_t altered_start_null_pos = get_first_null_bit_pos(altered_table); - memcpy(pos, &altered_start_null_pos, sizeof(altered_start_null_pos)); - pos += sizeof(altered_start_null_pos); - - assert((pos-buf) == STATIC_ROW_MUTATOR_SIZE); - return pos - buf; -} - -static u_int32_t -fill_dynamic_row_mutator( - uchar* buf, - u_int32_t* columns, - u_int32_t num_columns, - TABLE* src_table, - KEY_AND_COL_INFO* src_kc_info, - u_int32_t keynr, - bool is_add, - bool* out_has_blobs - ) -{ - uchar* pos = buf; - bool has_blobs = false; - u_int32_t cols = num_columns; - memcpy(pos, &cols, sizeof(cols)); - pos += sizeof(cols); - for (u_int32_t i = 0; i < num_columns; i++) { - u_int32_t curr_index = columns[i]; - Field* curr_field = src_table->field[curr_index]; - - pos[0] = is_add ? COL_ADD : COL_DROP; - pos++; - // - // NULL bit information - // - bool is_null_default = false; - bool nullable = curr_field->null_bit != 0; - if (!nullable) { - pos[0] = 0; - pos++; - } - else { - pos[0] = 1; - pos++; - // write position of null byte that is to be removed - u_int32_t null_bit_position = get_overall_null_bit_position(src_table, curr_field); - memcpy(pos, &null_bit_position, sizeof(null_bit_position)); - pos += sizeof(null_bit_position); - // - // if adding a column, write the value of the default null_bit - // - if (is_add) { - is_null_default = is_overall_null_position_set( - src_table->s->default_values, - null_bit_position - ); - pos[0] = is_null_default ? 1 : 0; - pos++; - } - } - if (src_kc_info->field_lengths[curr_index] != 0) { - // we have a fixed field being dropped - // store the offset and the number of bytes - pos[0] = COL_FIXED; - pos++; - //store the offset - u_int32_t fixed_field_offset = src_kc_info->cp_info[keynr][curr_index].col_pack_val; - memcpy(pos, &fixed_field_offset, sizeof(fixed_field_offset)); - pos += sizeof(fixed_field_offset); - //store the number of bytes - u_int32_t num_bytes = src_kc_info->field_lengths[curr_index]; - memcpy(pos, &num_bytes, sizeof(num_bytes)); - pos += sizeof(num_bytes); - if (is_add && !is_null_default) { - uint curr_field_offset = field_offset(curr_field, src_table); - memcpy( - pos, - src_table->s->default_values + curr_field_offset, - num_bytes - ); - pos += num_bytes; - } - } - else if (src_kc_info->length_bytes[curr_index] != 0) { - pos[0] = COL_VAR; - pos++; - //store the index of the variable column - u_int32_t var_field_index = src_kc_info->cp_info[keynr][curr_index].col_pack_val; - memcpy(pos, &var_field_index, sizeof(var_field_index)); - pos += sizeof(var_field_index); - if (is_add && !is_null_default) { - uint curr_field_offset = field_offset(curr_field, src_table); - u_int32_t len_bytes = src_kc_info->length_bytes[curr_index]; - u_int32_t data_length = get_var_data_length( - src_table->s->default_values + curr_field_offset, - len_bytes - ); - memcpy(pos, &data_length, sizeof(data_length)); - pos += sizeof(data_length); - memcpy( - pos, - src_table->s->default_values + curr_field_offset + len_bytes, - data_length - ); - pos += data_length; - } - } - else { - pos[0] = COL_BLOB; - pos++; - has_blobs = true; - } - } - *out_has_blobs = has_blobs; - return pos-buf; -} - -static u_int32_t -fill_static_blob_row_mutator( - uchar* buf, - TABLE* src_table, - KEY_AND_COL_INFO* src_kc_info - ) -{ - uchar* pos = buf; - // copy number of blobs - memcpy(pos, &src_kc_info->num_blobs, sizeof(src_kc_info->num_blobs)); - pos += sizeof(src_kc_info->num_blobs); - // copy length bytes for each blob - for (u_int32_t i = 0; i < src_kc_info->num_blobs; i++) { - u_int32_t curr_field_index = src_kc_info->blob_fields[i]; - Field* field = src_table->field[curr_field_index]; - u_int32_t len_bytes = field->row_pack_length(); - assert(len_bytes <= 4); - pos[0] = len_bytes; - pos++; - } - - return pos-buf; -} - -static u_int32_t -fill_dynamic_blob_row_mutator( - uchar* buf, - u_int32_t* columns, - u_int32_t num_columns, - TABLE* src_table, - KEY_AND_COL_INFO* src_kc_info, - bool is_add - ) -{ - uchar* pos = buf; - for (u_int32_t i = 0; i < num_columns; i++) { - u_int32_t curr_field_index = columns[i]; - Field* curr_field = src_table->field[curr_field_index]; - if (src_kc_info->field_lengths[curr_field_index] == 0 && - src_kc_info->length_bytes[curr_field_index]== 0 - ) - { - // find out which blob it is - u_int32_t blob_index = src_kc_info->num_blobs; - for (u_int32_t j = 0; j < src_kc_info->num_blobs; j++) { - if (curr_field_index == src_kc_info->blob_fields[j]) { - blob_index = j; - break; - } - } - // assert we found blob in list - assert(blob_index < src_kc_info->num_blobs); - pos[0] = is_add ? COL_ADD : COL_DROP; - pos++; - memcpy(pos, &blob_index, sizeof(blob_index)); - pos += sizeof(blob_index); - if (is_add) { - u_int32_t len_bytes = curr_field->row_pack_length(); - assert(len_bytes <= 4); - pos[0] = len_bytes; - pos++; - - // create a zero length blob field that can be directly copied in - // for now, in MySQL, we can only have blob fields - // that have no default value - memset(pos, 0, len_bytes); - pos += len_bytes; - } - } - else { - // not a blob, continue - continue; - } - } - return pos-buf; -} - -// TODO: carefully review to make sure that the right information is used -// TODO: namely, when do we get stuff from share->kc_info and when we get -// TODO: it from altered_kc_info, and when is keynr associated with the right thing -u_int32_t -ha_tokudb::fill_row_mutator( - uchar* buf, - u_int32_t* columns, - u_int32_t num_columns, - TABLE* altered_table, - KEY_AND_COL_INFO* altered_kc_info, - u_int32_t keynr, - bool is_add - ) -{ - if (tokudb_debug & TOKUDB_DEBUG_ALTER_TABLE_INFO) { - printf("*****some info:*************\n"); - printf( - "old things: num_null_bytes %d, num_offset_bytes %d, fixed_field_size %d, fixed_field_size %d\n", - table->s->null_bytes, - share->kc_info.num_offset_bytes, - share->kc_info.mcp_info[keynr].fixed_field_size, - share->kc_info.mcp_info[keynr].len_of_offsets - ); - printf( - "new things: num_null_bytes %d, num_offset_bytes %d, fixed_field_size %d, fixed_field_size %d\n", - altered_table->s->null_bytes, - altered_kc_info->num_offset_bytes, - altered_kc_info->mcp_info[keynr].fixed_field_size, - altered_kc_info->mcp_info[keynr].len_of_offsets - ); - printf("****************************\n"); - } - uchar* pos = buf; - bool has_blobs = false; - pos += fill_static_row_mutator( - pos, - table, - altered_table, - &share->kc_info, - altered_kc_info, - keynr - ); - - if (is_add) { - pos += fill_dynamic_row_mutator( - pos, - columns, - num_columns, - altered_table, - altered_kc_info, - keynr, - is_add, - &has_blobs - ); - } - else { - pos += fill_dynamic_row_mutator( - pos, - columns, - num_columns, - table, - &share->kc_info, - keynr, - is_add, - &has_blobs - ); - } - if (has_blobs) { - pos += fill_static_blob_row_mutator( - pos, - table, - &share->kc_info - ); - if (is_add) { - pos += fill_dynamic_blob_row_mutator( - pos, - columns, - num_columns, - altered_table, - altered_kc_info, - is_add - ); - } - else { - pos += fill_dynamic_blob_row_mutator( - pos, - columns, - num_columns, - table, - &share->kc_info, - is_add - ); - } - } - return pos-buf; -} - int ha_tokudb::alter_table_phase2( THD *thd, diff --git a/storage/tokudb/ha_tokudb_alter_56.cc b/storage/tokudb/ha_tokudb_alter_56.cc index 613aeb32a40..21deda3ceb4 100644 --- a/storage/tokudb/ha_tokudb_alter_56.cc +++ b/storage/tokudb/ha_tokudb_alter_56.cc @@ -1,688 +1,52 @@ #if TOKU_INCLUDE_ALTER_56 -static bool -tables_have_same_keys(TABLE* table, TABLE* altered_table, bool print_error, bool check_field_index) { - bool retval; - if (table->s->keys != altered_table->s->keys) { - if (print_error) { - sql_print_error("tables have different number of keys"); - } - retval = false; - goto cleanup; - } - if (table->s->primary_key != altered_table->s->primary_key) { - if (print_error) { - sql_print_error( - "Tables have different primary keys, %d %d", - table->s->primary_key, - altered_table->s->primary_key - ); - } - retval = false; - goto cleanup; - } - for (u_int32_t i=0; i < table->s->keys; i++) { - KEY* curr_orig_key = &table->key_info[i]; - KEY* curr_altered_key = &altered_table->key_info[i]; - if (strcmp(curr_orig_key->name, curr_altered_key->name)) { - if (print_error) { - sql_print_error( - "key %d has different name, %s %s", - i, - curr_orig_key->name, - curr_altered_key->name - ); - } - retval = false; - goto cleanup; - } - if (((curr_orig_key->flags & HA_CLUSTERING) == 0) != ((curr_altered_key->flags & HA_CLUSTERING) == 0)) { - if (print_error) { - sql_print_error( - "keys disagree on if they are clustering, %d, %d", - curr_orig_key->key_parts, - curr_altered_key->key_parts - ); - } - retval = false; - goto cleanup; - } - if (((curr_orig_key->flags & HA_NOSAME) == 0) != ((curr_altered_key->flags & HA_NOSAME) == 0)) { - if (print_error) { - sql_print_error( - "keys disagree on if they are unique, %d, %d", - curr_orig_key->key_parts, - curr_altered_key->key_parts - ); - } - retval = false; - goto cleanup; - } - if (curr_orig_key->key_parts != curr_altered_key->key_parts) { - if (print_error) { - sql_print_error( - "keys have different number of parts, %d, %d", - curr_orig_key->key_parts, - curr_altered_key->key_parts - ); - } - retval = false; - goto cleanup; - } - // - // now verify that each field in the key is the same - // - for (u_int32_t j = 0; j < curr_orig_key->key_parts; j++) { - KEY_PART_INFO* curr_orig_part = &curr_orig_key->key_part[j]; - KEY_PART_INFO* curr_altered_part = &curr_altered_key->key_part[j]; - Field* curr_orig_field = curr_orig_part->field; - Field* curr_altered_field = curr_altered_part->field; - if (curr_orig_part->length != curr_altered_part->length) { - if (print_error) { - sql_print_error( - "Key %s has different length at index %d", - curr_orig_key->name, - j - ); - } - retval = false; - goto cleanup; - } - bool are_fields_same; - are_fields_same = (check_field_index) ? - (curr_orig_part->fieldnr == curr_altered_part->fieldnr && - fields_are_same_type(curr_orig_field, curr_altered_field)) : - (are_two_fields_same(curr_orig_field,curr_altered_field)); - - if (!are_fields_same) { - if (print_error) { - sql_print_error( - "Key %s has different field at index %d", - curr_orig_key->name, - j - ); - } - retval = false; - goto cleanup; - } +#include "ha_tokudb_alter_common.cc" + +void +ha_tokudb::print_alter_info(TABLE *altered_table, Alter_inplace_info *ha_alter_info) { + printf("***are keys of two tables same? %d\n", tables_have_same_keys(table, altered_table, false, false)); + if (ha_alter_info->handler_flags) { + printf("***alter flags set ***\n"); + for (int i = 0; i < 32; i++) { + if (ha_alter_info->handler_flags & (1 << i)) + printf("%d\n", i); } } - retval = true; -cleanup: - return retval; -} - -// -// MySQL sets the null_bit as a number that you can bit-wise AND a byte to -// to evaluate whether a field is NULL or not. This value is a power of 2, from -// 2^0 to 2^7. We return the position of the bit within the byte, which is -// lg null_bit -// -static inline u_int32_t -get_null_bit_position(u_int32_t null_bit) { - u_int32_t retval = 0; - switch(null_bit) { - case (1): - retval = 0; - break; - case (2): - retval = 1; - break; - case (4): - retval = 2; - break; - case (8): - retval = 3; - break; - case (16): - retval = 4; - break; - case (32): - retval = 5; - break; - case (64): - retval = 6; - break; - case (128): - retval = 7; - break; - default: - assert(false); - } - return retval; -} - -// -// returns the index of the null bit of field. -// -static inline u_int32_t -get_overall_null_bit_position(TABLE* table, Field* field) { - u_int32_t offset = get_null_offset(table, field); - u_int32_t null_bit = field->null_bit; - return offset*8 + get_null_bit_position(null_bit); -} - -#if 0 -static bool -are_null_bits_in_order(TABLE* table) { - u_int32_t curr_null_pos = 0; - bool first = true; - bool retval = true; + // everyone calculates data by doing some default_values - record[0], but I do not see why + // that is necessary + printf("******\n"); + printf("***orig table***\n"); for (uint i = 0; i < table->s->fields; i++) { - Field* curr_field = table->field[i]; - bool nullable = (curr_field->null_bit != 0); - if (nullable) { - u_int32_t pos = get_overall_null_bit_position( - table, - curr_field - ); - if (!first && pos != curr_null_pos+1){ - retval = false; - break; - } - first = false; - curr_null_pos = pos; - } + // + // make sure to use table->field, and NOT table->s->field + // + Field* curr_field = table->field[i]; + uint null_offset = get_null_offset(table, curr_field); + printf( + "name: %s, nullable: %d, null_offset: %d, is_null_field: %d, is_null %d, \n", + curr_field->field_name, + curr_field->null_bit, + null_offset, + (curr_field->null_ptr != NULL), + (curr_field->null_ptr != NULL) ? table->s->default_values[null_offset] & curr_field->null_bit : 0xffffffff + ); } - return retval; -} -#endif - -static u_int32_t -get_first_null_bit_pos(TABLE* table) { - u_int32_t table_pos = 0; - for (uint i = 0; i < table->s->fields; i++) { - Field* curr_field = table->field[i]; - bool nullable = (curr_field->null_bit != 0); - if (nullable) { - table_pos = get_overall_null_bit_position( - table, - curr_field - ); - break; - } + printf("******\n"); + printf("***altered table***\n"); + for (uint i = 0; i < altered_table->s->fields; i++) { + Field* curr_field = altered_table->field[i]; + uint null_offset = get_null_offset(altered_table, curr_field); + printf( + "name: %s, nullable: %d, null_offset: %d, is_null_field: %d, is_null %d, \n", + curr_field->field_name, + curr_field->null_bit, + null_offset, + (curr_field->null_ptr != NULL), + (curr_field->null_ptr != NULL) ? altered_table->s->default_values[null_offset] & curr_field->null_bit : 0xffffffff + ); } - return table_pos; -} - -#if 0 -static bool -is_column_default_null(TABLE* src_table, u_int32_t field_index) { - Field* curr_field = src_table->field[field_index]; - bool is_null_default = false; - bool nullable = curr_field->null_bit != 0; - if (nullable) { - u_int32_t null_bit_position = get_overall_null_bit_position(src_table, curr_field); - is_null_default = is_overall_null_position_set( - src_table->s->default_values, - null_bit_position - ); - } - return is_null_default; -} -#endif - -static u_int32_t -fill_static_row_mutator( - uchar* buf, - TABLE* orig_table, - TABLE* altered_table, - KEY_AND_COL_INFO* orig_kc_info, - KEY_AND_COL_INFO* altered_kc_info, - u_int32_t keynr - ) -{ - // - // start packing extra - // - uchar* pos = buf; - // says what the operation is - pos[0] = UP_COL_ADD_OR_DROP; - pos++; - - // - // null byte information - // - memcpy(pos, &orig_table->s->null_bytes, sizeof(orig_table->s->null_bytes)); - pos += sizeof(orig_table->s->null_bytes); - memcpy(pos, &altered_table->s->null_bytes, sizeof(orig_table->s->null_bytes)); - pos += sizeof(altered_table->s->null_bytes); - - // - // num_offset_bytes - // - assert(orig_kc_info->num_offset_bytes <= 2); - pos[0] = orig_kc_info->num_offset_bytes; - pos++; - assert(altered_kc_info->num_offset_bytes <= 2); - pos[0] = altered_kc_info->num_offset_bytes; - pos++; - - // - // size of fixed fields - // - u_int32_t fixed_field_size = orig_kc_info->mcp_info[keynr].fixed_field_size; - memcpy(pos, &fixed_field_size, sizeof(fixed_field_size)); - pos += sizeof(fixed_field_size); - fixed_field_size = altered_kc_info->mcp_info[keynr].fixed_field_size; - memcpy(pos, &fixed_field_size, sizeof(fixed_field_size)); - pos += sizeof(fixed_field_size); - - // - // length of offsets - // - u_int32_t len_of_offsets = orig_kc_info->mcp_info[keynr].len_of_offsets; - memcpy(pos, &len_of_offsets, sizeof(len_of_offsets)); - pos += sizeof(len_of_offsets); - len_of_offsets = altered_kc_info->mcp_info[keynr].len_of_offsets; - memcpy(pos, &len_of_offsets, sizeof(len_of_offsets)); - pos += sizeof(len_of_offsets); - - u_int32_t orig_start_null_pos = get_first_null_bit_pos(orig_table); - memcpy(pos, &orig_start_null_pos, sizeof(orig_start_null_pos)); - pos += sizeof(orig_start_null_pos); - u_int32_t altered_start_null_pos = get_first_null_bit_pos(altered_table); - memcpy(pos, &altered_start_null_pos, sizeof(altered_start_null_pos)); - pos += sizeof(altered_start_null_pos); - - assert((pos-buf) == STATIC_ROW_MUTATOR_SIZE); - return pos - buf; -} - -static u_int32_t -fill_dynamic_row_mutator( - uchar* buf, - u_int32_t* columns, - u_int32_t num_columns, - TABLE* src_table, - KEY_AND_COL_INFO* src_kc_info, - u_int32_t keynr, - bool is_add, - bool* out_has_blobs - ) -{ - uchar* pos = buf; - bool has_blobs = false; - u_int32_t cols = num_columns; - memcpy(pos, &cols, sizeof(cols)); - pos += sizeof(cols); - for (u_int32_t i = 0; i < num_columns; i++) { - u_int32_t curr_index = columns[i]; - Field* curr_field = src_table->field[curr_index]; - - pos[0] = is_add ? COL_ADD : COL_DROP; - pos++; - // - // NULL bit information - // - bool is_null_default = false; - bool nullable = curr_field->null_bit != 0; - if (!nullable) { - pos[0] = 0; - pos++; - } - else { - pos[0] = 1; - pos++; - // write position of null byte that is to be removed - u_int32_t null_bit_position = get_overall_null_bit_position(src_table, curr_field); - memcpy(pos, &null_bit_position, sizeof(null_bit_position)); - pos += sizeof(null_bit_position); - // - // if adding a column, write the value of the default null_bit - // - if (is_add) { - is_null_default = is_overall_null_position_set( - src_table->s->default_values, - null_bit_position - ); - pos[0] = is_null_default ? 1 : 0; - pos++; - } - } - if (src_kc_info->field_lengths[curr_index] != 0) { - // we have a fixed field being dropped - // store the offset and the number of bytes - pos[0] = COL_FIXED; - pos++; - //store the offset - u_int32_t fixed_field_offset = src_kc_info->cp_info[keynr][curr_index].col_pack_val; - memcpy(pos, &fixed_field_offset, sizeof(fixed_field_offset)); - pos += sizeof(fixed_field_offset); - //store the number of bytes - u_int32_t num_bytes = src_kc_info->field_lengths[curr_index]; - memcpy(pos, &num_bytes, sizeof(num_bytes)); - pos += sizeof(num_bytes); - if (is_add && !is_null_default) { - uint curr_field_offset = field_offset(curr_field, src_table); - memcpy( - pos, - src_table->s->default_values + curr_field_offset, - num_bytes - ); - pos += num_bytes; - } - } - else if (src_kc_info->length_bytes[curr_index] != 0) { - pos[0] = COL_VAR; - pos++; - //store the index of the variable column - u_int32_t var_field_index = src_kc_info->cp_info[keynr][curr_index].col_pack_val; - memcpy(pos, &var_field_index, sizeof(var_field_index)); - pos += sizeof(var_field_index); - if (is_add && !is_null_default) { - uint curr_field_offset = field_offset(curr_field, src_table); - u_int32_t len_bytes = src_kc_info->length_bytes[curr_index]; - u_int32_t data_length = get_var_data_length( - src_table->s->default_values + curr_field_offset, - len_bytes - ); - memcpy(pos, &data_length, sizeof(data_length)); - pos += sizeof(data_length); - memcpy( - pos, - src_table->s->default_values + curr_field_offset + len_bytes, - data_length - ); - pos += data_length; - } - } - else { - pos[0] = COL_BLOB; - pos++; - has_blobs = true; - } - } - *out_has_blobs = has_blobs; - return pos-buf; -} - -static u_int32_t -fill_static_blob_row_mutator( - uchar* buf, - TABLE* src_table, - KEY_AND_COL_INFO* src_kc_info - ) -{ - uchar* pos = buf; - // copy number of blobs - memcpy(pos, &src_kc_info->num_blobs, sizeof(src_kc_info->num_blobs)); - pos += sizeof(src_kc_info->num_blobs); - // copy length bytes for each blob - for (u_int32_t i = 0; i < src_kc_info->num_blobs; i++) { - u_int32_t curr_field_index = src_kc_info->blob_fields[i]; - Field* field = src_table->field[curr_field_index]; - u_int32_t len_bytes = field->row_pack_length(); - assert(len_bytes <= 4); - pos[0] = len_bytes; - pos++; - } - - return pos-buf; -} - -static u_int32_t -fill_dynamic_blob_row_mutator( - uchar* buf, - u_int32_t* columns, - u_int32_t num_columns, - TABLE* src_table, - KEY_AND_COL_INFO* src_kc_info, - bool is_add - ) -{ - uchar* pos = buf; - for (u_int32_t i = 0; i < num_columns; i++) { - u_int32_t curr_field_index = columns[i]; - Field* curr_field = src_table->field[curr_field_index]; - if (src_kc_info->field_lengths[curr_field_index] == 0 && - src_kc_info->length_bytes[curr_field_index]== 0 - ) - { - // find out which blob it is - u_int32_t blob_index = src_kc_info->num_blobs; - for (u_int32_t j = 0; j < src_kc_info->num_blobs; j++) { - if (curr_field_index == src_kc_info->blob_fields[j]) { - blob_index = j; - break; - } - } - // assert we found blob in list - assert(blob_index < src_kc_info->num_blobs); - pos[0] = is_add ? COL_ADD : COL_DROP; - pos++; - memcpy(pos, &blob_index, sizeof(blob_index)); - pos += sizeof(blob_index); - if (is_add) { - u_int32_t len_bytes = curr_field->row_pack_length(); - assert(len_bytes <= 4); - pos[0] = len_bytes; - pos++; - - // create a zero length blob field that can be directly copied in - // for now, in MySQL, we can only have blob fields - // that have no default value - memset(pos, 0, len_bytes); - pos += len_bytes; - } - } - else { - // not a blob, continue - continue; - } - } - return pos-buf; -} - -// TODO: carefully review to make sure that the right information is used -// TODO: namely, when do we get stuff from share->kc_info and when we get -// TODO: it from altered_kc_info, and when is keynr associated with the right thing -u_int32_t -ha_tokudb::fill_row_mutator( - uchar* buf, - u_int32_t* columns, - u_int32_t num_columns, - TABLE* altered_table, - KEY_AND_COL_INFO* altered_kc_info, - u_int32_t keynr, - bool is_add - ) -{ - if (tokudb_debug & TOKUDB_DEBUG_ALTER_TABLE_INFO) { - printf("*****some info:*************\n"); - printf( - "old things: num_null_bytes %d, num_offset_bytes %d, fixed_field_size %d, fixed_field_size %d\n", - table->s->null_bytes, - share->kc_info.num_offset_bytes, - share->kc_info.mcp_info[keynr].fixed_field_size, - share->kc_info.mcp_info[keynr].len_of_offsets - ); - printf( - "new things: num_null_bytes %d, num_offset_bytes %d, fixed_field_size %d, fixed_field_size %d\n", - altered_table->s->null_bytes, - altered_kc_info->num_offset_bytes, - altered_kc_info->mcp_info[keynr].fixed_field_size, - altered_kc_info->mcp_info[keynr].len_of_offsets - ); - printf("****************************\n"); - } - uchar* pos = buf; - bool has_blobs = false; - pos += fill_static_row_mutator( - pos, - table, - altered_table, - &share->kc_info, - altered_kc_info, - keynr - ); - - if (is_add) { - pos += fill_dynamic_row_mutator( - pos, - columns, - num_columns, - altered_table, - altered_kc_info, - keynr, - is_add, - &has_blobs - ); - } - else { - pos += fill_dynamic_row_mutator( - pos, - columns, - num_columns, - table, - &share->kc_info, - keynr, - is_add, - &has_blobs - ); - } - if (has_blobs) { - pos += fill_static_blob_row_mutator( - pos, - table, - &share->kc_info - ); - if (is_add) { - pos += fill_dynamic_blob_row_mutator( - pos, - columns, - num_columns, - altered_table, - altered_kc_info, - is_add - ); - } - else { - pos += fill_dynamic_blob_row_mutator( - pos, - columns, - num_columns, - table, - &share->kc_info, - is_add - ); - } - } - return pos-buf; -} - -static bool -column_rename_supported( - Alter_inplace_info *ha_alter_info, - TABLE* orig_table, - TABLE* new_table - ) -{ - bool retval = false; - bool keys_same_for_cr; - uint num_fields_with_different_names = 0; - uint field_with_different_name = orig_table->s->fields; - if (orig_table->s->fields != new_table->s->fields) { - retval = false; - goto cleanup; - } - if (ha_alter_info->handler_flags & Alter_inplace_info::ALTER_COLUMN_ORDER) { - retval = false; - goto cleanup; - } - for (uint i = 0; i < orig_table->s->fields; i++) { - Field* orig_field = orig_table->field[i]; - Field* new_field = new_table->field[i]; - if (!fields_are_same_type(orig_field, new_field)) { - retval = false; - goto cleanup; - } - if (!fields_have_same_name(orig_field, new_field)) { - num_fields_with_different_names++; - field_with_different_name = i; - } - } - // only allow one renamed field - if (num_fields_with_different_names != 1) { - retval = false; - goto cleanup; - } - assert(field_with_different_name < orig_table->s->fields); - // - // at this point, we have verified that the two tables have - // the same field types and with ONLY one field with a different name. - // We have also identified the field with the different name - // - // Now we need to check the indexes - // - keys_same_for_cr = tables_have_same_keys( - orig_table, - new_table, - false, - true - ); - if (!keys_same_for_cr) { - retval = false; - goto cleanup; - } - retval = true; -cleanup: - return retval; -} - -static int -find_changed_columns( - u_int32_t* changed_columns, - u_int32_t* num_changed_columns, - TABLE* smaller_table, - TABLE* bigger_table - ) -{ - int retval; - uint curr_new_col_index = 0; - u_int32_t curr_num_changed_columns=0; - assert(bigger_table->s->fields > smaller_table->s->fields); - for (uint i = 0; i < smaller_table->s->fields; i++, curr_new_col_index++) { - if (curr_new_col_index >= bigger_table->s->fields) { - sql_print_error("error in determining changed columns"); - retval = 1; - goto cleanup; - } - Field* curr_field_in_new = bigger_table->field[curr_new_col_index]; - Field* curr_field_in_orig = smaller_table->field[i]; - while (!fields_have_same_name(curr_field_in_orig, curr_field_in_new)) { - changed_columns[curr_num_changed_columns] = curr_new_col_index; - curr_num_changed_columns++; - curr_new_col_index++; - curr_field_in_new = bigger_table->field[curr_new_col_index]; - if (curr_new_col_index >= bigger_table->s->fields) { - sql_print_error("error in determining changed columns"); - retval = 1; - goto cleanup; - } - } - // at this point, curr_field_in_orig and curr_field_in_new should be the same, let's verify - // make sure the two fields that have the same name are ok - if (!are_two_fields_same(curr_field_in_orig, curr_field_in_new)) { - sql_print_error( - "Two fields that were supposedly the same are not: \ - %s in original, %s in new", - curr_field_in_orig->field_name, - curr_field_in_new->field_name - ); - retval = 1; - goto cleanup; - } - } - for (uint i = curr_new_col_index; i < bigger_table->s->fields; i++) { - changed_columns[curr_num_changed_columns] = i; - curr_num_changed_columns++; - } - *num_changed_columns = curr_num_changed_columns; - retval = 0; -cleanup: - return retval; + printf("******\n"); } class ha_tokudb_add_index_ctx : public inplace_alter_handler_ctx { @@ -744,7 +108,7 @@ ha_tokudb::check_if_supported_inplace_alter(TABLE *altered_table, Alter_inplace_ // now need to verify that one and only one column // has changed only its name. If we find anything to // the contrary, we don't allow it, also check indexes - bool cr_supported = column_rename_supported(ha_alter_info, table, altered_table); + bool cr_supported = column_rename_supported(table, altered_table, (ha_alter_info->handler_flags & Alter_inplace_info::ALTER_COLUMN_ORDER) != 0); if (cr_supported) result = HA_ALTER_INPLACE_NO_LOCK; } else @@ -1095,51 +459,4 @@ ha_tokudb::commit_inplace_alter_table(TABLE *altered_table, Alter_inplace_info * DBUG_RETURN(result); } -void -ha_tokudb::print_alter_info(TABLE *altered_table, Alter_inplace_info *ha_alter_info) { - printf("***are keys of two tables same? %d\n", tables_have_same_keys(table, altered_table, false, false)); - if (ha_alter_info->handler_flags) { - printf("***alter flags set ***\n"); - for (int i = 0; i < 32; i++) { - if (ha_alter_info->handler_flags & (1 << i)) - printf("%d\n", i); - } - } - - // everyone calculates data by doing some default_values - record[0], but I do not see why - // that is necessary - printf("******\n"); - printf("***orig table***\n"); - for (uint i = 0; i < table->s->fields; i++) { - // - // make sure to use table->field, and NOT table->s->field - // - Field* curr_field = table->field[i]; - uint null_offset = get_null_offset(table, curr_field); - printf( - "name: %s, nullable: %d, null_offset: %d, is_null_field: %d, is_null %d, \n", - curr_field->field_name, - curr_field->null_bit, - null_offset, - (curr_field->null_ptr != NULL), - (curr_field->null_ptr != NULL) ? table->s->default_values[null_offset] & curr_field->null_bit : 0xffffffff - ); - } - printf("******\n"); - printf("***altered table***\n"); - for (uint i = 0; i < altered_table->s->fields; i++) { - Field* curr_field = altered_table->field[i]; - uint null_offset = get_null_offset(altered_table, curr_field); - printf( - "name: %s, nullable: %d, null_offset: %d, is_null_field: %d, is_null %d, \n", - curr_field->field_name, - curr_field->null_bit, - null_offset, - (curr_field->null_ptr != NULL), - (curr_field->null_ptr != NULL) ? altered_table->s->default_values[null_offset] & curr_field->null_bit : 0xffffffff - ); - } - printf("******\n"); -} - #endif diff --git a/storage/tokudb/ha_tokudb_alter_common.cc b/storage/tokudb/ha_tokudb_alter_common.cc new file mode 100644 index 00000000000..bc6753adde1 --- /dev/null +++ b/storage/tokudb/ha_tokudb_alter_common.cc @@ -0,0 +1,679 @@ +static bool +tables_have_same_keys(TABLE* table, TABLE* altered_table, bool print_error, bool check_field_index) { + bool retval; + if (table->s->keys != altered_table->s->keys) { + if (print_error) { + sql_print_error("tables have different number of keys"); + } + retval = false; + goto cleanup; + } + if (table->s->primary_key != altered_table->s->primary_key) { + if (print_error) { + sql_print_error( + "Tables have different primary keys, %d %d", + table->s->primary_key, + altered_table->s->primary_key + ); + } + retval = false; + goto cleanup; + } + for (u_int32_t i=0; i < table->s->keys; i++) { + KEY* curr_orig_key = &table->key_info[i]; + KEY* curr_altered_key = &altered_table->key_info[i]; + if (strcmp(curr_orig_key->name, curr_altered_key->name)) { + if (print_error) { + sql_print_error( + "key %d has different name, %s %s", + i, + curr_orig_key->name, + curr_altered_key->name + ); + } + retval = false; + goto cleanup; + } + if (((curr_orig_key->flags & HA_CLUSTERING) == 0) != ((curr_altered_key->flags & HA_CLUSTERING) == 0)) { + if (print_error) { + sql_print_error( + "keys disagree on if they are clustering, %d, %d", + curr_orig_key->key_parts, + curr_altered_key->key_parts + ); + } + retval = false; + goto cleanup; + } + if (((curr_orig_key->flags & HA_NOSAME) == 0) != ((curr_altered_key->flags & HA_NOSAME) == 0)) { + if (print_error) { + sql_print_error( + "keys disagree on if they are unique, %d, %d", + curr_orig_key->key_parts, + curr_altered_key->key_parts + ); + } + retval = false; + goto cleanup; + } + if (curr_orig_key->key_parts != curr_altered_key->key_parts) { + if (print_error) { + sql_print_error( + "keys have different number of parts, %d, %d", + curr_orig_key->key_parts, + curr_altered_key->key_parts + ); + } + retval = false; + goto cleanup; + } + // + // now verify that each field in the key is the same + // + for (u_int32_t j = 0; j < curr_orig_key->key_parts; j++) { + KEY_PART_INFO* curr_orig_part = &curr_orig_key->key_part[j]; + KEY_PART_INFO* curr_altered_part = &curr_altered_key->key_part[j]; + Field* curr_orig_field = curr_orig_part->field; + Field* curr_altered_field = curr_altered_part->field; + if (curr_orig_part->length != curr_altered_part->length) { + if (print_error) { + sql_print_error( + "Key %s has different length at index %d", + curr_orig_key->name, + j + ); + } + retval = false; + goto cleanup; + } + bool are_fields_same; + are_fields_same = (check_field_index) ? + (curr_orig_part->fieldnr == curr_altered_part->fieldnr && + fields_are_same_type(curr_orig_field, curr_altered_field)) : + (are_two_fields_same(curr_orig_field,curr_altered_field)); + + if (!are_fields_same) { + if (print_error) { + sql_print_error( + "Key %s has different field at index %d", + curr_orig_key->name, + j + ); + } + retval = false; + goto cleanup; + } + } + } + + retval = true; +cleanup: + return retval; +} + +// MySQL sets the null_bit as a number that you can bit-wise AND a byte to +// to evaluate whether a field is NULL or not. This value is a power of 2, from +// 2^0 to 2^7. We return the position of the bit within the byte, which is +// lg null_bit +static inline u_int32_t +get_null_bit_position(u_int32_t null_bit) { + u_int32_t retval = 0; + switch(null_bit) { + case (1): + retval = 0; + break; + case (2): + retval = 1; + break; + case (4): + retval = 2; + break; + case (8): + retval = 3; + break; + case (16): + retval = 4; + break; + case (32): + retval = 5; + break; + case (64): + retval = 6; + break; + case (128): + retval = 7; + break; + default: + assert(false); + } + return retval; +} + +// returns the index of the null bit of field. +static inline u_int32_t +get_overall_null_bit_position(TABLE* table, Field* field) { + u_int32_t offset = get_null_offset(table, field); + u_int32_t null_bit = field->null_bit; + return offset*8 + get_null_bit_position(null_bit); +} + +// not static since 51 uses this and 56 does not +bool +are_null_bits_in_order(TABLE* table) { + u_int32_t curr_null_pos = 0; + bool first = true; + bool retval = true; + for (uint i = 0; i < table->s->fields; i++) { + Field* curr_field = table->field[i]; + bool nullable = (curr_field->null_bit != 0); + if (nullable) { + u_int32_t pos = get_overall_null_bit_position( + table, + curr_field + ); + if (!first && pos != curr_null_pos+1){ + retval = false; + break; + } + first = false; + curr_null_pos = pos; + } + } + return retval; +} + +static u_int32_t +get_first_null_bit_pos(TABLE* table) { + u_int32_t table_pos = 0; + for (uint i = 0; i < table->s->fields; i++) { + Field* curr_field = table->field[i]; + bool nullable = (curr_field->null_bit != 0); + if (nullable) { + table_pos = get_overall_null_bit_position( + table, + curr_field + ); + break; + } + } + return table_pos; +} + +#if 0 +static bool +is_column_default_null(TABLE* src_table, u_int32_t field_index) { + Field* curr_field = src_table->field[field_index]; + bool is_null_default = false; + bool nullable = curr_field->null_bit != 0; + if (nullable) { + u_int32_t null_bit_position = get_overall_null_bit_position(src_table, curr_field); + is_null_default = is_overall_null_position_set( + src_table->s->default_values, + null_bit_position + ); + } + return is_null_default; +} +#endif + +static u_int32_t +fill_static_row_mutator( + uchar* buf, + TABLE* orig_table, + TABLE* altered_table, + KEY_AND_COL_INFO* orig_kc_info, + KEY_AND_COL_INFO* altered_kc_info, + u_int32_t keynr + ) +{ + // + // start packing extra + // + uchar* pos = buf; + // says what the operation is + pos[0] = UP_COL_ADD_OR_DROP; + pos++; + + // + // null byte information + // + memcpy(pos, &orig_table->s->null_bytes, sizeof(orig_table->s->null_bytes)); + pos += sizeof(orig_table->s->null_bytes); + memcpy(pos, &altered_table->s->null_bytes, sizeof(orig_table->s->null_bytes)); + pos += sizeof(altered_table->s->null_bytes); + + // + // num_offset_bytes + // + assert(orig_kc_info->num_offset_bytes <= 2); + pos[0] = orig_kc_info->num_offset_bytes; + pos++; + assert(altered_kc_info->num_offset_bytes <= 2); + pos[0] = altered_kc_info->num_offset_bytes; + pos++; + + // + // size of fixed fields + // + u_int32_t fixed_field_size = orig_kc_info->mcp_info[keynr].fixed_field_size; + memcpy(pos, &fixed_field_size, sizeof(fixed_field_size)); + pos += sizeof(fixed_field_size); + fixed_field_size = altered_kc_info->mcp_info[keynr].fixed_field_size; + memcpy(pos, &fixed_field_size, sizeof(fixed_field_size)); + pos += sizeof(fixed_field_size); + + // + // length of offsets + // + u_int32_t len_of_offsets = orig_kc_info->mcp_info[keynr].len_of_offsets; + memcpy(pos, &len_of_offsets, sizeof(len_of_offsets)); + pos += sizeof(len_of_offsets); + len_of_offsets = altered_kc_info->mcp_info[keynr].len_of_offsets; + memcpy(pos, &len_of_offsets, sizeof(len_of_offsets)); + pos += sizeof(len_of_offsets); + + u_int32_t orig_start_null_pos = get_first_null_bit_pos(orig_table); + memcpy(pos, &orig_start_null_pos, sizeof(orig_start_null_pos)); + pos += sizeof(orig_start_null_pos); + u_int32_t altered_start_null_pos = get_first_null_bit_pos(altered_table); + memcpy(pos, &altered_start_null_pos, sizeof(altered_start_null_pos)); + pos += sizeof(altered_start_null_pos); + + assert((pos-buf) == STATIC_ROW_MUTATOR_SIZE); + return pos - buf; +} + +static u_int32_t +fill_dynamic_row_mutator( + uchar* buf, + u_int32_t* columns, + u_int32_t num_columns, + TABLE* src_table, + KEY_AND_COL_INFO* src_kc_info, + u_int32_t keynr, + bool is_add, + bool* out_has_blobs + ) +{ + uchar* pos = buf; + bool has_blobs = false; + u_int32_t cols = num_columns; + memcpy(pos, &cols, sizeof(cols)); + pos += sizeof(cols); + for (u_int32_t i = 0; i < num_columns; i++) { + u_int32_t curr_index = columns[i]; + Field* curr_field = src_table->field[curr_index]; + + pos[0] = is_add ? COL_ADD : COL_DROP; + pos++; + // + // NULL bit information + // + bool is_null_default = false; + bool nullable = curr_field->null_bit != 0; + if (!nullable) { + pos[0] = 0; + pos++; + } + else { + pos[0] = 1; + pos++; + // write position of null byte that is to be removed + u_int32_t null_bit_position = get_overall_null_bit_position(src_table, curr_field); + memcpy(pos, &null_bit_position, sizeof(null_bit_position)); + pos += sizeof(null_bit_position); + // + // if adding a column, write the value of the default null_bit + // + if (is_add) { + is_null_default = is_overall_null_position_set( + src_table->s->default_values, + null_bit_position + ); + pos[0] = is_null_default ? 1 : 0; + pos++; + } + } + if (src_kc_info->field_lengths[curr_index] != 0) { + // we have a fixed field being dropped + // store the offset and the number of bytes + pos[0] = COL_FIXED; + pos++; + //store the offset + u_int32_t fixed_field_offset = src_kc_info->cp_info[keynr][curr_index].col_pack_val; + memcpy(pos, &fixed_field_offset, sizeof(fixed_field_offset)); + pos += sizeof(fixed_field_offset); + //store the number of bytes + u_int32_t num_bytes = src_kc_info->field_lengths[curr_index]; + memcpy(pos, &num_bytes, sizeof(num_bytes)); + pos += sizeof(num_bytes); + if (is_add && !is_null_default) { + uint curr_field_offset = field_offset(curr_field, src_table); + memcpy( + pos, + src_table->s->default_values + curr_field_offset, + num_bytes + ); + pos += num_bytes; + } + } + else if (src_kc_info->length_bytes[curr_index] != 0) { + pos[0] = COL_VAR; + pos++; + //store the index of the variable column + u_int32_t var_field_index = src_kc_info->cp_info[keynr][curr_index].col_pack_val; + memcpy(pos, &var_field_index, sizeof(var_field_index)); + pos += sizeof(var_field_index); + if (is_add && !is_null_default) { + uint curr_field_offset = field_offset(curr_field, src_table); + u_int32_t len_bytes = src_kc_info->length_bytes[curr_index]; + u_int32_t data_length = get_var_data_length( + src_table->s->default_values + curr_field_offset, + len_bytes + ); + memcpy(pos, &data_length, sizeof(data_length)); + pos += sizeof(data_length); + memcpy( + pos, + src_table->s->default_values + curr_field_offset + len_bytes, + data_length + ); + pos += data_length; + } + } + else { + pos[0] = COL_BLOB; + pos++; + has_blobs = true; + } + } + *out_has_blobs = has_blobs; + return pos-buf; +} + +static u_int32_t +fill_static_blob_row_mutator( + uchar* buf, + TABLE* src_table, + KEY_AND_COL_INFO* src_kc_info + ) +{ + uchar* pos = buf; + // copy number of blobs + memcpy(pos, &src_kc_info->num_blobs, sizeof(src_kc_info->num_blobs)); + pos += sizeof(src_kc_info->num_blobs); + // copy length bytes for each blob + for (u_int32_t i = 0; i < src_kc_info->num_blobs; i++) { + u_int32_t curr_field_index = src_kc_info->blob_fields[i]; + Field* field = src_table->field[curr_field_index]; + u_int32_t len_bytes = field->row_pack_length(); + assert(len_bytes <= 4); + pos[0] = len_bytes; + pos++; + } + + return pos-buf; +} + +static u_int32_t +fill_dynamic_blob_row_mutator( + uchar* buf, + u_int32_t* columns, + u_int32_t num_columns, + TABLE* src_table, + KEY_AND_COL_INFO* src_kc_info, + bool is_add + ) +{ + uchar* pos = buf; + for (u_int32_t i = 0; i < num_columns; i++) { + u_int32_t curr_field_index = columns[i]; + Field* curr_field = src_table->field[curr_field_index]; + if (src_kc_info->field_lengths[curr_field_index] == 0 && + src_kc_info->length_bytes[curr_field_index]== 0 + ) + { + // find out which blob it is + u_int32_t blob_index = src_kc_info->num_blobs; + for (u_int32_t j = 0; j < src_kc_info->num_blobs; j++) { + if (curr_field_index == src_kc_info->blob_fields[j]) { + blob_index = j; + break; + } + } + // assert we found blob in list + assert(blob_index < src_kc_info->num_blobs); + pos[0] = is_add ? COL_ADD : COL_DROP; + pos++; + memcpy(pos, &blob_index, sizeof(blob_index)); + pos += sizeof(blob_index); + if (is_add) { + u_int32_t len_bytes = curr_field->row_pack_length(); + assert(len_bytes <= 4); + pos[0] = len_bytes; + pos++; + + // create a zero length blob field that can be directly copied in + // for now, in MySQL, we can only have blob fields + // that have no default value + memset(pos, 0, len_bytes); + pos += len_bytes; + } + } + else { + // not a blob, continue + continue; + } + } + return pos-buf; +} + +// TODO: carefully review to make sure that the right information is used +// TODO: namely, when do we get stuff from share->kc_info and when we get +// TODO: it from altered_kc_info, and when is keynr associated with the right thing +u_int32_t +ha_tokudb::fill_row_mutator( + uchar* buf, + u_int32_t* columns, + u_int32_t num_columns, + TABLE* altered_table, + KEY_AND_COL_INFO* altered_kc_info, + u_int32_t keynr, + bool is_add + ) +{ + if (tokudb_debug & TOKUDB_DEBUG_ALTER_TABLE_INFO) { + printf("*****some info:*************\n"); + printf( + "old things: num_null_bytes %d, num_offset_bytes %d, fixed_field_size %d, fixed_field_size %d\n", + table->s->null_bytes, + share->kc_info.num_offset_bytes, + share->kc_info.mcp_info[keynr].fixed_field_size, + share->kc_info.mcp_info[keynr].len_of_offsets + ); + printf( + "new things: num_null_bytes %d, num_offset_bytes %d, fixed_field_size %d, fixed_field_size %d\n", + altered_table->s->null_bytes, + altered_kc_info->num_offset_bytes, + altered_kc_info->mcp_info[keynr].fixed_field_size, + altered_kc_info->mcp_info[keynr].len_of_offsets + ); + printf("****************************\n"); + } + uchar* pos = buf; + bool has_blobs = false; + pos += fill_static_row_mutator( + pos, + table, + altered_table, + &share->kc_info, + altered_kc_info, + keynr + ); + + if (is_add) { + pos += fill_dynamic_row_mutator( + pos, + columns, + num_columns, + altered_table, + altered_kc_info, + keynr, + is_add, + &has_blobs + ); + } + else { + pos += fill_dynamic_row_mutator( + pos, + columns, + num_columns, + table, + &share->kc_info, + keynr, + is_add, + &has_blobs + ); + } + if (has_blobs) { + pos += fill_static_blob_row_mutator( + pos, + table, + &share->kc_info + ); + if (is_add) { + pos += fill_dynamic_blob_row_mutator( + pos, + columns, + num_columns, + altered_table, + altered_kc_info, + is_add + ); + } + else { + pos += fill_dynamic_blob_row_mutator( + pos, + columns, + num_columns, + table, + &share->kc_info, + is_add + ); + } + } + return pos-buf; +} + +static bool +column_rename_supported( + TABLE* orig_table, + TABLE* new_table, + bool alter_column_order + ) +{ + bool retval = false; + bool keys_same_for_cr; + uint num_fields_with_different_names = 0; + uint field_with_different_name = orig_table->s->fields; + if (orig_table->s->fields != new_table->s->fields) { + retval = false; + goto cleanup; + } + if (alter_column_order) { + retval = false; + goto cleanup; + } + for (uint i = 0; i < orig_table->s->fields; i++) { + Field* orig_field = orig_table->field[i]; + Field* new_field = new_table->field[i]; + if (!fields_are_same_type(orig_field, new_field)) { + retval = false; + goto cleanup; + } + if (!fields_have_same_name(orig_field, new_field)) { + num_fields_with_different_names++; + field_with_different_name = i; + } + } + // only allow one renamed field + if (num_fields_with_different_names != 1) { + retval = false; + goto cleanup; + } + assert(field_with_different_name < orig_table->s->fields); + // + // at this point, we have verified that the two tables have + // the same field types and with ONLY one field with a different name. + // We have also identified the field with the different name + // + // Now we need to check the indexes + // + keys_same_for_cr = tables_have_same_keys( + orig_table, + new_table, + false, + true + ); + if (!keys_same_for_cr) { + retval = false; + goto cleanup; + } + retval = true; +cleanup: + return retval; +} + +static int +find_changed_columns( + u_int32_t* changed_columns, + u_int32_t* num_changed_columns, + TABLE* smaller_table, + TABLE* bigger_table + ) +{ + int retval; + uint curr_new_col_index = 0; + u_int32_t curr_num_changed_columns=0; + assert(bigger_table->s->fields > smaller_table->s->fields); + for (uint i = 0; i < smaller_table->s->fields; i++, curr_new_col_index++) { + if (curr_new_col_index >= bigger_table->s->fields) { + sql_print_error("error in determining changed columns"); + retval = 1; + goto cleanup; + } + Field* curr_field_in_new = bigger_table->field[curr_new_col_index]; + Field* curr_field_in_orig = smaller_table->field[i]; + while (!fields_have_same_name(curr_field_in_orig, curr_field_in_new)) { + changed_columns[curr_num_changed_columns] = curr_new_col_index; + curr_num_changed_columns++; + curr_new_col_index++; + curr_field_in_new = bigger_table->field[curr_new_col_index]; + if (curr_new_col_index >= bigger_table->s->fields) { + sql_print_error("error in determining changed columns"); + retval = 1; + goto cleanup; + } + } + // at this point, curr_field_in_orig and curr_field_in_new should be the same, let's verify + // make sure the two fields that have the same name are ok + if (!are_two_fields_same(curr_field_in_orig, curr_field_in_new)) { + sql_print_error( + "Two fields that were supposedly the same are not: \ + %s in original, %s in new", + curr_field_in_orig->field_name, + curr_field_in_new->field_name + ); + retval = 1; + goto cleanup; + } + } + for (uint i = curr_new_col_index; i < bigger_table->s->fields; i++) { + changed_columns[curr_num_changed_columns] = i; + curr_num_changed_columns++; + } + *num_changed_columns = curr_num_changed_columns; + retval = 0; +cleanup: + return retval; +}