mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 20:42:30 +01:00
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
This commit is contained in:
parent
362e37b2d3
commit
0a23863b10
3 changed files with 724 additions and 1461 deletions
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
679
storage/tokudb/ha_tokudb_alter_common.cc
Normal file
679
storage/tokudb/ha_tokudb_alter_common.cc
Normal file
|
@ -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;
|
||||
}
|
Loading…
Reference in a new issue