Commit 435df185 authored by unknown's avatar unknown

Bug#26827 - table->read_set is set incorrectly,

          causing update of a different column

For efficiency some storage engines do not read a complete record
for update, but only the columns required for selecting the rows.

When updating a row of a partitioned table, modifying a column
that is part of the partition or subpartition expression, then
the row may need to move from one [sub]partition to another one.
This is done by inserting the new row into the target
[sub]partition and deleting the old row from the originating one.
For the insert we need a complete record.

If an above mentioned engine was used for a partitioned table, we
did not have a complete record in update_row(). The implicitly
executed write_row() got an incomplete record.

This is solved by instructing the engine to read a complete record
if one of the columns of the partition or subpartiton is to be
updated.

No testcase. This can be reproduced with Falcon only. The engines
contained in standard 5.1 do always return complete records on
update.


sql/ha_partition.cc:
  Bug#26827 - table->read_set is set incorrectly,
              causing update of a different column
  Setting partition field bits in read_set if a writing
  operation is going on. This replaces the old function
  include_partition_fields_in_used_fields().
  Setting all bits in read_set if write_set contains a column
  used in a partition or subpartition expression.
  Removed include_partition_fields_in_used_fields().
sql/ha_partition.h:
  Bug#26827 - table->read_set is set incorrectly,
              causing update of a different column
  Removed declaration of
  include_partition_fields_in_used_fields().
sql/partition_info.h:
  Bug#26827 - table->read_set is set incorrectly,
              causing update of a different column
  Added a bitmap to partition_info for a quick check of
  columns used in a partition or subpartition expression.
sql/sql_partition.cc:
  Bug#26827 - table->read_set is set incorrectly,
              causing update of a different column
  Initializing the new bitmap with all columns used in a
  partition or subpartition expression.
parent 1490e79d
......@@ -2961,8 +2961,34 @@ int ha_partition::rnd_init(bool scan)
uint32 part_id;
DBUG_ENTER("ha_partition::rnd_init");
include_partition_fields_in_used_fields();
/*
For operations that may need to change data, we may need to extend
read_set.
*/
if (m_lock_type == F_WRLCK)
{
/*
If write_set contains any of the fields used in partition and
subpartition expression, we need to set all bits in read_set because
the row may need to be inserted in a different [sub]partition. In
other words update_row() can be converted into write_row(), which
requires a complete record.
*/
if (bitmap_is_overlapping(&m_part_info->full_part_field_set,
table->write_set))
bitmap_set_all(table->read_set);
else
{
/*
Some handlers only read fields as specified by the bitmap for the
read set. For partitioned handlers we always require that the
fields of the partition functions are read such that we can
calculate the partition id to place updated and deleted records.
*/
bitmap_union(table->read_set, &m_part_info->full_part_field_set);
}
}
/* Now we see what the index of our first important partition is */
DBUG_PRINT("info", ("m_part_info->used_partitions: 0x%lx",
(long) m_part_info->used_partitions.bitmap));
......@@ -3276,7 +3302,15 @@ int ha_partition::index_init(uint inx, bool sorted)
m_start_key.length= 0;
m_ordered= sorted;
m_curr_key_info= table->key_info+inx;
include_partition_fields_in_used_fields();
/*
Some handlers only read fields as specified by the bitmap for the
read set. For partitioned handlers we always require that the
fields of the partition functions are read such that we can
calculate the partition id to place updated and deleted records.
But this is required for operations that may need to change data only.
*/
if (m_lock_type == F_WRLCK)
bitmap_union(table->read_set, &m_part_info->full_part_field_set);
file= m_file;
do
{
......@@ -4145,35 +4179,6 @@ int ha_partition::handle_ordered_prev(uchar *buf)
}
/*
Set fields in partition functions in read set for underlying handlers
SYNOPSIS
include_partition_fields_in_used_fields()
RETURN VALUE
NONE
DESCRIPTION
Some handlers only read fields as specified by the bitmap for the
read set. For partitioned handlers we always require that the
fields of the partition functions are read such that we can
calculate the partition id to place updated and deleted records.
*/
void ha_partition::include_partition_fields_in_used_fields()
{
Field **ptr= m_part_field_array;
DBUG_ENTER("ha_partition::include_partition_fields_in_used_fields");
do
{
bitmap_set_bit(table->read_set, (*ptr)->field_index);
} while (*(++ptr));
DBUG_VOID_RETURN;
}
/****************************************************************************
MODULE information calls
****************************************************************************/
......
......@@ -449,7 +449,6 @@ class ha_partition :public handler
int handle_ordered_next(uchar * buf, bool next_same);
int handle_ordered_prev(uchar * buf);
void return_top_record(uchar * buf);
void include_partition_fields_in_used_fields();
public:
/*
-------------------------------------------------------------------------
......
......@@ -81,6 +81,13 @@ class partition_info : public Sql_alloc
*/
Field **full_part_field_array;
Field **full_part_charset_field_array;
/*
Set of all fields used in partition and subpartition expression.
Required for testing of partition fields in write_set when
updating. We need to set all bits in read_set because the row may
need to be inserted in a different [sub]partition.
*/
MY_BITMAP full_part_field_set;
/*
When we have a field that requires transformation before calling the
......
......@@ -523,6 +523,7 @@ static bool set_up_field_array(TABLE *table,
SYNOPSIS
create_full_part_field_array()
thd Thread handle
table TABLE object for which partition fields are set-up
part_info Reference to partitioning data structure
......@@ -537,11 +538,12 @@ static bool set_up_field_array(TABLE *table,
This function is called from fix_partition_func
*/
static bool create_full_part_field_array(TABLE *table,
static bool create_full_part_field_array(THD *thd, TABLE *table,
partition_info *part_info)
{
bool result= FALSE;
Field **ptr;
my_bitmap_map *bitmap_buf;
DBUG_ENTER("create_full_part_field_array");
if (!part_info->is_sub_partitioned())
......@@ -578,6 +580,35 @@ static bool create_full_part_field_array(TABLE *table,
part_info->full_part_field_array= field_array;
part_info->no_full_part_fields= no_part_fields;
}
/*
Initialize the set of all fields used in partition and subpartition
expression. Required for testing of partition fields in write_set
when updating. We need to set all bits in read_set because the row
may need to be inserted in a different [sub]partition.
*/
if (!(bitmap_buf= (my_bitmap_map*)
thd->alloc(bitmap_buffer_size(table->s->fields))))
{
mem_alloc_error(bitmap_buffer_size(table->s->fields));
result= TRUE;
goto end;
}
if (bitmap_init(&part_info->full_part_field_set, bitmap_buf,
table->s->fields, FALSE))
{
mem_alloc_error(table->s->fields);
result= TRUE;
goto end;
}
/*
full_part_field_array may be NULL if storage engine supports native
partitioning.
*/
if ((ptr= part_info->full_part_field_array))
while (*ptr)
bitmap_set_bit(&part_info->full_part_field_set, (*ptr++)->field_index);
end:
DBUG_RETURN(result);
}
......@@ -1636,7 +1667,7 @@ bool fix_partition_func(THD *thd, TABLE *table,
my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
goto end;
}
if (unlikely(create_full_part_field_array(table, part_info)))
if (unlikely(create_full_part_field_array(thd, table, part_info)))
goto end;
if (unlikely(check_primary_key(table)))
goto end;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment