mirror of
https://github.com/MariaDB/server.git
synced 2025-01-19 13:32:33 +01:00
f7f060bbf9
git-svn-id: file:///svn/mysql/tokudb-engine/tokudb-engine@43893 c7de825b-a66e-492c-adef-691d508d4ae1
1683 lines
60 KiB
C++
1683 lines
60 KiB
C++
#if 50600 <= MYSQL_VERSION_ID && MYSQL_VERSION_ID <= 50699
|
|
|
|
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;
|
|
}
|
|
|
|
//
|
|
// checks whether the bit at index pos in data is set or not
|
|
//
|
|
static inline bool
|
|
is_overall_null_position_set(uchar* data, u_int32_t pos) {
|
|
u_int32_t offset = pos/8;
|
|
uchar remainder = pos%8;
|
|
uchar null_bit = 1<<remainder;
|
|
return ((data[offset] & null_bit) != 0);
|
|
}
|
|
|
|
//
|
|
// sets the bit at index pos in data to 1 if is_null, 0 otherwise
|
|
//
|
|
static inline void
|
|
set_overall_null_position(uchar* data, u_int32_t pos, bool is_null) {
|
|
u_int32_t offset = pos/8;
|
|
uchar remainder = pos%8;
|
|
uchar null_bit = 1<<remainder;
|
|
if (is_null) {
|
|
data[offset] |= null_bit;
|
|
}
|
|
else {
|
|
data[offset] &= ~null_bit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
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;
|
|
}
|
|
#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;
|
|
}
|
|
}
|
|
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
|
|
|
|
#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;
|
|
}
|
|
|
|
static inline void
|
|
copy_null_bits(
|
|
u_int32_t start_old_pos,
|
|
u_int32_t start_new_pos,
|
|
u_int32_t num_bits,
|
|
uchar* old_null_bytes,
|
|
uchar* new_null_bytes
|
|
)
|
|
{
|
|
for (u_int32_t i = 0; i < num_bits; i++) {
|
|
u_int32_t curr_old_pos = i + start_old_pos;
|
|
u_int32_t curr_new_pos = i + start_new_pos;
|
|
// copy over old null bytes
|
|
if (is_overall_null_position_set(old_null_bytes,curr_old_pos)) {
|
|
set_overall_null_position(new_null_bytes,curr_new_pos,true);
|
|
}
|
|
else {
|
|
set_overall_null_position(new_null_bytes,curr_new_pos,false);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
copy_var_fields(
|
|
u_int32_t start_old_num_var_field, //index of var fields that we should start writing
|
|
u_int32_t num_var_fields, // number of var fields to copy
|
|
uchar* old_var_field_offset_ptr, //static ptr to where offset bytes begin in old row
|
|
uchar old_num_offset_bytes, //number of offset bytes used in old row
|
|
uchar* start_new_var_field_data_ptr, // where the new var data should be written
|
|
uchar* start_new_var_field_offset_ptr, // where the new var offsets should be written
|
|
uchar* new_var_field_data_ptr, // pointer to beginning of var fields in new row
|
|
uchar* old_var_field_data_ptr, // pointer to beginning of var fields in old row
|
|
u_int32_t new_num_offset_bytes, // number of offset bytes used in new row
|
|
u_int32_t* num_data_bytes_written,
|
|
u_int32_t* num_offset_bytes_written
|
|
)
|
|
{
|
|
uchar* curr_new_var_field_data_ptr = start_new_var_field_data_ptr;
|
|
uchar* curr_new_var_field_offset_ptr = start_new_var_field_offset_ptr;
|
|
for (u_int32_t i = 0; i < num_var_fields; i++) {
|
|
u_int32_t field_len;
|
|
u_int32_t start_read_offset;
|
|
u_int32_t curr_old = i + start_old_num_var_field;
|
|
uchar* data_to_copy = NULL;
|
|
// get the length and pointer to data that needs to be copied
|
|
get_var_field_info(
|
|
&field_len,
|
|
&start_read_offset,
|
|
curr_old,
|
|
old_var_field_offset_ptr,
|
|
old_num_offset_bytes
|
|
);
|
|
data_to_copy = old_var_field_data_ptr + start_read_offset;
|
|
// now need to copy field_len bytes starting from data_to_copy
|
|
curr_new_var_field_data_ptr = write_var_field(
|
|
curr_new_var_field_offset_ptr,
|
|
curr_new_var_field_data_ptr,
|
|
new_var_field_data_ptr,
|
|
data_to_copy,
|
|
field_len,
|
|
new_num_offset_bytes
|
|
);
|
|
curr_new_var_field_offset_ptr += new_num_offset_bytes;
|
|
}
|
|
*num_data_bytes_written = (u_int32_t)(curr_new_var_field_data_ptr - start_new_var_field_data_ptr);
|
|
*num_offset_bytes_written = (u_int32_t)(curr_new_var_field_offset_ptr - start_new_var_field_offset_ptr);
|
|
}
|
|
|
|
static inline u_int32_t
|
|
copy_toku_blob(uchar* to_ptr, uchar* from_ptr, u_int32_t len_bytes, bool skip) {
|
|
u_int32_t length = 0;
|
|
if (!skip) {
|
|
memcpy(to_ptr, from_ptr, len_bytes);
|
|
}
|
|
length = get_blob_field_len(from_ptr,len_bytes);
|
|
if (!skip) {
|
|
memcpy(to_ptr + len_bytes, from_ptr + len_bytes, length);
|
|
}
|
|
return (length + len_bytes);
|
|
}
|
|
|
|
int
|
|
tokudb_update_fun(
|
|
DB* db,
|
|
const DBT *key,
|
|
const DBT *old_val,
|
|
const DBT *extra,
|
|
void (*set_val)(const DBT *new_val, void *set_extra),
|
|
void *set_extra
|
|
)
|
|
{
|
|
u_int32_t max_num_bytes;
|
|
u_int32_t num_columns;
|
|
DBT new_val;
|
|
u_int32_t num_bytes_left;
|
|
u_int32_t num_var_fields_to_copy;
|
|
u_int32_t num_data_bytes_written = 0;
|
|
u_int32_t num_offset_bytes_written = 0;
|
|
int error;
|
|
memset(&new_val, 0, sizeof(DBT));
|
|
uchar operation;
|
|
uchar* new_val_data = NULL;
|
|
uchar* extra_pos = NULL;
|
|
uchar* extra_pos_start = NULL;
|
|
//
|
|
// info for pointers into rows
|
|
//
|
|
u_int32_t old_num_null_bytes;
|
|
u_int32_t new_num_null_bytes;
|
|
uchar old_num_offset_bytes;
|
|
uchar new_num_offset_bytes;
|
|
u_int32_t old_fixed_field_size;
|
|
u_int32_t new_fixed_field_size;
|
|
u_int32_t old_len_of_offsets;
|
|
u_int32_t new_len_of_offsets;
|
|
|
|
uchar* old_fixed_field_ptr = NULL;
|
|
uchar* new_fixed_field_ptr = NULL;
|
|
u_int32_t curr_old_fixed_offset;
|
|
u_int32_t curr_new_fixed_offset;
|
|
|
|
uchar* old_null_bytes = NULL;
|
|
uchar* new_null_bytes = NULL;
|
|
u_int32_t curr_old_null_pos;
|
|
u_int32_t curr_new_null_pos;
|
|
u_int32_t old_null_bits_left;
|
|
u_int32_t new_null_bits_left;
|
|
u_int32_t overall_null_bits_left;
|
|
|
|
u_int32_t old_num_var_fields;
|
|
u_int32_t new_num_var_fields;
|
|
u_int32_t curr_old_num_var_field;
|
|
u_int32_t curr_new_num_var_field;
|
|
uchar* old_var_field_offset_ptr = NULL;
|
|
uchar* new_var_field_offset_ptr = NULL;
|
|
uchar* curr_new_var_field_offset_ptr = NULL;
|
|
uchar* old_var_field_data_ptr = NULL;
|
|
uchar* new_var_field_data_ptr = NULL;
|
|
uchar* curr_new_var_field_data_ptr = NULL;
|
|
|
|
u_int32_t start_blob_offset;
|
|
uchar* start_blob_ptr;
|
|
u_int32_t num_blob_bytes;
|
|
|
|
// came across a delete, nothing to update
|
|
if (old_val == NULL) {
|
|
error = 0;
|
|
goto cleanup;
|
|
}
|
|
|
|
extra_pos_start = (uchar *)extra->data;
|
|
extra_pos = (uchar *)extra->data;
|
|
|
|
operation = extra_pos[0];
|
|
extra_pos++;
|
|
assert(operation == UP_COL_ADD_OR_DROP);
|
|
|
|
memcpy(&old_num_null_bytes, extra_pos, sizeof(u_int32_t));
|
|
extra_pos += sizeof(u_int32_t);
|
|
memcpy(&new_num_null_bytes, extra_pos, sizeof(u_int32_t));
|
|
extra_pos += sizeof(u_int32_t);
|
|
|
|
old_num_offset_bytes = extra_pos[0];
|
|
extra_pos++;
|
|
new_num_offset_bytes = extra_pos[0];
|
|
extra_pos++;
|
|
|
|
memcpy(&old_fixed_field_size, extra_pos, sizeof(u_int32_t));
|
|
extra_pos += sizeof(u_int32_t);
|
|
memcpy(&new_fixed_field_size, extra_pos, sizeof(u_int32_t));
|
|
extra_pos += sizeof(u_int32_t);
|
|
|
|
memcpy(&old_len_of_offsets, extra_pos, sizeof(u_int32_t));
|
|
extra_pos += sizeof(u_int32_t);
|
|
memcpy(&new_len_of_offsets, extra_pos, sizeof(u_int32_t));
|
|
extra_pos += sizeof(u_int32_t);
|
|
|
|
max_num_bytes = old_val->size + extra->size + new_len_of_offsets + new_fixed_field_size;
|
|
new_val_data = (uchar *)my_malloc(
|
|
max_num_bytes,
|
|
MYF(MY_FAE)
|
|
);
|
|
if (new_val_data == NULL) { goto cleanup; }
|
|
|
|
old_fixed_field_ptr = (uchar *) old_val->data;
|
|
old_fixed_field_ptr += old_num_null_bytes;
|
|
new_fixed_field_ptr = new_val_data + new_num_null_bytes;
|
|
curr_old_fixed_offset = 0;
|
|
curr_new_fixed_offset = 0;
|
|
|
|
old_num_var_fields = old_len_of_offsets/old_num_offset_bytes;
|
|
new_num_var_fields = new_len_of_offsets/new_num_offset_bytes;
|
|
// following fields will change as we write the variable data
|
|
old_var_field_offset_ptr = old_fixed_field_ptr + old_fixed_field_size;
|
|
new_var_field_offset_ptr = new_fixed_field_ptr + new_fixed_field_size;
|
|
old_var_field_data_ptr = old_var_field_offset_ptr + old_len_of_offsets;
|
|
new_var_field_data_ptr = new_var_field_offset_ptr + new_len_of_offsets;
|
|
curr_new_var_field_offset_ptr = new_var_field_offset_ptr;
|
|
curr_new_var_field_data_ptr = new_var_field_data_ptr;
|
|
curr_old_num_var_field = 0;
|
|
curr_new_num_var_field = 0;
|
|
|
|
old_null_bytes = (uchar *)old_val->data;
|
|
new_null_bytes = new_val_data;
|
|
|
|
|
|
memcpy(&curr_old_null_pos, extra_pos, sizeof(u_int32_t));
|
|
extra_pos += sizeof(u_int32_t);
|
|
memcpy(&curr_new_null_pos, extra_pos, sizeof(u_int32_t));
|
|
extra_pos += sizeof(u_int32_t);
|
|
|
|
memcpy(&num_columns, extra_pos, sizeof(num_columns));
|
|
extra_pos += sizeof(num_columns);
|
|
|
|
//
|
|
// now go through and apply the change into new_val_data
|
|
//
|
|
for (u_int32_t i = 0; i < num_columns; i++) {
|
|
uchar op_type = extra_pos[0];
|
|
bool is_null_default = false;
|
|
extra_pos++;
|
|
|
|
assert(op_type == COL_DROP || op_type == COL_ADD);
|
|
bool nullable = (extra_pos[0] != 0);
|
|
extra_pos++;
|
|
if (nullable) {
|
|
u_int32_t null_bit_position;
|
|
memcpy(&null_bit_position, extra_pos, sizeof(u_int32_t));
|
|
extra_pos += sizeof(u_int32_t);
|
|
u_int32_t num_bits;
|
|
if (op_type == COL_DROP) {
|
|
assert(curr_old_null_pos <= null_bit_position);
|
|
num_bits = null_bit_position - curr_old_null_pos;
|
|
}
|
|
else {
|
|
assert(curr_new_null_pos <= null_bit_position);
|
|
num_bits = null_bit_position - curr_new_null_pos;
|
|
}
|
|
copy_null_bits(
|
|
curr_old_null_pos,
|
|
curr_new_null_pos,
|
|
num_bits,
|
|
old_null_bytes,
|
|
new_null_bytes
|
|
);
|
|
// update the positions
|
|
curr_new_null_pos += num_bits;
|
|
curr_old_null_pos += num_bits;
|
|
if (op_type == COL_DROP) {
|
|
curr_old_null_pos++; // account for dropped column
|
|
}
|
|
else {
|
|
is_null_default = (extra_pos[0] != 0);
|
|
extra_pos++;
|
|
set_overall_null_position(
|
|
new_null_bytes,
|
|
null_bit_position,
|
|
is_null_default
|
|
);
|
|
curr_new_null_pos++; //account for added column
|
|
}
|
|
}
|
|
uchar col_type = extra_pos[0];
|
|
extra_pos++;
|
|
if (col_type == COL_FIXED) {
|
|
u_int32_t col_offset;
|
|
u_int32_t col_size;
|
|
u_int32_t num_bytes_to_copy;
|
|
memcpy(&col_offset, extra_pos, sizeof(u_int32_t));
|
|
extra_pos += sizeof(u_int32_t);
|
|
memcpy(&col_size, extra_pos, sizeof(u_int32_t));
|
|
extra_pos += sizeof(u_int32_t);
|
|
|
|
if (op_type == COL_DROP) {
|
|
num_bytes_to_copy = col_offset - curr_old_fixed_offset;
|
|
}
|
|
else {
|
|
num_bytes_to_copy = col_offset - curr_new_fixed_offset;
|
|
}
|
|
memcpy(
|
|
new_fixed_field_ptr + curr_new_fixed_offset,
|
|
old_fixed_field_ptr + curr_old_fixed_offset,
|
|
num_bytes_to_copy
|
|
);
|
|
curr_old_fixed_offset += num_bytes_to_copy;
|
|
curr_new_fixed_offset += num_bytes_to_copy;
|
|
if (op_type == COL_DROP) {
|
|
// move old_fixed_offset val to skip OVER column that is being dropped
|
|
curr_old_fixed_offset += col_size;
|
|
}
|
|
else {
|
|
if (is_null_default) {
|
|
// copy zeroes
|
|
memset(new_fixed_field_ptr + curr_new_fixed_offset, 0, col_size);
|
|
}
|
|
else {
|
|
// copy data from extra_pos into new row
|
|
memcpy(
|
|
new_fixed_field_ptr + curr_new_fixed_offset,
|
|
extra_pos,
|
|
col_size
|
|
);
|
|
extra_pos += col_size;
|
|
}
|
|
curr_new_fixed_offset += col_size;
|
|
}
|
|
|
|
}
|
|
else if (col_type == COL_VAR) {
|
|
u_int32_t var_col_index;
|
|
memcpy(&var_col_index, extra_pos, sizeof(u_int32_t));
|
|
extra_pos += sizeof(u_int32_t);
|
|
if (op_type == COL_DROP) {
|
|
num_var_fields_to_copy = var_col_index - curr_old_num_var_field;
|
|
}
|
|
else {
|
|
num_var_fields_to_copy = var_col_index - curr_new_num_var_field;
|
|
}
|
|
copy_var_fields(
|
|
curr_old_num_var_field,
|
|
num_var_fields_to_copy,
|
|
old_var_field_offset_ptr,
|
|
old_num_offset_bytes,
|
|
curr_new_var_field_data_ptr,
|
|
curr_new_var_field_offset_ptr,
|
|
new_var_field_data_ptr, // pointer to beginning of var fields in new row
|
|
old_var_field_data_ptr, // pointer to beginning of var fields in old row
|
|
new_num_offset_bytes, // number of offset bytes used in new row
|
|
&num_data_bytes_written,
|
|
&num_offset_bytes_written
|
|
);
|
|
curr_new_var_field_data_ptr += num_data_bytes_written;
|
|
curr_new_var_field_offset_ptr += num_offset_bytes_written;
|
|
curr_new_num_var_field += num_var_fields_to_copy;
|
|
curr_old_num_var_field += num_var_fields_to_copy;
|
|
if (op_type == COL_DROP) {
|
|
curr_old_num_var_field++; // skip over dropped field
|
|
}
|
|
else {
|
|
if (is_null_default) {
|
|
curr_new_var_field_data_ptr = write_var_field(
|
|
curr_new_var_field_offset_ptr,
|
|
curr_new_var_field_data_ptr,
|
|
new_var_field_data_ptr,
|
|
NULL, //copying no data
|
|
0, //copying 0 bytes
|
|
new_num_offset_bytes
|
|
);
|
|
curr_new_var_field_offset_ptr += new_num_offset_bytes;
|
|
}
|
|
else {
|
|
u_int32_t data_length;
|
|
memcpy(&data_length, extra_pos, sizeof(data_length));
|
|
extra_pos += sizeof(data_length);
|
|
curr_new_var_field_data_ptr = write_var_field(
|
|
curr_new_var_field_offset_ptr,
|
|
curr_new_var_field_data_ptr,
|
|
new_var_field_data_ptr,
|
|
extra_pos, //copying data from mutator
|
|
data_length, //copying data_length bytes
|
|
new_num_offset_bytes
|
|
);
|
|
extra_pos += data_length;
|
|
curr_new_var_field_offset_ptr += new_num_offset_bytes;
|
|
}
|
|
curr_new_num_var_field++; //account for added column
|
|
}
|
|
}
|
|
else if (col_type == COL_BLOB) {
|
|
// handle blob data later
|
|
continue;
|
|
}
|
|
else {
|
|
assert(false);
|
|
}
|
|
}
|
|
// finish copying the null stuff
|
|
old_null_bits_left = 8*old_num_null_bytes - curr_old_null_pos;
|
|
new_null_bits_left = 8*new_num_null_bytes - curr_new_null_pos;
|
|
overall_null_bits_left = old_null_bits_left;
|
|
set_if_smaller(overall_null_bits_left, new_null_bits_left);
|
|
copy_null_bits(
|
|
curr_old_null_pos,
|
|
curr_new_null_pos,
|
|
overall_null_bits_left,
|
|
old_null_bytes,
|
|
new_null_bytes
|
|
);
|
|
// finish copying fixed field stuff
|
|
num_bytes_left = old_fixed_field_size - curr_old_fixed_offset;
|
|
memcpy(
|
|
new_fixed_field_ptr + curr_new_fixed_offset,
|
|
old_fixed_field_ptr + curr_old_fixed_offset,
|
|
num_bytes_left
|
|
);
|
|
curr_old_fixed_offset += num_bytes_left;
|
|
curr_new_fixed_offset += num_bytes_left;
|
|
// sanity check
|
|
assert(curr_new_fixed_offset == new_fixed_field_size);
|
|
|
|
// finish copying var field stuff
|
|
num_var_fields_to_copy = old_num_var_fields - curr_old_num_var_field;
|
|
copy_var_fields(
|
|
curr_old_num_var_field,
|
|
num_var_fields_to_copy,
|
|
old_var_field_offset_ptr,
|
|
old_num_offset_bytes,
|
|
curr_new_var_field_data_ptr,
|
|
curr_new_var_field_offset_ptr,
|
|
new_var_field_data_ptr, // pointer to beginning of var fields in new row
|
|
old_var_field_data_ptr, // pointer to beginning of var fields in old row
|
|
new_num_offset_bytes, // number of offset bytes used in new row
|
|
&num_data_bytes_written,
|
|
&num_offset_bytes_written
|
|
);
|
|
curr_new_var_field_offset_ptr += num_offset_bytes_written;
|
|
curr_new_var_field_data_ptr += num_data_bytes_written;
|
|
// sanity check
|
|
assert(curr_new_var_field_offset_ptr == new_var_field_data_ptr);
|
|
|
|
// start handling blobs
|
|
get_blob_field_info(
|
|
&start_blob_offset,
|
|
old_len_of_offsets,
|
|
old_var_field_data_ptr,
|
|
old_num_offset_bytes
|
|
);
|
|
start_blob_ptr = old_var_field_data_ptr + start_blob_offset;
|
|
// if nothing else in extra, then there are no blobs to add or drop, so can copy blobs straight
|
|
if ((extra_pos - extra_pos_start) == extra->size) {
|
|
num_blob_bytes = old_val->size - (start_blob_ptr - old_null_bytes);
|
|
memcpy(curr_new_var_field_data_ptr, start_blob_ptr, num_blob_bytes);
|
|
curr_new_var_field_data_ptr += num_blob_bytes;
|
|
}
|
|
// else, there is blob information to process
|
|
else {
|
|
uchar* len_bytes = NULL;
|
|
u_int32_t curr_old_blob = 0;
|
|
u_int32_t curr_new_blob = 0;
|
|
u_int32_t num_old_blobs = 0;
|
|
uchar* curr_old_blob_ptr = start_blob_ptr;
|
|
memcpy(&num_old_blobs, extra_pos, sizeof(num_old_blobs));
|
|
extra_pos += sizeof(num_old_blobs);
|
|
len_bytes = extra_pos;
|
|
extra_pos += num_old_blobs;
|
|
// copy over blob fields one by one
|
|
while ((extra_pos - extra_pos_start) < extra->size) {
|
|
uchar op_type = extra_pos[0];
|
|
extra_pos++;
|
|
u_int32_t num_blobs_to_copy = 0;
|
|
u_int32_t blob_index;
|
|
memcpy(&blob_index, extra_pos, sizeof(blob_index));
|
|
extra_pos += sizeof(blob_index);
|
|
assert (op_type == COL_DROP || op_type == COL_ADD);
|
|
if (op_type == COL_DROP) {
|
|
num_blobs_to_copy = blob_index - curr_old_blob;
|
|
}
|
|
else {
|
|
num_blobs_to_copy = blob_index - curr_new_blob;
|
|
}
|
|
for (u_int32_t i = 0; i < num_blobs_to_copy; i++) {
|
|
u_int32_t num_bytes_written = copy_toku_blob(
|
|
curr_new_var_field_data_ptr,
|
|
curr_old_blob_ptr,
|
|
len_bytes[curr_old_blob + i],
|
|
false
|
|
);
|
|
curr_old_blob_ptr += num_bytes_written;
|
|
curr_new_var_field_data_ptr += num_bytes_written;
|
|
}
|
|
curr_old_blob += num_blobs_to_copy;
|
|
curr_new_blob += num_blobs_to_copy;
|
|
if (op_type == COL_DROP) {
|
|
// skip over blob in row
|
|
u_int32_t num_bytes = copy_toku_blob(
|
|
NULL,
|
|
curr_old_blob_ptr,
|
|
len_bytes[curr_old_blob],
|
|
true
|
|
);
|
|
curr_old_blob++;
|
|
curr_old_blob_ptr += num_bytes;
|
|
}
|
|
else {
|
|
// copy new data
|
|
u_int32_t new_len_bytes = extra_pos[0];
|
|
extra_pos++;
|
|
u_int32_t num_bytes = copy_toku_blob(
|
|
curr_new_var_field_data_ptr,
|
|
extra_pos,
|
|
new_len_bytes,
|
|
false
|
|
);
|
|
curr_new_blob++;
|
|
curr_new_var_field_data_ptr += num_bytes;
|
|
extra_pos += num_bytes;
|
|
}
|
|
}
|
|
num_blob_bytes = old_val->size - (curr_old_blob_ptr - old_null_bytes);
|
|
memcpy(curr_new_var_field_data_ptr, curr_old_blob_ptr, num_blob_bytes);
|
|
curr_new_var_field_data_ptr += num_blob_bytes;
|
|
}
|
|
new_val.data = new_val_data;
|
|
new_val.size = curr_new_var_field_data_ptr - new_val_data;
|
|
set_val(&new_val, set_extra);
|
|
|
|
error = 0;
|
|
cleanup:
|
|
my_free(new_val_data, MYF(MY_ALLOW_ZERO_PTR));
|
|
return error;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
class ha_tokudb_add_index_ctx : public inplace_alter_handler_ctx {
|
|
public:
|
|
ha_tokudb_add_index_ctx(bool _incremented_num_DBs, bool _modified_DBs) : incremented_num_DBs(_incremented_num_DBs), modified_DBs(_modified_DBs) {
|
|
}
|
|
virtual ~ha_tokudb_add_index_ctx() {
|
|
}
|
|
public:
|
|
bool incremented_num_DBs, modified_DBs;
|
|
};
|
|
|
|
enum_alter_inplace_result
|
|
ha_tokudb::check_if_supported_inplace_alter(TABLE *altered_table, Alter_inplace_info *ha_alter_info) {
|
|
TOKUDB_DBUG_ENTER("check_if_supported_alter");
|
|
|
|
if (tokudb_debug & TOKUDB_DEBUG_ALTER_TABLE_INFO) {
|
|
print_alter_info(altered_table, ha_alter_info);
|
|
}
|
|
|
|
THD *thd = ha_thd();
|
|
enum_alter_inplace_result result = HA_ALTER_INPLACE_NOT_SUPPORTED; // default is NOT inplace
|
|
|
|
// column rename
|
|
if ((ha_alter_info->handler_flags & ~(Alter_inplace_info::ALTER_COLUMN_NAME + Alter_inplace_info::ALTER_COLUMN_DEFAULT)) == 0) {
|
|
// we have identified a possible column rename,
|
|
// but let's do some more checks
|
|
|
|
// we will only allow an hcr if there are no changes
|
|
// in column positions
|
|
#if 0 // TODO
|
|
if (alter_info->contains_first_or_after) {
|
|
result = HA_ALTER_INPLACE_NOT_SUPPORTED;
|
|
} else
|
|
#endif
|
|
{
|
|
// 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);
|
|
if (cr_supported)
|
|
result = HA_ALTER_INPLACE_NO_LOCK;
|
|
}
|
|
} else
|
|
// add index
|
|
if (ha_alter_info->handler_flags == Alter_inplace_info::ADD_INDEX ||
|
|
ha_alter_info->handler_flags == Alter_inplace_info::ADD_UNIQUE_INDEX) { // && tables_have_same_keys TODO???
|
|
assert(ha_alter_info->index_drop_count == 0);
|
|
result = HA_ALTER_INPLACE_SHARED_LOCK;
|
|
// TODO allow multiple hot indexes via alter table add key. don't forget to change the store_lock function.x
|
|
if (get_create_index_online(thd) && ha_alter_info->index_add_count == 1 && thd_sql_command(thd) == SQLCOM_CREATE_INDEX)
|
|
result = HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE;
|
|
} else
|
|
// drop index
|
|
if (ha_alter_info->handler_flags == Alter_inplace_info::DROP_INDEX ||
|
|
ha_alter_info->handler_flags == Alter_inplace_info::DROP_UNIQUE_INDEX) { // && tables_have_same_keys TODO???
|
|
assert(ha_alter_info->index_add_count == 0);
|
|
result = HA_ALTER_INPLACE_EXCLUSIVE_LOCK;;
|
|
} else
|
|
// add column
|
|
if (ha_alter_info->handler_flags == Alter_inplace_info::ADD_COLUMN ||
|
|
ha_alter_info->handler_flags == Alter_inplace_info::ADD_COLUMN + Alter_inplace_info::ALTER_COLUMN_ORDER) {
|
|
u_int32_t added_columns[altered_table->s->fields];
|
|
u_int32_t num_added_columns = 0;
|
|
int r = find_changed_columns(added_columns, &num_added_columns, table, altered_table);
|
|
if (r == 0) {
|
|
if (tokudb_debug & TOKUDB_DEBUG_ALTER_TABLE_INFO) {
|
|
for (u_int32_t i = 0; i < num_added_columns; i++) {
|
|
u_int32_t curr_added_index = added_columns[i];
|
|
Field* curr_added_field = altered_table->field[curr_added_index];
|
|
printf("Added column: index %d, name %s\n", curr_added_index, curr_added_field->field_name);
|
|
}
|
|
}
|
|
result = HA_ALTER_INPLACE_EXCLUSIVE_LOCK;
|
|
}
|
|
} else
|
|
// drop column
|
|
if (ha_alter_info->handler_flags == Alter_inplace_info::DROP_COLUMN ||
|
|
ha_alter_info->handler_flags == Alter_inplace_info::DROP_COLUMN + Alter_inplace_info::ALTER_COLUMN_ORDER) {
|
|
u_int32_t dropped_columns[table->s->fields];
|
|
u_int32_t num_dropped_columns = 0;
|
|
int r = find_changed_columns(dropped_columns, &num_dropped_columns, altered_table, table);
|
|
if (r == 0) {
|
|
if (tokudb_debug & TOKUDB_DEBUG_ALTER_TABLE_INFO) {
|
|
for (u_int32_t i = 0; i < num_dropped_columns; i++) {
|
|
u_int32_t curr_dropped_index = dropped_columns[i];
|
|
Field* curr_dropped_field = table->field[curr_dropped_index];
|
|
printf("Dropped column: index %d, name %s\n", curr_dropped_index, curr_dropped_field->field_name);
|
|
}
|
|
}
|
|
result = HA_ALTER_INPLACE_EXCLUSIVE_LOCK;
|
|
}
|
|
}
|
|
|
|
if (result == HA_ALTER_INPLACE_NOT_SUPPORTED && get_disable_slow_alter(thd)) {
|
|
print_error(HA_ERR_UNSUPPORTED, MYF(0));
|
|
result = HA_ALTER_ERROR;
|
|
}
|
|
|
|
DBUG_RETURN(result);
|
|
}
|
|
|
|
bool
|
|
ha_tokudb::prepare_inplace_alter_table(TABLE *altered_table, Alter_inplace_info *ha_alter_info) {
|
|
TOKUDB_DBUG_ENTER("prepare_inplace_alter_table");
|
|
|
|
bool result = false; // success
|
|
|
|
DBUG_RETURN(result);
|
|
}
|
|
|
|
bool
|
|
ha_tokudb::inplace_alter_table(TABLE *altered_table, Alter_inplace_info *ha_alter_info) {
|
|
TOKUDB_DBUG_ENTER("inplace_alter_table");
|
|
|
|
int error = 0;
|
|
|
|
if (ha_alter_info->handler_flags == Alter_inplace_info::ADD_INDEX ||
|
|
ha_alter_info->handler_flags == Alter_inplace_info::ADD_UNIQUE_INDEX) {
|
|
error = alter_table_add_index(altered_table, ha_alter_info);
|
|
} else
|
|
if (ha_alter_info->handler_flags == Alter_inplace_info::DROP_INDEX ||
|
|
ha_alter_info->handler_flags == Alter_inplace_info::DROP_UNIQUE_INDEX) {
|
|
error = alter_table_drop_index(altered_table, ha_alter_info);
|
|
} else
|
|
if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_COLUMN ||
|
|
ha_alter_info->handler_flags & Alter_inplace_info::DROP_COLUMN) {
|
|
error = alter_table_add_or_drop_column(altered_table, ha_alter_info);
|
|
}
|
|
|
|
bool result = false; // success
|
|
if (error) {
|
|
print_error(error, MYF(0));
|
|
result = true; // failure
|
|
}
|
|
|
|
DBUG_RETURN(result);
|
|
}
|
|
|
|
int
|
|
ha_tokudb::alter_table_add_index(TABLE *altered_table, Alter_inplace_info *ha_alter_info) {
|
|
|
|
// TODO what does this do?
|
|
KEY *key_info = (KEY*) my_malloc(sizeof (KEY) * ha_alter_info->index_add_count, MYF(MY_WME));
|
|
KEY *key = key_info;
|
|
for (uint i = 0; i < ha_alter_info->index_add_count; i++) {
|
|
*key = ha_alter_info->key_info_buffer[ha_alter_info->index_add_buffer[i]];
|
|
for (KEY_PART_INFO *key_part= key->key_part; key_part < key->key_part + key->key_parts; key_part++)
|
|
key_part->field = table->field[key_part->fieldnr];
|
|
}
|
|
|
|
bool incremented_num_DBs = false;
|
|
bool modified_DBs = false;
|
|
int error = tokudb_add_index(table, key_info, ha_alter_info->index_add_count, transaction, &incremented_num_DBs, &modified_DBs);
|
|
if (error == HA_ERR_FOUND_DUPP_KEY) {
|
|
// hack for now, in case of duplicate key error,
|
|
// because at the moment we cannot display the right key
|
|
// information to the user, so that he knows potentially what went
|
|
// wrong.
|
|
last_dup_key = MAX_KEY;
|
|
}
|
|
|
|
assert(ha_alter_info->handler_ctx == NULL);
|
|
ha_alter_info->handler_ctx = new ha_tokudb_add_index_ctx(incremented_num_DBs, modified_DBs);
|
|
assert(ha_alter_info->handler_ctx);
|
|
|
|
my_free(key_info);
|
|
|
|
return error;
|
|
}
|
|
|
|
int
|
|
ha_tokudb::alter_table_drop_index(TABLE *altered_table, Alter_inplace_info *ha_alter_info) {
|
|
// translate KEY pointers to indexes into the key_info array
|
|
uint index_drop_offsets[ha_alter_info->index_drop_count];
|
|
for (uint i = 0; i < ha_alter_info->index_drop_count; i++)
|
|
index_drop_offsets[i] = ha_alter_info->index_drop_buffer[i] - table->key_info;
|
|
|
|
// drop indexes
|
|
int error = drop_indexes(table, index_drop_offsets, ha_alter_info->index_drop_count, transaction);
|
|
|
|
return error;
|
|
}
|
|
|
|
int
|
|
ha_tokudb::alter_table_add_or_drop_column(TABLE *altered_table, Alter_inplace_info *ha_alter_info) {
|
|
int error;
|
|
uchar *column_extra = NULL;
|
|
uchar *row_desc_buff = NULL;
|
|
u_int32_t max_new_desc_size = 0;
|
|
u_int32_t max_column_extra_size;
|
|
u_int32_t num_column_extra;
|
|
u_int32_t num_columns = 0;
|
|
u_int32_t curr_num_DBs = table->s->keys + test(hidden_primary_key);
|
|
|
|
u_int32_t columns[table->s->fields + altered_table->s->fields]; // set size such that we know it is big enough for both cases
|
|
memset(columns, 0, sizeof(columns));
|
|
|
|
KEY_AND_COL_INFO altered_kc_info;
|
|
memset(&altered_kc_info, 0, sizeof(altered_kc_info));
|
|
|
|
error = allocate_key_and_col_info(altered_table->s, &altered_kc_info);
|
|
if (error) { goto cleanup; }
|
|
|
|
max_new_desc_size = get_max_desc_size(&altered_kc_info, altered_table);
|
|
row_desc_buff = (uchar *)my_malloc(max_new_desc_size, MYF(MY_WME));
|
|
if (row_desc_buff == NULL){ error = ENOMEM; goto cleanup;}
|
|
|
|
error = initialize_key_and_col_info(
|
|
altered_table->s,
|
|
altered_table,
|
|
&altered_kc_info,
|
|
hidden_primary_key,
|
|
primary_key
|
|
);
|
|
if (error) { goto cleanup; }
|
|
|
|
// generate the array of columns
|
|
if (ha_alter_info->handler_flags & Alter_inplace_info::DROP_COLUMN) {
|
|
find_changed_columns(
|
|
columns,
|
|
&num_columns,
|
|
altered_table,
|
|
table
|
|
);
|
|
} else
|
|
if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_COLUMN) {
|
|
find_changed_columns(
|
|
columns,
|
|
&num_columns,
|
|
table,
|
|
altered_table
|
|
);
|
|
} else
|
|
assert(0);
|
|
max_column_extra_size =
|
|
STATIC_ROW_MUTATOR_SIZE + //max static row_mutator
|
|
4 + num_columns*(1+1+4+1+1+4) + altered_table->s->reclength + // max dynamic row_mutator
|
|
(4 + share->kc_info.num_blobs) + // max static blob size
|
|
(num_columns*(1+4+1+4)); // max dynamic blob size
|
|
column_extra = (uchar *)my_malloc(max_column_extra_size, MYF(MY_WME));
|
|
if (column_extra == NULL) { error = ENOMEM; goto cleanup; }
|
|
|
|
for (u_int32_t i = 0; i < curr_num_DBs; i++) {
|
|
DBT row_descriptor;
|
|
memset(&row_descriptor, 0, sizeof(row_descriptor));
|
|
KEY* prim_key = (hidden_primary_key) ? NULL : &altered_table->s->key_info[primary_key];
|
|
KEY* key_info = &altered_table->key_info[i];
|
|
if (i == primary_key) {
|
|
row_descriptor.size = create_main_key_descriptor(
|
|
row_desc_buff,
|
|
prim_key,
|
|
hidden_primary_key,
|
|
primary_key,
|
|
altered_table,
|
|
&altered_kc_info
|
|
);
|
|
row_descriptor.data = row_desc_buff;
|
|
}
|
|
else {
|
|
row_descriptor.size = create_secondary_key_descriptor(
|
|
row_desc_buff,
|
|
key_info,
|
|
prim_key,
|
|
hidden_primary_key,
|
|
altered_table,
|
|
primary_key,
|
|
i,
|
|
&altered_kc_info
|
|
);
|
|
row_descriptor.data = row_desc_buff;
|
|
}
|
|
error = share->key_file[i]->change_descriptor(
|
|
share->key_file[i],
|
|
transaction,
|
|
&row_descriptor,
|
|
0
|
|
);
|
|
if (error) { goto cleanup; }
|
|
|
|
if (i == primary_key || table_share->key_info[i].flags & HA_CLUSTERING) {
|
|
num_column_extra = fill_row_mutator(
|
|
column_extra,
|
|
columns,
|
|
num_columns,
|
|
altered_table,
|
|
&altered_kc_info,
|
|
i,
|
|
(ha_alter_info->handler_flags & Alter_inplace_info::ADD_COLUMN) != 0 // true if adding columns, otherwise is a drop
|
|
);
|
|
|
|
DBT column_dbt;
|
|
memset(&column_dbt, 0, sizeof column_dbt);
|
|
column_dbt.data = column_extra;
|
|
column_dbt.size = num_column_extra;
|
|
DBUG_ASSERT(num_column_extra <= max_column_extra_size);
|
|
error = share->key_file[i]->update_broadcast(
|
|
share->key_file[i],
|
|
transaction,
|
|
&column_dbt,
|
|
DB_IS_RESETTING_OP
|
|
);
|
|
if (error) { goto cleanup; }
|
|
}
|
|
}
|
|
|
|
error = 0;
|
|
cleanup:
|
|
free_key_and_col_info(&altered_kc_info);
|
|
my_free(row_desc_buff, MYF(MY_ALLOW_ZERO_PTR));
|
|
my_free(column_extra, MYF(MY_ALLOW_ZERO_PTR));
|
|
return error;
|
|
}
|
|
|
|
bool
|
|
ha_tokudb::commit_inplace_alter_table(TABLE *altered_table, Alter_inplace_info *ha_alter_info, bool commit) {
|
|
TOKUDB_DBUG_ENTER("commit_inplace_alter_table");
|
|
|
|
bool result = false; // success
|
|
|
|
if (commit) {
|
|
if (altered_table->part_info == NULL) {
|
|
// read frmdata for the altered table
|
|
uchar *frm_data; size_t frm_len;
|
|
int error = readfrm(altered_table->s->path.str, &frm_data, &frm_len);
|
|
if (error) {
|
|
result = true;
|
|
} else {
|
|
// transactionally write frmdata to status
|
|
assert(transaction);
|
|
error = write_to_status(share->status_block, hatoku_frm_data, (void *)frm_data, (uint)frm_len, transaction);
|
|
if (error) {
|
|
result = true;
|
|
}
|
|
|
|
my_free(frm_data);
|
|
}
|
|
if (error)
|
|
print_error(error, MYF(0));
|
|
}
|
|
}
|
|
|
|
if (!commit || result == true) {
|
|
if (ha_alter_info->handler_flags == Alter_inplace_info::ADD_INDEX ||
|
|
ha_alter_info->handler_flags == Alter_inplace_info::ADD_UNIQUE_INDEX) {
|
|
ha_tokudb_add_index_ctx *ctx = static_cast<ha_tokudb_add_index_ctx *>(ha_alter_info->handler_ctx);
|
|
assert(ctx);
|
|
restore_add_index(table, ha_alter_info->index_add_count, ctx->incremented_num_DBs, ctx->modified_DBs);
|
|
} else
|
|
if (ha_alter_info->handler_flags == Alter_inplace_info::DROP_INDEX ||
|
|
ha_alter_info->handler_flags == Alter_inplace_info::DROP_UNIQUE_INDEX) {
|
|
// translate KEY pointers to indexes into the key_info array
|
|
uint index_drop_offsets[ha_alter_info->index_drop_count];
|
|
for (uint i = 0; i < ha_alter_info->index_drop_count; i++)
|
|
index_drop_offsets[i] = ha_alter_info->index_drop_buffer[i] - table->key_info;
|
|
|
|
restore_drop_indexes(table, index_drop_offsets, ha_alter_info->index_drop_count);
|
|
}
|
|
}
|
|
|
|
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
|