You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

7273 lines
241 KiB

WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
Bug #45807: crash accessing partitioned table and sql_mode contains ONLY_FULL_GROUP_BY The partitioning code needs to issue a Item::fix_fields() on the partitioning expression in order to prepare it for being evaluated. It does this by creating a special table and a table list for the scope of the partitioning expression. But when checking ONLY_FULL_GROUP_BY the Item_field::fix_fields() was relying that there always be cached_table set and was trying to use it to get the select_lex of the SELECT the field's table is in. But the cached_table was not set by the partitioning code that creates the artificial TABLE_LIST used to resolve the partitioning expression and this resulted in a crash. Fixed by rectifying the following errors : 1. Item_field::fix_fields() : the code that check for ONLY_FULL_GROUP_BY relies on having tables with cacheable_table set. This is mostly true, the only two exceptions being the partitioning context table and the trigger context table. Fixed by taking the current parsing context if no pointer to the TABLE_LIST instance is present in the cached_table. 2. fix_fields_part_func() : 2a. The code that adds the table being created to the scope for the partitioning expression is mostly a copy of the add_table_to_list and friends with one exception : it was not marking the table as cacheable (something that normal add_table_to_list is doing). This caused the problem in the check for ONLY_FULL_GROUP_BY in Item_field::fix_fields() to appear. Fixed by setting the correct members to make the table cacheable. The ideal structural fix for this is to use a unified interface for adding a table to a table list (add_table_to_list?) : noted in a TODO comment 2b. The Item::fix_fields() was called with a NULL destination pointer. This causes uninitalized memory reads in the overloaded ::fix_fields() function (namely Item_field::fix_fields()) as it expects a non-zero pointer there. Fixed by passing the source pointer similarly to how it's done in JOIN::prepare().
17 years ago
Bug #45807: crash accessing partitioned table and sql_mode contains ONLY_FULL_GROUP_BY The partitioning code needs to issue a Item::fix_fields() on the partitioning expression in order to prepare it for being evaluated. It does this by creating a special table and a table list for the scope of the partitioning expression. But when checking ONLY_FULL_GROUP_BY the Item_field::fix_fields() was relying that there always be cached_table set and was trying to use it to get the select_lex of the SELECT the field's table is in. But the cached_table was not set by the partitioning code that creates the artificial TABLE_LIST used to resolve the partitioning expression and this resulted in a crash. Fixed by rectifying the following errors : 1. Item_field::fix_fields() : the code that check for ONLY_FULL_GROUP_BY relies on having tables with cacheable_table set. This is mostly true, the only two exceptions being the partitioning context table and the trigger context table. Fixed by taking the current parsing context if no pointer to the TABLE_LIST instance is present in the cached_table. 2. fix_fields_part_func() : 2a. The code that adds the table being created to the scope for the partitioning expression is mostly a copy of the add_table_to_list and friends with one exception : it was not marking the table as cacheable (something that normal add_table_to_list is doing). This caused the problem in the check for ONLY_FULL_GROUP_BY in Item_field::fix_fields() to appear. Fixed by setting the correct members to make the table cacheable. The ideal structural fix for this is to use a unified interface for adding a table to a table list (add_table_to_list?) : noted in a TODO comment 2b. The Item::fix_fields() was called with a NULL destination pointer. This causes uninitalized memory reads in the overloaded ::fix_fields() function (namely Item_field::fix_fields()) as it expects a non-zero pointer there. Fixed by passing the source pointer similarly to how it's done in JOIN::prepare().
17 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
20 years ago
20 years ago
20 years ago
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes Changes that requires code changes in other code of other storage engines. (Note that all changes are very straightforward and one should find all issues by compiling a --debug build and fixing all compiler errors and all asserts in field.cc while running the test suite), - New optional handler function introduced: reset() This is called after every DML statement to make it easy for a handler to statement specific cleanups. (The only case it's not called is if force the file to be closed) - handler::extra(HA_EXTRA_RESET) is removed. Code that was there before should be moved to handler::reset() - table->read_set contains a bitmap over all columns that are needed in the query. read_row() and similar functions only needs to read these columns - table->write_set contains a bitmap over all columns that will be updated in the query. write_row() and update_row() only needs to update these columns. The above bitmaps should now be up to date in all context (including ALTER TABLE, filesort()). The handler is informed of any changes to the bitmap after fix_fields() by calling the virtual function handler::column_bitmaps_signal(). If the handler does caching of these bitmaps (instead of using table->read_set, table->write_set), it should redo the caching in this code. as the signal() may be sent several times, it's probably best to set a variable in the signal and redo the caching on read_row() / write_row() if the variable was set. - Removed the read_set and write_set bitmap objects from the handler class - Removed all column bit handling functions from the handler class. (Now one instead uses the normal bitmap functions in my_bitmap.c instead of handler dedicated bitmap functions) - field->query_id is removed. One should instead instead check table->read_set and table->write_set if a field is used in the query. - handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now instead use table->read_set to check for which columns to retrieve. - If a handler needs to call Field->val() or Field->store() on columns that are not used in the query, one should install a temporary all-columns-used map while doing so. For this, we provide the following functions: my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); field->val(); dbug_tmp_restore_column_map(table->read_set, old_map); and similar for the write map: my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); field->val(); dbug_tmp_restore_column_map(table->write_set, old_map); If this is not done, you will sooner or later hit a DBUG_ASSERT in the field store() / val() functions. (For not DBUG binaries, the dbug_tmp_restore_column_map() and dbug_tmp_restore_column_map() are inline dummy functions and should be optimized away be the compiler). - If one needs to temporary set the column map for all binaries (and not just to avoid the DBUG_ASSERT() in the Field::store() / Field::val() methods) one should use the functions tmp_use_all_columns() and tmp_restore_column_map() instead of the above dbug_ variants. - All 'status' fields in the handler base class (like records, data_file_length etc) are now stored in a 'stats' struct. This makes it easier to know what status variables are provided by the base handler. This requires some trivial variable names in the extra() function. - New virtual function handler::records(). This is called to optimize COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true. (stats.records is not supposed to be an exact value. It's only has to be 'reasonable enough' for the optimizer to be able to choose a good optimization path). - Non virtual handler::init() function added for caching of virtual constants from engine. - Removed has_transactions() virtual method. Now one should instead return HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support transactions. - The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument that is to be used with 'new handler_name()' to allocate the handler in the right area. The xxxx_create_handler() function is also responsible for any initialization of the object before returning. For example, one should change: static handler *myisam_create_handler(TABLE_SHARE *table) { return new ha_myisam(table); } -> static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root) { return new (mem_root) ha_myisam(table); } - New optional virtual function: use_hidden_primary_key(). This is called in case of an update/delete when (table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined but we don't have a primary key. This allows the handler to take precisions in remembering any hidden primary key to able to update/delete any found row. The default handler marks all columns to be read. - handler::table_flags() now returns a ulonglong (to allow for more flags). - New/changed table_flags() - HA_HAS_RECORDS Set if ::records() is supported - HA_NO_TRANSACTIONS Set if engine doesn't support transactions - HA_PRIMARY_KEY_REQUIRED_FOR_DELETE Set if we should mark all primary key columns for read when reading rows as part of a DELETE statement. If there is no primary key, all columns are marked for read. - HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some cases (based on table->read_set) - HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION. - HA_DUPP_POS Renamed to HA_DUPLICATE_POS - HA_REQUIRES_KEY_COLUMNS_FOR_DELETE Set this if we should mark ALL key columns for read when when reading rows as part of a DELETE statement. In case of an update we will mark all keys for read for which key part changed value. - HA_STATS_RECORDS_IS_EXACT Set this if stats.records is exact. (This saves us some extra records() calls when optimizing COUNT(*)) - Removed table_flags() - HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if handler::records() gives an exact count() and HA_STATS_RECORDS_IS_EXACT if stats.records is exact. - HA_READ_RND_SAME Removed (no one supported this one) - Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk() - Renamed handler::dupp_pos to handler::dup_pos - Removed not used variable handler::sortkey Upper level handler changes: - ha_reset() now does some overall checks and calls ::reset() - ha_table_flags() added. This is a cached version of table_flags(). The cache is updated on engine creation time and updated on open. MySQL level changes (not obvious from the above): - DBUG_ASSERT() added to check that column usage matches what is set in the column usage bit maps. (This found a LOT of bugs in current column marking code). - In 5.1 before, all used columns was marked in read_set and only updated columns was marked in write_set. Now we only mark columns for which we need a value in read_set. - Column bitmaps are created in open_binary_frm() and open_table_from_share(). (Before this was in table.cc) - handler::table_flags() calls are replaced with handler::ha_table_flags() - For calling field->val() you must have the corresponding bit set in table->read_set. For calling field->store() you must have the corresponding bit set in table->write_set. (There are asserts in all store()/val() functions to catch wrong usage) - thd->set_query_id is renamed to thd->mark_used_columns and instead of setting this to an integer value, this has now the values: MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE Changed also all variables named 'set_query_id' to mark_used_columns. - In filesort() we now inform the handler of exactly which columns are needed doing the sort and choosing the rows. - The TABLE_SHARE object has a 'all_set' column bitmap one can use when one needs a column bitmap with all columns set. (This is used for table->use_all_columns() and other places) - The TABLE object has 3 column bitmaps: - def_read_set Default bitmap for columns to be read - def_write_set Default bitmap for columns to be written - tmp_set Can be used as a temporary bitmap when needed. The table object has also two pointer to bitmaps read_set and write_set that the handler should use to find out which columns are used in which way. - count() optimization now calls handler::records() instead of using handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true). - Added extra argument to Item::walk() to indicate if we should also traverse sub queries. - Added TABLE parameter to cp_buffer_from_ref() - Don't close tables created with CREATE ... SELECT but keep them in the table cache. (Faster usage of newly created tables). New interfaces: - table->clear_column_bitmaps() to initialize the bitmaps for tables at start of new statements. - table->column_bitmaps_set() to set up new column bitmaps and signal the handler about this. - table->column_bitmaps_set_no_signal() for some few cases where we need to setup new column bitmaps but don't signal the handler (as the handler has already been signaled about these before). Used for the momement only in opt_range.cc when doing ROR scans. - table->use_all_columns() to install a bitmap where all columns are marked as use in the read and the write set. - table->default_column_bitmaps() to install the normal read and write column bitmaps, but not signaling the handler about this. This is mainly used when creating TABLE instances. - table->mark_columns_needed_for_delete(), table->mark_columns_needed_for_delete() and table->mark_columns_needed_for_insert() to allow us to put additional columns in column usage maps if handler so requires. (The handler indicates what it neads in handler->table_flags()) - table->prepare_for_position() to allow us to tell handler that it needs to read primary key parts to be able to store them in future table->position() calls. (This replaces the table->file->ha_retrieve_all_pk function) - table->mark_auto_increment_column() to tell handler are going to update columns part of any auto_increment key. - table->mark_columns_used_by_index() to mark all columns that is part of an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow it to quickly know that it only needs to read colums that are part of the key. (The handler can also use the column map for detecting this, but simpler/faster handler can just monitor the extra() call). - table->mark_columns_used_by_index_no_reset() to in addition to other columns, also mark all columns that is used by the given key. - table->restore_column_maps_after_mark_index() to restore to default column maps after a call to table->mark_columns_used_by_index(). - New item function register_field_in_read_map(), for marking used columns in table->read_map. Used by filesort() to mark all used columns - Maintain in TABLE->merge_keys set of all keys that are used in query. (Simplices some optimization loops) - Maintain Field->part_of_key_not_clustered which is like Field->part_of_key but the field in the clustered key is not assumed to be part of all index. (used in opt_range.cc for faster loops) - dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map() tmp_use_all_columns() and tmp_restore_column_map() functions to temporally mark all columns as usable. The 'dbug_' version is primarily intended inside a handler when it wants to just call Field:store() & Field::val() functions, but don't need the column maps set for any other usage. (ie:: bitmap_is_set() is never called) - We can't use compare_records() to skip updates for handlers that returns a partial column set and the read_set doesn't cover all columns in the write set. The reason for this is that if we have a column marked only for write we can't in the MySQL level know if the value changed or not. The reason this worked before was that MySQL marked all to be written columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden bug'. - open_table_from_share() does not anymore setup temporary MEM_ROOT object as a thread specific variable for the handler. Instead we send the to-be-used MEMROOT to get_new_handler(). (Simpler, faster code) Bugs fixed: - Column marking was not done correctly in a lot of cases. (ALTER TABLE, when using triggers, auto_increment fields etc) (Could potentially result in wrong values inserted in table handlers relying on that the old column maps or field->set_query_id was correct) Especially when it comes to triggers, there may be cases where the old code would cause lost/wrong values for NDB and/or InnoDB tables. - Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags: OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG. This allowed me to remove some wrong warnings about: "Some non-transactional changed tables couldn't be rolled back" - Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset (thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose some warnings about "Some non-transactional changed tables couldn't be rolled back") - Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table() which could cause delete_table to report random failures. - Fixed core dumps for some tests when running with --debug - Added missing FN_LIBCHAR in mysql_rm_tmp_tables() (This has probably caused us to not properly remove temporary files after crash) - slow_logs was not properly initialized, which could maybe cause extra/lost entries in slow log. - If we get an duplicate row on insert, change column map to read and write all columns while retrying the operation. This is required by the definition of REPLACE and also ensures that fields that are only part of UPDATE are properly handled. This fixed a bug in NDB and REPLACE where REPLACE wrongly copied some column values from the replaced row. - For table handler that doesn't support NULL in keys, we would give an error when creating a primary key with NULL fields, even after the fields has been automaticly converted to NOT NULL. - Creating a primary key on a SPATIAL key, would fail if field was not declared as NOT NULL. Cleanups: - Removed not used condition argument to setup_tables - Removed not needed item function reset_query_id_processor(). - Field->add_index is removed. Now this is instead maintained in (field->flags & FIELD_IN_ADD_INDEX) - Field->fieldnr is removed (use field->field_index instead) - New argument to filesort() to indicate that it should return a set of row pointers (not used columns). This allowed me to remove some references to sql_command in filesort and should also enable us to return column results in some cases where we couldn't before. - Changed column bitmap handling in opt_range.cc to be aligned with TABLE bitmap, which allowed me to use bitmap functions instead of looping over all fields to create some needed bitmaps. (Faster and smaller code) - Broke up found too long lines - Moved some variable declaration at start of function for better code readability. - Removed some not used arguments from functions. (setup_fields(), mysql_prepare_insert_check_table()) - setup_fields() now takes an enum instead of an int for marking columns usage. - For internal temporary tables, use handler::write_row(), handler::delete_row() and handler::update_row() instead of handler::ha_xxxx() for faster execution. - Changed some constants to enum's and define's. - Using separate column read and write sets allows for easier checking of timestamp field was set by statement. - Remove calls to free_io_cache() as this is now done automaticly in ha_reset() - Don't build table->normalized_path as this is now identical to table->path (after bar's fixes to convert filenames) - Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to do comparision with the 'convert-dbug-for-diff' tool. Things left to do in 5.1: - We wrongly log failed CREATE TABLE ... SELECT in some cases when using row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result) Mats has promised to look into this. - Test that my fix for CREATE TABLE ... SELECT is indeed correct. (I added several test cases for this, but in this case it's better that someone else also tests this throughly). Lars has promosed to do this.
20 years ago
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes Changes that requires code changes in other code of other storage engines. (Note that all changes are very straightforward and one should find all issues by compiling a --debug build and fixing all compiler errors and all asserts in field.cc while running the test suite), - New optional handler function introduced: reset() This is called after every DML statement to make it easy for a handler to statement specific cleanups. (The only case it's not called is if force the file to be closed) - handler::extra(HA_EXTRA_RESET) is removed. Code that was there before should be moved to handler::reset() - table->read_set contains a bitmap over all columns that are needed in the query. read_row() and similar functions only needs to read these columns - table->write_set contains a bitmap over all columns that will be updated in the query. write_row() and update_row() only needs to update these columns. The above bitmaps should now be up to date in all context (including ALTER TABLE, filesort()). The handler is informed of any changes to the bitmap after fix_fields() by calling the virtual function handler::column_bitmaps_signal(). If the handler does caching of these bitmaps (instead of using table->read_set, table->write_set), it should redo the caching in this code. as the signal() may be sent several times, it's probably best to set a variable in the signal and redo the caching on read_row() / write_row() if the variable was set. - Removed the read_set and write_set bitmap objects from the handler class - Removed all column bit handling functions from the handler class. (Now one instead uses the normal bitmap functions in my_bitmap.c instead of handler dedicated bitmap functions) - field->query_id is removed. One should instead instead check table->read_set and table->write_set if a field is used in the query. - handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now instead use table->read_set to check for which columns to retrieve. - If a handler needs to call Field->val() or Field->store() on columns that are not used in the query, one should install a temporary all-columns-used map while doing so. For this, we provide the following functions: my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); field->val(); dbug_tmp_restore_column_map(table->read_set, old_map); and similar for the write map: my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); field->val(); dbug_tmp_restore_column_map(table->write_set, old_map); If this is not done, you will sooner or later hit a DBUG_ASSERT in the field store() / val() functions. (For not DBUG binaries, the dbug_tmp_restore_column_map() and dbug_tmp_restore_column_map() are inline dummy functions and should be optimized away be the compiler). - If one needs to temporary set the column map for all binaries (and not just to avoid the DBUG_ASSERT() in the Field::store() / Field::val() methods) one should use the functions tmp_use_all_columns() and tmp_restore_column_map() instead of the above dbug_ variants. - All 'status' fields in the handler base class (like records, data_file_length etc) are now stored in a 'stats' struct. This makes it easier to know what status variables are provided by the base handler. This requires some trivial variable names in the extra() function. - New virtual function handler::records(). This is called to optimize COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true. (stats.records is not supposed to be an exact value. It's only has to be 'reasonable enough' for the optimizer to be able to choose a good optimization path). - Non virtual handler::init() function added for caching of virtual constants from engine. - Removed has_transactions() virtual method. Now one should instead return HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support transactions. - The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument that is to be used with 'new handler_name()' to allocate the handler in the right area. The xxxx_create_handler() function is also responsible for any initialization of the object before returning. For example, one should change: static handler *myisam_create_handler(TABLE_SHARE *table) { return new ha_myisam(table); } -> static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root) { return new (mem_root) ha_myisam(table); } - New optional virtual function: use_hidden_primary_key(). This is called in case of an update/delete when (table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined but we don't have a primary key. This allows the handler to take precisions in remembering any hidden primary key to able to update/delete any found row. The default handler marks all columns to be read. - handler::table_flags() now returns a ulonglong (to allow for more flags). - New/changed table_flags() - HA_HAS_RECORDS Set if ::records() is supported - HA_NO_TRANSACTIONS Set if engine doesn't support transactions - HA_PRIMARY_KEY_REQUIRED_FOR_DELETE Set if we should mark all primary key columns for read when reading rows as part of a DELETE statement. If there is no primary key, all columns are marked for read. - HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some cases (based on table->read_set) - HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION. - HA_DUPP_POS Renamed to HA_DUPLICATE_POS - HA_REQUIRES_KEY_COLUMNS_FOR_DELETE Set this if we should mark ALL key columns for read when when reading rows as part of a DELETE statement. In case of an update we will mark all keys for read for which key part changed value. - HA_STATS_RECORDS_IS_EXACT Set this if stats.records is exact. (This saves us some extra records() calls when optimizing COUNT(*)) - Removed table_flags() - HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if handler::records() gives an exact count() and HA_STATS_RECORDS_IS_EXACT if stats.records is exact. - HA_READ_RND_SAME Removed (no one supported this one) - Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk() - Renamed handler::dupp_pos to handler::dup_pos - Removed not used variable handler::sortkey Upper level handler changes: - ha_reset() now does some overall checks and calls ::reset() - ha_table_flags() added. This is a cached version of table_flags(). The cache is updated on engine creation time and updated on open. MySQL level changes (not obvious from the above): - DBUG_ASSERT() added to check that column usage matches what is set in the column usage bit maps. (This found a LOT of bugs in current column marking code). - In 5.1 before, all used columns was marked in read_set and only updated columns was marked in write_set. Now we only mark columns for which we need a value in read_set. - Column bitmaps are created in open_binary_frm() and open_table_from_share(). (Before this was in table.cc) - handler::table_flags() calls are replaced with handler::ha_table_flags() - For calling field->val() you must have the corresponding bit set in table->read_set. For calling field->store() you must have the corresponding bit set in table->write_set. (There are asserts in all store()/val() functions to catch wrong usage) - thd->set_query_id is renamed to thd->mark_used_columns and instead of setting this to an integer value, this has now the values: MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE Changed also all variables named 'set_query_id' to mark_used_columns. - In filesort() we now inform the handler of exactly which columns are needed doing the sort and choosing the rows. - The TABLE_SHARE object has a 'all_set' column bitmap one can use when one needs a column bitmap with all columns set. (This is used for table->use_all_columns() and other places) - The TABLE object has 3 column bitmaps: - def_read_set Default bitmap for columns to be read - def_write_set Default bitmap for columns to be written - tmp_set Can be used as a temporary bitmap when needed. The table object has also two pointer to bitmaps read_set and write_set that the handler should use to find out which columns are used in which way. - count() optimization now calls handler::records() instead of using handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true). - Added extra argument to Item::walk() to indicate if we should also traverse sub queries. - Added TABLE parameter to cp_buffer_from_ref() - Don't close tables created with CREATE ... SELECT but keep them in the table cache. (Faster usage of newly created tables). New interfaces: - table->clear_column_bitmaps() to initialize the bitmaps for tables at start of new statements. - table->column_bitmaps_set() to set up new column bitmaps and signal the handler about this. - table->column_bitmaps_set_no_signal() for some few cases where we need to setup new column bitmaps but don't signal the handler (as the handler has already been signaled about these before). Used for the momement only in opt_range.cc when doing ROR scans. - table->use_all_columns() to install a bitmap where all columns are marked as use in the read and the write set. - table->default_column_bitmaps() to install the normal read and write column bitmaps, but not signaling the handler about this. This is mainly used when creating TABLE instances. - table->mark_columns_needed_for_delete(), table->mark_columns_needed_for_delete() and table->mark_columns_needed_for_insert() to allow us to put additional columns in column usage maps if handler so requires. (The handler indicates what it neads in handler->table_flags()) - table->prepare_for_position() to allow us to tell handler that it needs to read primary key parts to be able to store them in future table->position() calls. (This replaces the table->file->ha_retrieve_all_pk function) - table->mark_auto_increment_column() to tell handler are going to update columns part of any auto_increment key. - table->mark_columns_used_by_index() to mark all columns that is part of an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow it to quickly know that it only needs to read colums that are part of the key. (The handler can also use the column map for detecting this, but simpler/faster handler can just monitor the extra() call). - table->mark_columns_used_by_index_no_reset() to in addition to other columns, also mark all columns that is used by the given key. - table->restore_column_maps_after_mark_index() to restore to default column maps after a call to table->mark_columns_used_by_index(). - New item function register_field_in_read_map(), for marking used columns in table->read_map. Used by filesort() to mark all used columns - Maintain in TABLE->merge_keys set of all keys that are used in query. (Simplices some optimization loops) - Maintain Field->part_of_key_not_clustered which is like Field->part_of_key but the field in the clustered key is not assumed to be part of all index. (used in opt_range.cc for faster loops) - dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map() tmp_use_all_columns() and tmp_restore_column_map() functions to temporally mark all columns as usable. The 'dbug_' version is primarily intended inside a handler when it wants to just call Field:store() & Field::val() functions, but don't need the column maps set for any other usage. (ie:: bitmap_is_set() is never called) - We can't use compare_records() to skip updates for handlers that returns a partial column set and the read_set doesn't cover all columns in the write set. The reason for this is that if we have a column marked only for write we can't in the MySQL level know if the value changed or not. The reason this worked before was that MySQL marked all to be written columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden bug'. - open_table_from_share() does not anymore setup temporary MEM_ROOT object as a thread specific variable for the handler. Instead we send the to-be-used MEMROOT to get_new_handler(). (Simpler, faster code) Bugs fixed: - Column marking was not done correctly in a lot of cases. (ALTER TABLE, when using triggers, auto_increment fields etc) (Could potentially result in wrong values inserted in table handlers relying on that the old column maps or field->set_query_id was correct) Especially when it comes to triggers, there may be cases where the old code would cause lost/wrong values for NDB and/or InnoDB tables. - Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags: OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG. This allowed me to remove some wrong warnings about: "Some non-transactional changed tables couldn't be rolled back" - Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset (thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose some warnings about "Some non-transactional changed tables couldn't be rolled back") - Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table() which could cause delete_table to report random failures. - Fixed core dumps for some tests when running with --debug - Added missing FN_LIBCHAR in mysql_rm_tmp_tables() (This has probably caused us to not properly remove temporary files after crash) - slow_logs was not properly initialized, which could maybe cause extra/lost entries in slow log. - If we get an duplicate row on insert, change column map to read and write all columns while retrying the operation. This is required by the definition of REPLACE and also ensures that fields that are only part of UPDATE are properly handled. This fixed a bug in NDB and REPLACE where REPLACE wrongly copied some column values from the replaced row. - For table handler that doesn't support NULL in keys, we would give an error when creating a primary key with NULL fields, even after the fields has been automaticly converted to NOT NULL. - Creating a primary key on a SPATIAL key, would fail if field was not declared as NOT NULL. Cleanups: - Removed not used condition argument to setup_tables - Removed not needed item function reset_query_id_processor(). - Field->add_index is removed. Now this is instead maintained in (field->flags & FIELD_IN_ADD_INDEX) - Field->fieldnr is removed (use field->field_index instead) - New argument to filesort() to indicate that it should return a set of row pointers (not used columns). This allowed me to remove some references to sql_command in filesort and should also enable us to return column results in some cases where we couldn't before. - Changed column bitmap handling in opt_range.cc to be aligned with TABLE bitmap, which allowed me to use bitmap functions instead of looping over all fields to create some needed bitmaps. (Faster and smaller code) - Broke up found too long lines - Moved some variable declaration at start of function for better code readability. - Removed some not used arguments from functions. (setup_fields(), mysql_prepare_insert_check_table()) - setup_fields() now takes an enum instead of an int for marking columns usage. - For internal temporary tables, use handler::write_row(), handler::delete_row() and handler::update_row() instead of handler::ha_xxxx() for faster execution. - Changed some constants to enum's and define's. - Using separate column read and write sets allows for easier checking of timestamp field was set by statement. - Remove calls to free_io_cache() as this is now done automaticly in ha_reset() - Don't build table->normalized_path as this is now identical to table->path (after bar's fixes to convert filenames) - Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to do comparision with the 'convert-dbug-for-diff' tool. Things left to do in 5.1: - We wrongly log failed CREATE TABLE ... SELECT in some cases when using row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result) Mats has promised to look into this. - Test that my fix for CREATE TABLE ... SELECT is indeed correct. (I added several test cases for this, but in this case it's better that someone else also tests this throughly). Lars has promosed to do this.
20 years ago
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes Changes that requires code changes in other code of other storage engines. (Note that all changes are very straightforward and one should find all issues by compiling a --debug build and fixing all compiler errors and all asserts in field.cc while running the test suite), - New optional handler function introduced: reset() This is called after every DML statement to make it easy for a handler to statement specific cleanups. (The only case it's not called is if force the file to be closed) - handler::extra(HA_EXTRA_RESET) is removed. Code that was there before should be moved to handler::reset() - table->read_set contains a bitmap over all columns that are needed in the query. read_row() and similar functions only needs to read these columns - table->write_set contains a bitmap over all columns that will be updated in the query. write_row() and update_row() only needs to update these columns. The above bitmaps should now be up to date in all context (including ALTER TABLE, filesort()). The handler is informed of any changes to the bitmap after fix_fields() by calling the virtual function handler::column_bitmaps_signal(). If the handler does caching of these bitmaps (instead of using table->read_set, table->write_set), it should redo the caching in this code. as the signal() may be sent several times, it's probably best to set a variable in the signal and redo the caching on read_row() / write_row() if the variable was set. - Removed the read_set and write_set bitmap objects from the handler class - Removed all column bit handling functions from the handler class. (Now one instead uses the normal bitmap functions in my_bitmap.c instead of handler dedicated bitmap functions) - field->query_id is removed. One should instead instead check table->read_set and table->write_set if a field is used in the query. - handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now instead use table->read_set to check for which columns to retrieve. - If a handler needs to call Field->val() or Field->store() on columns that are not used in the query, one should install a temporary all-columns-used map while doing so. For this, we provide the following functions: my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); field->val(); dbug_tmp_restore_column_map(table->read_set, old_map); and similar for the write map: my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); field->val(); dbug_tmp_restore_column_map(table->write_set, old_map); If this is not done, you will sooner or later hit a DBUG_ASSERT in the field store() / val() functions. (For not DBUG binaries, the dbug_tmp_restore_column_map() and dbug_tmp_restore_column_map() are inline dummy functions and should be optimized away be the compiler). - If one needs to temporary set the column map for all binaries (and not just to avoid the DBUG_ASSERT() in the Field::store() / Field::val() methods) one should use the functions tmp_use_all_columns() and tmp_restore_column_map() instead of the above dbug_ variants. - All 'status' fields in the handler base class (like records, data_file_length etc) are now stored in a 'stats' struct. This makes it easier to know what status variables are provided by the base handler. This requires some trivial variable names in the extra() function. - New virtual function handler::records(). This is called to optimize COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true. (stats.records is not supposed to be an exact value. It's only has to be 'reasonable enough' for the optimizer to be able to choose a good optimization path). - Non virtual handler::init() function added for caching of virtual constants from engine. - Removed has_transactions() virtual method. Now one should instead return HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support transactions. - The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument that is to be used with 'new handler_name()' to allocate the handler in the right area. The xxxx_create_handler() function is also responsible for any initialization of the object before returning. For example, one should change: static handler *myisam_create_handler(TABLE_SHARE *table) { return new ha_myisam(table); } -> static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root) { return new (mem_root) ha_myisam(table); } - New optional virtual function: use_hidden_primary_key(). This is called in case of an update/delete when (table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined but we don't have a primary key. This allows the handler to take precisions in remembering any hidden primary key to able to update/delete any found row. The default handler marks all columns to be read. - handler::table_flags() now returns a ulonglong (to allow for more flags). - New/changed table_flags() - HA_HAS_RECORDS Set if ::records() is supported - HA_NO_TRANSACTIONS Set if engine doesn't support transactions - HA_PRIMARY_KEY_REQUIRED_FOR_DELETE Set if we should mark all primary key columns for read when reading rows as part of a DELETE statement. If there is no primary key, all columns are marked for read. - HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some cases (based on table->read_set) - HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION. - HA_DUPP_POS Renamed to HA_DUPLICATE_POS - HA_REQUIRES_KEY_COLUMNS_FOR_DELETE Set this if we should mark ALL key columns for read when when reading rows as part of a DELETE statement. In case of an update we will mark all keys for read for which key part changed value. - HA_STATS_RECORDS_IS_EXACT Set this if stats.records is exact. (This saves us some extra records() calls when optimizing COUNT(*)) - Removed table_flags() - HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if handler::records() gives an exact count() and HA_STATS_RECORDS_IS_EXACT if stats.records is exact. - HA_READ_RND_SAME Removed (no one supported this one) - Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk() - Renamed handler::dupp_pos to handler::dup_pos - Removed not used variable handler::sortkey Upper level handler changes: - ha_reset() now does some overall checks and calls ::reset() - ha_table_flags() added. This is a cached version of table_flags(). The cache is updated on engine creation time and updated on open. MySQL level changes (not obvious from the above): - DBUG_ASSERT() added to check that column usage matches what is set in the column usage bit maps. (This found a LOT of bugs in current column marking code). - In 5.1 before, all used columns was marked in read_set and only updated columns was marked in write_set. Now we only mark columns for which we need a value in read_set. - Column bitmaps are created in open_binary_frm() and open_table_from_share(). (Before this was in table.cc) - handler::table_flags() calls are replaced with handler::ha_table_flags() - For calling field->val() you must have the corresponding bit set in table->read_set. For calling field->store() you must have the corresponding bit set in table->write_set. (There are asserts in all store()/val() functions to catch wrong usage) - thd->set_query_id is renamed to thd->mark_used_columns and instead of setting this to an integer value, this has now the values: MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE Changed also all variables named 'set_query_id' to mark_used_columns. - In filesort() we now inform the handler of exactly which columns are needed doing the sort and choosing the rows. - The TABLE_SHARE object has a 'all_set' column bitmap one can use when one needs a column bitmap with all columns set. (This is used for table->use_all_columns() and other places) - The TABLE object has 3 column bitmaps: - def_read_set Default bitmap for columns to be read - def_write_set Default bitmap for columns to be written - tmp_set Can be used as a temporary bitmap when needed. The table object has also two pointer to bitmaps read_set and write_set that the handler should use to find out which columns are used in which way. - count() optimization now calls handler::records() instead of using handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true). - Added extra argument to Item::walk() to indicate if we should also traverse sub queries. - Added TABLE parameter to cp_buffer_from_ref() - Don't close tables created with CREATE ... SELECT but keep them in the table cache. (Faster usage of newly created tables). New interfaces: - table->clear_column_bitmaps() to initialize the bitmaps for tables at start of new statements. - table->column_bitmaps_set() to set up new column bitmaps and signal the handler about this. - table->column_bitmaps_set_no_signal() for some few cases where we need to setup new column bitmaps but don't signal the handler (as the handler has already been signaled about these before). Used for the momement only in opt_range.cc when doing ROR scans. - table->use_all_columns() to install a bitmap where all columns are marked as use in the read and the write set. - table->default_column_bitmaps() to install the normal read and write column bitmaps, but not signaling the handler about this. This is mainly used when creating TABLE instances. - table->mark_columns_needed_for_delete(), table->mark_columns_needed_for_delete() and table->mark_columns_needed_for_insert() to allow us to put additional columns in column usage maps if handler so requires. (The handler indicates what it neads in handler->table_flags()) - table->prepare_for_position() to allow us to tell handler that it needs to read primary key parts to be able to store them in future table->position() calls. (This replaces the table->file->ha_retrieve_all_pk function) - table->mark_auto_increment_column() to tell handler are going to update columns part of any auto_increment key. - table->mark_columns_used_by_index() to mark all columns that is part of an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow it to quickly know that it only needs to read colums that are part of the key. (The handler can also use the column map for detecting this, but simpler/faster handler can just monitor the extra() call). - table->mark_columns_used_by_index_no_reset() to in addition to other columns, also mark all columns that is used by the given key. - table->restore_column_maps_after_mark_index() to restore to default column maps after a call to table->mark_columns_used_by_index(). - New item function register_field_in_read_map(), for marking used columns in table->read_map. Used by filesort() to mark all used columns - Maintain in TABLE->merge_keys set of all keys that are used in query. (Simplices some optimization loops) - Maintain Field->part_of_key_not_clustered which is like Field->part_of_key but the field in the clustered key is not assumed to be part of all index. (used in opt_range.cc for faster loops) - dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map() tmp_use_all_columns() and tmp_restore_column_map() functions to temporally mark all columns as usable. The 'dbug_' version is primarily intended inside a handler when it wants to just call Field:store() & Field::val() functions, but don't need the column maps set for any other usage. (ie:: bitmap_is_set() is never called) - We can't use compare_records() to skip updates for handlers that returns a partial column set and the read_set doesn't cover all columns in the write set. The reason for this is that if we have a column marked only for write we can't in the MySQL level know if the value changed or not. The reason this worked before was that MySQL marked all to be written columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden bug'. - open_table_from_share() does not anymore setup temporary MEM_ROOT object as a thread specific variable for the handler. Instead we send the to-be-used MEMROOT to get_new_handler(). (Simpler, faster code) Bugs fixed: - Column marking was not done correctly in a lot of cases. (ALTER TABLE, when using triggers, auto_increment fields etc) (Could potentially result in wrong values inserted in table handlers relying on that the old column maps or field->set_query_id was correct) Especially when it comes to triggers, there may be cases where the old code would cause lost/wrong values for NDB and/or InnoDB tables. - Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags: OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG. This allowed me to remove some wrong warnings about: "Some non-transactional changed tables couldn't be rolled back" - Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset (thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose some warnings about "Some non-transactional changed tables couldn't be rolled back") - Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table() which could cause delete_table to report random failures. - Fixed core dumps for some tests when running with --debug - Added missing FN_LIBCHAR in mysql_rm_tmp_tables() (This has probably caused us to not properly remove temporary files after crash) - slow_logs was not properly initialized, which could maybe cause extra/lost entries in slow log. - If we get an duplicate row on insert, change column map to read and write all columns while retrying the operation. This is required by the definition of REPLACE and also ensures that fields that are only part of UPDATE are properly handled. This fixed a bug in NDB and REPLACE where REPLACE wrongly copied some column values from the replaced row. - For table handler that doesn't support NULL in keys, we would give an error when creating a primary key with NULL fields, even after the fields has been automaticly converted to NOT NULL. - Creating a primary key on a SPATIAL key, would fail if field was not declared as NOT NULL. Cleanups: - Removed not used condition argument to setup_tables - Removed not needed item function reset_query_id_processor(). - Field->add_index is removed. Now this is instead maintained in (field->flags & FIELD_IN_ADD_INDEX) - Field->fieldnr is removed (use field->field_index instead) - New argument to filesort() to indicate that it should return a set of row pointers (not used columns). This allowed me to remove some references to sql_command in filesort and should also enable us to return column results in some cases where we couldn't before. - Changed column bitmap handling in opt_range.cc to be aligned with TABLE bitmap, which allowed me to use bitmap functions instead of looping over all fields to create some needed bitmaps. (Faster and smaller code) - Broke up found too long lines - Moved some variable declaration at start of function for better code readability. - Removed some not used arguments from functions. (setup_fields(), mysql_prepare_insert_check_table()) - setup_fields() now takes an enum instead of an int for marking columns usage. - For internal temporary tables, use handler::write_row(), handler::delete_row() and handler::update_row() instead of handler::ha_xxxx() for faster execution. - Changed some constants to enum's and define's. - Using separate column read and write sets allows for easier checking of timestamp field was set by statement. - Remove calls to free_io_cache() as this is now done automaticly in ha_reset() - Don't build table->normalized_path as this is now identical to table->path (after bar's fixes to convert filenames) - Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to do comparision with the 'convert-dbug-for-diff' tool. Things left to do in 5.1: - We wrongly log failed CREATE TABLE ... SELECT in some cases when using row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result) Mats has promised to look into this. - Test that my fix for CREATE TABLE ... SELECT is indeed correct. (I added several test cases for this, but in this case it's better that someone else also tests this throughly). Lars has promosed to do this.
20 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
21 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
Fix for bug#20670 "UPDATE using key and invoking trigger that modifies this key does not stop" (5.1 version). UPDATE statement which WHERE clause used key and which invoked trigger that modified field in this key worked indefinetely. This problem occured because in cases when UPDATE statement was executed in update-on-the-fly mode (in which row is updated right during evaluation of select for WHERE clause) the new version of the row became visible to select representing WHERE clause and was updated again and again. We already solve this problem for UPDATE statements which does not invoke triggers by detecting the fact that we are going to update field in key used for scanning and performing update in two steps, during the first step we gather information about the rows to be updated and then doing actual updates. We also do this for MULTI-UPDATE and in its case we even detect situation when such fields are updated in triggers (actually we simply assume that we always update fields used in key if we have before update trigger). The fix simply extends this check which is done with help of check_if_key_used()/QUICK_SELECT_I::check_if_keys_used() routine/method in such way that it also detects cases when field used in key is updated in trigger. We do this by changing check_if_key_used() to take field bitmap instead field list as argument and passing TABLE::write_set to it (we also have to add info about fields used in triggers to this bitmap a bit earlier). As nice side-effect we have more precise and thus more optimal perfomance-wise check for the MULTI-UPDATE. Also check_if_key_used() routine and similar method were renamed to is_key_used()/is_keys_used() in order to better reflect that it is simple boolean predicate. Finally, partition_key_modified() routine now also takes field bitmap instead of field list as argument.
20 years ago
Fix for bug#20670 "UPDATE using key and invoking trigger that modifies this key does not stop" (5.1 version). UPDATE statement which WHERE clause used key and which invoked trigger that modified field in this key worked indefinetely. This problem occured because in cases when UPDATE statement was executed in update-on-the-fly mode (in which row is updated right during evaluation of select for WHERE clause) the new version of the row became visible to select representing WHERE clause and was updated again and again. We already solve this problem for UPDATE statements which does not invoke triggers by detecting the fact that we are going to update field in key used for scanning and performing update in two steps, during the first step we gather information about the rows to be updated and then doing actual updates. We also do this for MULTI-UPDATE and in its case we even detect situation when such fields are updated in triggers (actually we simply assume that we always update fields used in key if we have before update trigger). The fix simply extends this check which is done with help of check_if_key_used()/QUICK_SELECT_I::check_if_keys_used() routine/method in such way that it also detects cases when field used in key is updated in trigger. We do this by changing check_if_key_used() to take field bitmap instead field list as argument and passing TABLE::write_set to it (we also have to add info about fields used in triggers to this bitmap a bit earlier). As nice side-effect we have more precise and thus more optimal perfomance-wise check for the MULTI-UPDATE. Also check_if_key_used() routine and similar method were renamed to is_key_used()/is_keys_used() in order to better reflect that it is simple boolean predicate. Finally, partition_key_modified() routine now also takes field bitmap instead of field list as argument.
20 years ago
Fix for bug#20670 "UPDATE using key and invoking trigger that modifies this key does not stop" (5.1 version). UPDATE statement which WHERE clause used key and which invoked trigger that modified field in this key worked indefinetely. This problem occured because in cases when UPDATE statement was executed in update-on-the-fly mode (in which row is updated right during evaluation of select for WHERE clause) the new version of the row became visible to select representing WHERE clause and was updated again and again. We already solve this problem for UPDATE statements which does not invoke triggers by detecting the fact that we are going to update field in key used for scanning and performing update in two steps, during the first step we gather information about the rows to be updated and then doing actual updates. We also do this for MULTI-UPDATE and in its case we even detect situation when such fields are updated in triggers (actually we simply assume that we always update fields used in key if we have before update trigger). The fix simply extends this check which is done with help of check_if_key_used()/QUICK_SELECT_I::check_if_keys_used() routine/method in such way that it also detects cases when field used in key is updated in trigger. We do this by changing check_if_key_used() to take field bitmap instead field list as argument and passing TABLE::write_set to it (we also have to add info about fields used in triggers to this bitmap a bit earlier). As nice side-effect we have more precise and thus more optimal perfomance-wise check for the MULTI-UPDATE. Also check_if_key_used() routine and similar method were renamed to is_key_used()/is_keys_used() in order to better reflect that it is simple boolean predicate. Finally, partition_key_modified() routine now also takes field bitmap instead of field list as argument.
20 years ago
Fix for bug#20670 "UPDATE using key and invoking trigger that modifies this key does not stop" (5.1 version). UPDATE statement which WHERE clause used key and which invoked trigger that modified field in this key worked indefinetely. This problem occured because in cases when UPDATE statement was executed in update-on-the-fly mode (in which row is updated right during evaluation of select for WHERE clause) the new version of the row became visible to select representing WHERE clause and was updated again and again. We already solve this problem for UPDATE statements which does not invoke triggers by detecting the fact that we are going to update field in key used for scanning and performing update in two steps, during the first step we gather information about the rows to be updated and then doing actual updates. We also do this for MULTI-UPDATE and in its case we even detect situation when such fields are updated in triggers (actually we simply assume that we always update fields used in key if we have before update trigger). The fix simply extends this check which is done with help of check_if_key_used()/QUICK_SELECT_I::check_if_keys_used() routine/method in such way that it also detects cases when field used in key is updated in trigger. We do this by changing check_if_key_used() to take field bitmap instead field list as argument and passing TABLE::write_set to it (we also have to add info about fields used in triggers to this bitmap a bit earlier). As nice side-effect we have more precise and thus more optimal perfomance-wise check for the MULTI-UPDATE. Also check_if_key_used() routine and similar method were renamed to is_key_used()/is_keys_used() in order to better reflect that it is simple boolean predicate. Finally, partition_key_modified() routine now also takes field bitmap instead of field list as argument.
20 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes Changes that requires code changes in other code of other storage engines. (Note that all changes are very straightforward and one should find all issues by compiling a --debug build and fixing all compiler errors and all asserts in field.cc while running the test suite), - New optional handler function introduced: reset() This is called after every DML statement to make it easy for a handler to statement specific cleanups. (The only case it's not called is if force the file to be closed) - handler::extra(HA_EXTRA_RESET) is removed. Code that was there before should be moved to handler::reset() - table->read_set contains a bitmap over all columns that are needed in the query. read_row() and similar functions only needs to read these columns - table->write_set contains a bitmap over all columns that will be updated in the query. write_row() and update_row() only needs to update these columns. The above bitmaps should now be up to date in all context (including ALTER TABLE, filesort()). The handler is informed of any changes to the bitmap after fix_fields() by calling the virtual function handler::column_bitmaps_signal(). If the handler does caching of these bitmaps (instead of using table->read_set, table->write_set), it should redo the caching in this code. as the signal() may be sent several times, it's probably best to set a variable in the signal and redo the caching on read_row() / write_row() if the variable was set. - Removed the read_set and write_set bitmap objects from the handler class - Removed all column bit handling functions from the handler class. (Now one instead uses the normal bitmap functions in my_bitmap.c instead of handler dedicated bitmap functions) - field->query_id is removed. One should instead instead check table->read_set and table->write_set if a field is used in the query. - handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now instead use table->read_set to check for which columns to retrieve. - If a handler needs to call Field->val() or Field->store() on columns that are not used in the query, one should install a temporary all-columns-used map while doing so. For this, we provide the following functions: my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); field->val(); dbug_tmp_restore_column_map(table->read_set, old_map); and similar for the write map: my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); field->val(); dbug_tmp_restore_column_map(table->write_set, old_map); If this is not done, you will sooner or later hit a DBUG_ASSERT in the field store() / val() functions. (For not DBUG binaries, the dbug_tmp_restore_column_map() and dbug_tmp_restore_column_map() are inline dummy functions and should be optimized away be the compiler). - If one needs to temporary set the column map for all binaries (and not just to avoid the DBUG_ASSERT() in the Field::store() / Field::val() methods) one should use the functions tmp_use_all_columns() and tmp_restore_column_map() instead of the above dbug_ variants. - All 'status' fields in the handler base class (like records, data_file_length etc) are now stored in a 'stats' struct. This makes it easier to know what status variables are provided by the base handler. This requires some trivial variable names in the extra() function. - New virtual function handler::records(). This is called to optimize COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true. (stats.records is not supposed to be an exact value. It's only has to be 'reasonable enough' for the optimizer to be able to choose a good optimization path). - Non virtual handler::init() function added for caching of virtual constants from engine. - Removed has_transactions() virtual method. Now one should instead return HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support transactions. - The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument that is to be used with 'new handler_name()' to allocate the handler in the right area. The xxxx_create_handler() function is also responsible for any initialization of the object before returning. For example, one should change: static handler *myisam_create_handler(TABLE_SHARE *table) { return new ha_myisam(table); } -> static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root) { return new (mem_root) ha_myisam(table); } - New optional virtual function: use_hidden_primary_key(). This is called in case of an update/delete when (table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined but we don't have a primary key. This allows the handler to take precisions in remembering any hidden primary key to able to update/delete any found row. The default handler marks all columns to be read. - handler::table_flags() now returns a ulonglong (to allow for more flags). - New/changed table_flags() - HA_HAS_RECORDS Set if ::records() is supported - HA_NO_TRANSACTIONS Set if engine doesn't support transactions - HA_PRIMARY_KEY_REQUIRED_FOR_DELETE Set if we should mark all primary key columns for read when reading rows as part of a DELETE statement. If there is no primary key, all columns are marked for read. - HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some cases (based on table->read_set) - HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION. - HA_DUPP_POS Renamed to HA_DUPLICATE_POS - HA_REQUIRES_KEY_COLUMNS_FOR_DELETE Set this if we should mark ALL key columns for read when when reading rows as part of a DELETE statement. In case of an update we will mark all keys for read for which key part changed value. - HA_STATS_RECORDS_IS_EXACT Set this if stats.records is exact. (This saves us some extra records() calls when optimizing COUNT(*)) - Removed table_flags() - HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if handler::records() gives an exact count() and HA_STATS_RECORDS_IS_EXACT if stats.records is exact. - HA_READ_RND_SAME Removed (no one supported this one) - Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk() - Renamed handler::dupp_pos to handler::dup_pos - Removed not used variable handler::sortkey Upper level handler changes: - ha_reset() now does some overall checks and calls ::reset() - ha_table_flags() added. This is a cached version of table_flags(). The cache is updated on engine creation time and updated on open. MySQL level changes (not obvious from the above): - DBUG_ASSERT() added to check that column usage matches what is set in the column usage bit maps. (This found a LOT of bugs in current column marking code). - In 5.1 before, all used columns was marked in read_set and only updated columns was marked in write_set. Now we only mark columns for which we need a value in read_set. - Column bitmaps are created in open_binary_frm() and open_table_from_share(). (Before this was in table.cc) - handler::table_flags() calls are replaced with handler::ha_table_flags() - For calling field->val() you must have the corresponding bit set in table->read_set. For calling field->store() you must have the corresponding bit set in table->write_set. (There are asserts in all store()/val() functions to catch wrong usage) - thd->set_query_id is renamed to thd->mark_used_columns and instead of setting this to an integer value, this has now the values: MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE Changed also all variables named 'set_query_id' to mark_used_columns. - In filesort() we now inform the handler of exactly which columns are needed doing the sort and choosing the rows. - The TABLE_SHARE object has a 'all_set' column bitmap one can use when one needs a column bitmap with all columns set. (This is used for table->use_all_columns() and other places) - The TABLE object has 3 column bitmaps: - def_read_set Default bitmap for columns to be read - def_write_set Default bitmap for columns to be written - tmp_set Can be used as a temporary bitmap when needed. The table object has also two pointer to bitmaps read_set and write_set that the handler should use to find out which columns are used in which way. - count() optimization now calls handler::records() instead of using handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true). - Added extra argument to Item::walk() to indicate if we should also traverse sub queries. - Added TABLE parameter to cp_buffer_from_ref() - Don't close tables created with CREATE ... SELECT but keep them in the table cache. (Faster usage of newly created tables). New interfaces: - table->clear_column_bitmaps() to initialize the bitmaps for tables at start of new statements. - table->column_bitmaps_set() to set up new column bitmaps and signal the handler about this. - table->column_bitmaps_set_no_signal() for some few cases where we need to setup new column bitmaps but don't signal the handler (as the handler has already been signaled about these before). Used for the momement only in opt_range.cc when doing ROR scans. - table->use_all_columns() to install a bitmap where all columns are marked as use in the read and the write set. - table->default_column_bitmaps() to install the normal read and write column bitmaps, but not signaling the handler about this. This is mainly used when creating TABLE instances. - table->mark_columns_needed_for_delete(), table->mark_columns_needed_for_delete() and table->mark_columns_needed_for_insert() to allow us to put additional columns in column usage maps if handler so requires. (The handler indicates what it neads in handler->table_flags()) - table->prepare_for_position() to allow us to tell handler that it needs to read primary key parts to be able to store them in future table->position() calls. (This replaces the table->file->ha_retrieve_all_pk function) - table->mark_auto_increment_column() to tell handler are going to update columns part of any auto_increment key. - table->mark_columns_used_by_index() to mark all columns that is part of an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow it to quickly know that it only needs to read colums that are part of the key. (The handler can also use the column map for detecting this, but simpler/faster handler can just monitor the extra() call). - table->mark_columns_used_by_index_no_reset() to in addition to other columns, also mark all columns that is used by the given key. - table->restore_column_maps_after_mark_index() to restore to default column maps after a call to table->mark_columns_used_by_index(). - New item function register_field_in_read_map(), for marking used columns in table->read_map. Used by filesort() to mark all used columns - Maintain in TABLE->merge_keys set of all keys that are used in query. (Simplices some optimization loops) - Maintain Field->part_of_key_not_clustered which is like Field->part_of_key but the field in the clustered key is not assumed to be part of all index. (used in opt_range.cc for faster loops) - dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map() tmp_use_all_columns() and tmp_restore_column_map() functions to temporally mark all columns as usable. The 'dbug_' version is primarily intended inside a handler when it wants to just call Field:store() & Field::val() functions, but don't need the column maps set for any other usage. (ie:: bitmap_is_set() is never called) - We can't use compare_records() to skip updates for handlers that returns a partial column set and the read_set doesn't cover all columns in the write set. The reason for this is that if we have a column marked only for write we can't in the MySQL level know if the value changed or not. The reason this worked before was that MySQL marked all to be written columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden bug'. - open_table_from_share() does not anymore setup temporary MEM_ROOT object as a thread specific variable for the handler. Instead we send the to-be-used MEMROOT to get_new_handler(). (Simpler, faster code) Bugs fixed: - Column marking was not done correctly in a lot of cases. (ALTER TABLE, when using triggers, auto_increment fields etc) (Could potentially result in wrong values inserted in table handlers relying on that the old column maps or field->set_query_id was correct) Especially when it comes to triggers, there may be cases where the old code would cause lost/wrong values for NDB and/or InnoDB tables. - Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags: OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG. This allowed me to remove some wrong warnings about: "Some non-transactional changed tables couldn't be rolled back" - Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset (thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose some warnings about "Some non-transactional changed tables couldn't be rolled back") - Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table() which could cause delete_table to report random failures. - Fixed core dumps for some tests when running with --debug - Added missing FN_LIBCHAR in mysql_rm_tmp_tables() (This has probably caused us to not properly remove temporary files after crash) - slow_logs was not properly initialized, which could maybe cause extra/lost entries in slow log. - If we get an duplicate row on insert, change column map to read and write all columns while retrying the operation. This is required by the definition of REPLACE and also ensures that fields that are only part of UPDATE are properly handled. This fixed a bug in NDB and REPLACE where REPLACE wrongly copied some column values from the replaced row. - For table handler that doesn't support NULL in keys, we would give an error when creating a primary key with NULL fields, even after the fields has been automaticly converted to NOT NULL. - Creating a primary key on a SPATIAL key, would fail if field was not declared as NOT NULL. Cleanups: - Removed not used condition argument to setup_tables - Removed not needed item function reset_query_id_processor(). - Field->add_index is removed. Now this is instead maintained in (field->flags & FIELD_IN_ADD_INDEX) - Field->fieldnr is removed (use field->field_index instead) - New argument to filesort() to indicate that it should return a set of row pointers (not used columns). This allowed me to remove some references to sql_command in filesort and should also enable us to return column results in some cases where we couldn't before. - Changed column bitmap handling in opt_range.cc to be aligned with TABLE bitmap, which allowed me to use bitmap functions instead of looping over all fields to create some needed bitmaps. (Faster and smaller code) - Broke up found too long lines - Moved some variable declaration at start of function for better code readability. - Removed some not used arguments from functions. (setup_fields(), mysql_prepare_insert_check_table()) - setup_fields() now takes an enum instead of an int for marking columns usage. - For internal temporary tables, use handler::write_row(), handler::delete_row() and handler::update_row() instead of handler::ha_xxxx() for faster execution. - Changed some constants to enum's and define's. - Using separate column read and write sets allows for easier checking of timestamp field was set by statement. - Remove calls to free_io_cache() as this is now done automaticly in ha_reset() - Don't build table->normalized_path as this is now identical to table->path (after bar's fixes to convert filenames) - Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to do comparision with the 'convert-dbug-for-diff' tool. Things left to do in 5.1: - We wrongly log failed CREATE TABLE ... SELECT in some cases when using row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result) Mats has promised to look into this. - Test that my fix for CREATE TABLE ... SELECT is indeed correct. (I added several test cases for this, but in this case it's better that someone else also tests this throughly). Lars has promosed to do this.
20 years ago
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes Changes that requires code changes in other code of other storage engines. (Note that all changes are very straightforward and one should find all issues by compiling a --debug build and fixing all compiler errors and all asserts in field.cc while running the test suite), - New optional handler function introduced: reset() This is called after every DML statement to make it easy for a handler to statement specific cleanups. (The only case it's not called is if force the file to be closed) - handler::extra(HA_EXTRA_RESET) is removed. Code that was there before should be moved to handler::reset() - table->read_set contains a bitmap over all columns that are needed in the query. read_row() and similar functions only needs to read these columns - table->write_set contains a bitmap over all columns that will be updated in the query. write_row() and update_row() only needs to update these columns. The above bitmaps should now be up to date in all context (including ALTER TABLE, filesort()). The handler is informed of any changes to the bitmap after fix_fields() by calling the virtual function handler::column_bitmaps_signal(). If the handler does caching of these bitmaps (instead of using table->read_set, table->write_set), it should redo the caching in this code. as the signal() may be sent several times, it's probably best to set a variable in the signal and redo the caching on read_row() / write_row() if the variable was set. - Removed the read_set and write_set bitmap objects from the handler class - Removed all column bit handling functions from the handler class. (Now one instead uses the normal bitmap functions in my_bitmap.c instead of handler dedicated bitmap functions) - field->query_id is removed. One should instead instead check table->read_set and table->write_set if a field is used in the query. - handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now instead use table->read_set to check for which columns to retrieve. - If a handler needs to call Field->val() or Field->store() on columns that are not used in the query, one should install a temporary all-columns-used map while doing so. For this, we provide the following functions: my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); field->val(); dbug_tmp_restore_column_map(table->read_set, old_map); and similar for the write map: my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); field->val(); dbug_tmp_restore_column_map(table->write_set, old_map); If this is not done, you will sooner or later hit a DBUG_ASSERT in the field store() / val() functions. (For not DBUG binaries, the dbug_tmp_restore_column_map() and dbug_tmp_restore_column_map() are inline dummy functions and should be optimized away be the compiler). - If one needs to temporary set the column map for all binaries (and not just to avoid the DBUG_ASSERT() in the Field::store() / Field::val() methods) one should use the functions tmp_use_all_columns() and tmp_restore_column_map() instead of the above dbug_ variants. - All 'status' fields in the handler base class (like records, data_file_length etc) are now stored in a 'stats' struct. This makes it easier to know what status variables are provided by the base handler. This requires some trivial variable names in the extra() function. - New virtual function handler::records(). This is called to optimize COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true. (stats.records is not supposed to be an exact value. It's only has to be 'reasonable enough' for the optimizer to be able to choose a good optimization path). - Non virtual handler::init() function added for caching of virtual constants from engine. - Removed has_transactions() virtual method. Now one should instead return HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support transactions. - The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument that is to be used with 'new handler_name()' to allocate the handler in the right area. The xxxx_create_handler() function is also responsible for any initialization of the object before returning. For example, one should change: static handler *myisam_create_handler(TABLE_SHARE *table) { return new ha_myisam(table); } -> static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root) { return new (mem_root) ha_myisam(table); } - New optional virtual function: use_hidden_primary_key(). This is called in case of an update/delete when (table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined but we don't have a primary key. This allows the handler to take precisions in remembering any hidden primary key to able to update/delete any found row. The default handler marks all columns to be read. - handler::table_flags() now returns a ulonglong (to allow for more flags). - New/changed table_flags() - HA_HAS_RECORDS Set if ::records() is supported - HA_NO_TRANSACTIONS Set if engine doesn't support transactions - HA_PRIMARY_KEY_REQUIRED_FOR_DELETE Set if we should mark all primary key columns for read when reading rows as part of a DELETE statement. If there is no primary key, all columns are marked for read. - HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some cases (based on table->read_set) - HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION. - HA_DUPP_POS Renamed to HA_DUPLICATE_POS - HA_REQUIRES_KEY_COLUMNS_FOR_DELETE Set this if we should mark ALL key columns for read when when reading rows as part of a DELETE statement. In case of an update we will mark all keys for read for which key part changed value. - HA_STATS_RECORDS_IS_EXACT Set this if stats.records is exact. (This saves us some extra records() calls when optimizing COUNT(*)) - Removed table_flags() - HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if handler::records() gives an exact count() and HA_STATS_RECORDS_IS_EXACT if stats.records is exact. - HA_READ_RND_SAME Removed (no one supported this one) - Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk() - Renamed handler::dupp_pos to handler::dup_pos - Removed not used variable handler::sortkey Upper level handler changes: - ha_reset() now does some overall checks and calls ::reset() - ha_table_flags() added. This is a cached version of table_flags(). The cache is updated on engine creation time and updated on open. MySQL level changes (not obvious from the above): - DBUG_ASSERT() added to check that column usage matches what is set in the column usage bit maps. (This found a LOT of bugs in current column marking code). - In 5.1 before, all used columns was marked in read_set and only updated columns was marked in write_set. Now we only mark columns for which we need a value in read_set. - Column bitmaps are created in open_binary_frm() and open_table_from_share(). (Before this was in table.cc) - handler::table_flags() calls are replaced with handler::ha_table_flags() - For calling field->val() you must have the corresponding bit set in table->read_set. For calling field->store() you must have the corresponding bit set in table->write_set. (There are asserts in all store()/val() functions to catch wrong usage) - thd->set_query_id is renamed to thd->mark_used_columns and instead of setting this to an integer value, this has now the values: MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE Changed also all variables named 'set_query_id' to mark_used_columns. - In filesort() we now inform the handler of exactly which columns are needed doing the sort and choosing the rows. - The TABLE_SHARE object has a 'all_set' column bitmap one can use when one needs a column bitmap with all columns set. (This is used for table->use_all_columns() and other places) - The TABLE object has 3 column bitmaps: - def_read_set Default bitmap for columns to be read - def_write_set Default bitmap for columns to be written - tmp_set Can be used as a temporary bitmap when needed. The table object has also two pointer to bitmaps read_set and write_set that the handler should use to find out which columns are used in which way. - count() optimization now calls handler::records() instead of using handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true). - Added extra argument to Item::walk() to indicate if we should also traverse sub queries. - Added TABLE parameter to cp_buffer_from_ref() - Don't close tables created with CREATE ... SELECT but keep them in the table cache. (Faster usage of newly created tables). New interfaces: - table->clear_column_bitmaps() to initialize the bitmaps for tables at start of new statements. - table->column_bitmaps_set() to set up new column bitmaps and signal the handler about this. - table->column_bitmaps_set_no_signal() for some few cases where we need to setup new column bitmaps but don't signal the handler (as the handler has already been signaled about these before). Used for the momement only in opt_range.cc when doing ROR scans. - table->use_all_columns() to install a bitmap where all columns are marked as use in the read and the write set. - table->default_column_bitmaps() to install the normal read and write column bitmaps, but not signaling the handler about this. This is mainly used when creating TABLE instances. - table->mark_columns_needed_for_delete(), table->mark_columns_needed_for_delete() and table->mark_columns_needed_for_insert() to allow us to put additional columns in column usage maps if handler so requires. (The handler indicates what it neads in handler->table_flags()) - table->prepare_for_position() to allow us to tell handler that it needs to read primary key parts to be able to store them in future table->position() calls. (This replaces the table->file->ha_retrieve_all_pk function) - table->mark_auto_increment_column() to tell handler are going to update columns part of any auto_increment key. - table->mark_columns_used_by_index() to mark all columns that is part of an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow it to quickly know that it only needs to read colums that are part of the key. (The handler can also use the column map for detecting this, but simpler/faster handler can just monitor the extra() call). - table->mark_columns_used_by_index_no_reset() to in addition to other columns, also mark all columns that is used by the given key. - table->restore_column_maps_after_mark_index() to restore to default column maps after a call to table->mark_columns_used_by_index(). - New item function register_field_in_read_map(), for marking used columns in table->read_map. Used by filesort() to mark all used columns - Maintain in TABLE->merge_keys set of all keys that are used in query. (Simplices some optimization loops) - Maintain Field->part_of_key_not_clustered which is like Field->part_of_key but the field in the clustered key is not assumed to be part of all index. (used in opt_range.cc for faster loops) - dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map() tmp_use_all_columns() and tmp_restore_column_map() functions to temporally mark all columns as usable. The 'dbug_' version is primarily intended inside a handler when it wants to just call Field:store() & Field::val() functions, but don't need the column maps set for any other usage. (ie:: bitmap_is_set() is never called) - We can't use compare_records() to skip updates for handlers that returns a partial column set and the read_set doesn't cover all columns in the write set. The reason for this is that if we have a column marked only for write we can't in the MySQL level know if the value changed or not. The reason this worked before was that MySQL marked all to be written columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden bug'. - open_table_from_share() does not anymore setup temporary MEM_ROOT object as a thread specific variable for the handler. Instead we send the to-be-used MEMROOT to get_new_handler(). (Simpler, faster code) Bugs fixed: - Column marking was not done correctly in a lot of cases. (ALTER TABLE, when using triggers, auto_increment fields etc) (Could potentially result in wrong values inserted in table handlers relying on that the old column maps or field->set_query_id was correct) Especially when it comes to triggers, there may be cases where the old code would cause lost/wrong values for NDB and/or InnoDB tables. - Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags: OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG. This allowed me to remove some wrong warnings about: "Some non-transactional changed tables couldn't be rolled back" - Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset (thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose some warnings about "Some non-transactional changed tables couldn't be rolled back") - Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table() which could cause delete_table to report random failures. - Fixed core dumps for some tests when running with --debug - Added missing FN_LIBCHAR in mysql_rm_tmp_tables() (This has probably caused us to not properly remove temporary files after crash) - slow_logs was not properly initialized, which could maybe cause extra/lost entries in slow log. - If we get an duplicate row on insert, change column map to read and write all columns while retrying the operation. This is required by the definition of REPLACE and also ensures that fields that are only part of UPDATE are properly handled. This fixed a bug in NDB and REPLACE where REPLACE wrongly copied some column values from the replaced row. - For table handler that doesn't support NULL in keys, we would give an error when creating a primary key with NULL fields, even after the fields has been automaticly converted to NOT NULL. - Creating a primary key on a SPATIAL key, would fail if field was not declared as NOT NULL. Cleanups: - Removed not used condition argument to setup_tables - Removed not needed item function reset_query_id_processor(). - Field->add_index is removed. Now this is instead maintained in (field->flags & FIELD_IN_ADD_INDEX) - Field->fieldnr is removed (use field->field_index instead) - New argument to filesort() to indicate that it should return a set of row pointers (not used columns). This allowed me to remove some references to sql_command in filesort and should also enable us to return column results in some cases where we couldn't before. - Changed column bitmap handling in opt_range.cc to be aligned with TABLE bitmap, which allowed me to use bitmap functions instead of looping over all fields to create some needed bitmaps. (Faster and smaller code) - Broke up found too long lines - Moved some variable declaration at start of function for better code readability. - Removed some not used arguments from functions. (setup_fields(), mysql_prepare_insert_check_table()) - setup_fields() now takes an enum instead of an int for marking columns usage. - For internal temporary tables, use handler::write_row(), handler::delete_row() and handler::update_row() instead of handler::ha_xxxx() for faster execution. - Changed some constants to enum's and define's. - Using separate column read and write sets allows for easier checking of timestamp field was set by statement. - Remove calls to free_io_cache() as this is now done automaticly in ha_reset() - Don't build table->normalized_path as this is now identical to table->path (after bar's fixes to convert filenames) - Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to do comparision with the 'convert-dbug-for-diff' tool. Things left to do in 5.1: - We wrongly log failed CREATE TABLE ... SELECT in some cases when using row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result) Mats has promised to look into this. - Test that my fix for CREATE TABLE ... SELECT is indeed correct. (I added several test cases for this, but in this case it's better that someone else also tests this throughly). Lars has promosed to do this.
20 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
5.1 version of a fix and test cases for bugs: Bug#4968 ""Stored procedure crash if cursor opened on altered table" Bug#6895 "Prepared Statements: ALTER TABLE DROP COLUMN does nothing" Bug#19182 "CREATE TABLE bar (m INT) SELECT n FROM foo; doesn't work from stored procedure." Bug#19733 "Repeated alter, or repeated create/drop, fails" Bug#22060 "ALTER TABLE x AUTO_INCREMENT=y in SP crashes server" Bug#24879 "Prepared Statements: CREATE TABLE (UTF8 KEY) produces a growing key length" (this bug is not fixed in 5.0) Re-execution of CREATE DATABASE, CREATE TABLE and ALTER TABLE statements in stored routines or as prepared statements caused incorrect results (and crashes in versions prior to 5.0.25). In 5.1 the problem occured only for CREATE DATABASE, CREATE TABLE SELECT and CREATE TABLE with INDEX/DATA DIRECTOY options). The problem of bugs 4968, 19733, 19282 and 6895 was that functions mysql_prepare_table, mysql_create_table and mysql_alter_table are not re-execution friendly: during their operation they modify contents of LEX (members create_info, alter_info, key_list, create_list), thus making the LEX unusable for the next execution. In particular, these functions removed processed columns and keys from create_list, key_list and drop_list. Search the code in sql_table.cc for drop_it.remove() and similar patterns to find evidence. The fix is to supply to these functions a usable copy of each of the above structures at every re-execution of an SQL statement. To simplify memory management, LEX::key_list and LEX::create_list were added to LEX::alter_info, a fresh copy of which is created for every execution. The problem of crashing bug 22060 stemmed from the fact that the above metnioned functions were not only modifying HA_CREATE_INFO structure in LEX, but also were changing it to point to areas in volatile memory of the execution memory root. The patch solves this problem by creating and using an on-stack copy of HA_CREATE_INFO in mysql_execute_command. Additionally, this patch splits the part of mysql_alter_table that analizes and rewrites information from the parser into a separate function - mysql_prepare_alter_table, in analogy with mysql_prepare_table, which is renamed to mysql_prepare_create_table.
19 years ago
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Bug 26867 - LOCK TABLES + REPAIR + merge table result in memory/cpu hogging Bug 26377 - Deadlock with MERGE and FLUSH TABLE Bug 25038 - Waiting TRUNCATE Bug 25700 - merge base tables get corrupted by optimize/analyze/repair table Bug 30275 - Merge tables: flush tables or unlock tables causes server to crash Bug 19627 - temporary merge table locking Bug 27660 - Falcon: merge table possible Bug 30273 - merge tables: Can't lock file (errno: 155) The problems were: Bug 26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table 1. A thread trying to lock a MERGE table performs busy waiting while REPAIR TABLE or a similar table administration task is ongoing on one or more of its MyISAM tables. 2. A thread trying to lock a MERGE table performs busy waiting until all threads that did REPAIR TABLE or similar table administration tasks on one or more of its MyISAM tables in LOCK TABLES segments do UNLOCK TABLES. The difference against problem #1 is that the busy waiting takes place *after* the administration task. It is terminated by UNLOCK TABLES only. 3. Two FLUSH TABLES within a LOCK TABLES segment can invalidate the lock. This does *not* require a MERGE table. The first FLUSH TABLES can be replaced by any statement that requires other threads to reopen the table. In 5.0 and 5.1 a single FLUSH TABLES can provoke the problem. Bug 26867 - LOCK TABLES + REPAIR + merge table result in memory/cpu hogging Trying DML on a MERGE table, which has a child locked and repaired by another thread, made an infinite loop in the server. Bug 26377 - Deadlock with MERGE and FLUSH TABLE Locking a MERGE table and its children in parent-child order and flushing the child deadlocked the server. Bug 25038 - Waiting TRUNCATE Truncating a MERGE child, while the MERGE table was in use, let the truncate fail instead of waiting for the table to become free. Bug 25700 - merge base tables get corrupted by optimize/analyze/repair table Repairing a child of an open MERGE table corrupted the child. It was necessary to FLUSH the child first. Bug 30275 - Merge tables: flush tables or unlock tables causes server to crash Flushing and optimizing locked MERGE children crashed the server. Bug 19627 - temporary merge table locking Use of a temporary MERGE table with non-temporary children could corrupt the children. Temporary tables are never locked. So we do now prohibit non-temporary chidlren of a temporary MERGE table. Bug 27660 - Falcon: merge table possible It was possible to create a MERGE table with non-MyISAM children. Bug 30273 - merge tables: Can't lock file (errno: 155) This was a Windows-only bug. Table administration statements sometimes failed with "Can't lock file (errno: 155)". These bugs are fixed by a new implementation of MERGE table open. When opening a MERGE table in open_tables() we do now add the child tables to the list of tables to be opened by open_tables() (the "query_list"). The children are not opened in the handler at this stage. After opening the parent, open_tables() opens each child from the now extended query_list. When the last child is opened, we remove the children from the query_list again and attach the children to the parent. This behaves similar to the old open. However it does not open the MyISAM tables directly, but grabs them from the already open children. When closing a MERGE table in close_thread_table() we detach the children only. Closing of the children is done implicitly because they are in thd->open_tables. For more detail see the comment at the top of ha_myisammrg.cc. Changed from open_ltable() to open_and_lock_tables() in all places that can be relevant for MERGE tables. The latter can handle tables added to the list on the fly. When open_ltable() was used in a loop over a list of tables, the list must be temporarily terminated after every table for open_and_lock_tables(). table_list->required_type is set to FRMTYPE_TABLE to avoid open of special tables. Handling of derived tables is suppressed. These details are handled by the new function open_n_lock_single_table(), which has nearly the same signature as open_ltable() and can replace it in most cases. In reopen_tables() some of the tables open by a thread can be closed and reopened. When a MERGE child is affected, the parent must be closed and reopened too. Closing of the parent is forced before the first child is closed. Reopen happens in the order of thd->open_tables. MERGE parents do not attach their children automatically at open. This is done after all tables are reopened. So all children are open when attaching them. Special lock handling like mysql_lock_abort() or mysql_lock_remove() needs to be suppressed for MERGE children or forwarded to the parent. This depends on the situation. In loops over all open tables one suppresses child lock handling. When a single table is touched, forwarding is done. Behavioral changes: =================== This patch changes the behavior of temporary MERGE tables. Temporary MERGE must have temporary children. The old behavior was wrong. A temporary table is not locked. Hence even non-temporary children were not locked. See Bug 19627 - temporary merge table locking. You cannot change the union list of a non-temporary MERGE table when LOCK TABLES is in effect. The following does *not* work: CREATE TABLE m1 ... ENGINE=MRG_MYISAM ...; LOCK TABLES t1 WRITE, t2 WRITE, m1 WRITE; ALTER TABLE m1 ... UNION=(t1,t2) ...; However, you can do this with a temporary MERGE table. You cannot create a MERGE table with CREATE ... SELECT, neither as a temporary MERGE table, nor as a non-temporary MERGE table. CREATE TABLE m1 ... ENGINE=MRG_MYISAM ... SELECT ...; Gives error message: table is not BASE TABLE.
18 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
Bug#18775 - Temporary table from alter table visible to other threads Continued implementation of WL#1324 (table name to filename encoding) The intermediate (not temporary) files of the new table during ALTER TABLE was visible for SHOW TABLES. These intermediate files are copies of the original table with the changes done by ALTER TABLE. After all the data is copied over from the original table, these files are renamed to the original tables file names. So they are not temporary files. They persist after ALTER TABLE, but just with another name. In 5.0 the intermediate files are invisible for SHOW TABLES because all file names beginning with "#sql" were suppressed. This failed since 5.1.6 because even temporary table names were converted when making file names from them. The prefix became converted to "@0023sql". Converting the prefix during SHOW TABLES would suppress the listing of user tables that start with "#sql". The solution of the problem is to continue the implementation of the table name to file name conversion feature. One requirement is to suppress the conversion for temporary table names. This change is straightforward for real temporary tables as there is a function that creates temporary file names. But the generated path names are located in TMPDIR and have no relation to the internal table name. This cannot be used for ALTER TABLE. Its intermediate files need to be in the same directory as the old table files. And it is necessary to be able to deduce the same path from the same table name repeatedly. Consequently the intermediate table files must be handled like normal tables. Their internal names shall start with tmp_file_prefix (#sql) and they shall not be converted like normal table names. I added a flags parameter to all relevant functions that are called from ALTER TABLE. It is used to suppress the conversion for the intermediate table files. The outcome is that the suppression of #sql in SHOW TABLES works again. It does not suppress user tables as these are converted to @0023sql on file level. This patch does also fix ALTER TABLE ... RENAME, which could not rename a table with non-ASCII characters in its name. It does also fix the problem that a user could create a table like `#sql-xxxx-yyyy`, where xxxx is mysqld's pid and yyyy is the thread ID of some other thread, which prevented this thread from running ALTER TABLE. Some of the above problems are mentioned in Bug 1405, which can be closed with this patch. This patch does also contain some minor fixes for other forgotten conversions. Still known problems are reported as bugs 21370, 21373, and 21387.
20 years ago
Bug#18775 - Temporary table from alter table visible to other threads Continued implementation of WL#1324 (table name to filename encoding) The intermediate (not temporary) files of the new table during ALTER TABLE was visible for SHOW TABLES. These intermediate files are copies of the original table with the changes done by ALTER TABLE. After all the data is copied over from the original table, these files are renamed to the original tables file names. So they are not temporary files. They persist after ALTER TABLE, but just with another name. In 5.0 the intermediate files are invisible for SHOW TABLES because all file names beginning with "#sql" were suppressed. This failed since 5.1.6 because even temporary table names were converted when making file names from them. The prefix became converted to "@0023sql". Converting the prefix during SHOW TABLES would suppress the listing of user tables that start with "#sql". The solution of the problem is to continue the implementation of the table name to file name conversion feature. One requirement is to suppress the conversion for temporary table names. This change is straightforward for real temporary tables as there is a function that creates temporary file names. But the generated path names are located in TMPDIR and have no relation to the internal table name. This cannot be used for ALTER TABLE. Its intermediate files need to be in the same directory as the old table files. And it is necessary to be able to deduce the same path from the same table name repeatedly. Consequently the intermediate table files must be handled like normal tables. Their internal names shall start with tmp_file_prefix (#sql) and they shall not be converted like normal table names. I added a flags parameter to all relevant functions that are called from ALTER TABLE. It is used to suppress the conversion for the intermediate table files. The outcome is that the suppression of #sql in SHOW TABLES works again. It does not suppress user tables as these are converted to @0023sql on file level. This patch does also fix ALTER TABLE ... RENAME, which could not rename a table with non-ASCII characters in its name. It does also fix the problem that a user could create a table like `#sql-xxxx-yyyy`, where xxxx is mysqld's pid and yyyy is the thread ID of some other thread, which prevented this thread from running ALTER TABLE. Some of the above problems are mentioned in Bug 1405, which can be closed with this patch. This patch does also contain some minor fixes for other forgotten conversions. Still known problems are reported as bugs 21370, 21373, and 21387.
20 years ago
Bug#18775 - Temporary table from alter table visible to other threads Continued implementation of WL#1324 (table name to filename encoding) The intermediate (not temporary) files of the new table during ALTER TABLE was visible for SHOW TABLES. These intermediate files are copies of the original table with the changes done by ALTER TABLE. After all the data is copied over from the original table, these files are renamed to the original tables file names. So they are not temporary files. They persist after ALTER TABLE, but just with another name. In 5.0 the intermediate files are invisible for SHOW TABLES because all file names beginning with "#sql" were suppressed. This failed since 5.1.6 because even temporary table names were converted when making file names from them. The prefix became converted to "@0023sql". Converting the prefix during SHOW TABLES would suppress the listing of user tables that start with "#sql". The solution of the problem is to continue the implementation of the table name to file name conversion feature. One requirement is to suppress the conversion for temporary table names. This change is straightforward for real temporary tables as there is a function that creates temporary file names. But the generated path names are located in TMPDIR and have no relation to the internal table name. This cannot be used for ALTER TABLE. Its intermediate files need to be in the same directory as the old table files. And it is necessary to be able to deduce the same path from the same table name repeatedly. Consequently the intermediate table files must be handled like normal tables. Their internal names shall start with tmp_file_prefix (#sql) and they shall not be converted like normal table names. I added a flags parameter to all relevant functions that are called from ALTER TABLE. It is used to suppress the conversion for the intermediate table files. The outcome is that the suppression of #sql in SHOW TABLES works again. It does not suppress user tables as these are converted to @0023sql on file level. This patch does also fix ALTER TABLE ... RENAME, which could not rename a table with non-ASCII characters in its name. It does also fix the problem that a user could create a table like `#sql-xxxx-yyyy`, where xxxx is mysqld's pid and yyyy is the thread ID of some other thread, which prevented this thread from running ALTER TABLE. Some of the above problems are mentioned in Bug 1405, which can be closed with this patch. This patch does also contain some minor fixes for other forgotten conversions. Still known problems are reported as bugs 21370, 21373, and 21387.
20 years ago
Bug#18775 - Temporary table from alter table visible to other threads Continued implementation of WL#1324 (table name to filename encoding) The intermediate (not temporary) files of the new table during ALTER TABLE was visible for SHOW TABLES. These intermediate files are copies of the original table with the changes done by ALTER TABLE. After all the data is copied over from the original table, these files are renamed to the original tables file names. So they are not temporary files. They persist after ALTER TABLE, but just with another name. In 5.0 the intermediate files are invisible for SHOW TABLES because all file names beginning with "#sql" were suppressed. This failed since 5.1.6 because even temporary table names were converted when making file names from them. The prefix became converted to "@0023sql". Converting the prefix during SHOW TABLES would suppress the listing of user tables that start with "#sql". The solution of the problem is to continue the implementation of the table name to file name conversion feature. One requirement is to suppress the conversion for temporary table names. This change is straightforward for real temporary tables as there is a function that creates temporary file names. But the generated path names are located in TMPDIR and have no relation to the internal table name. This cannot be used for ALTER TABLE. Its intermediate files need to be in the same directory as the old table files. And it is necessary to be able to deduce the same path from the same table name repeatedly. Consequently the intermediate table files must be handled like normal tables. Their internal names shall start with tmp_file_prefix (#sql) and they shall not be converted like normal table names. I added a flags parameter to all relevant functions that are called from ALTER TABLE. It is used to suppress the conversion for the intermediate table files. The outcome is that the suppression of #sql in SHOW TABLES works again. It does not suppress user tables as these are converted to @0023sql on file level. This patch does also fix ALTER TABLE ... RENAME, which could not rename a table with non-ASCII characters in its name. It does also fix the problem that a user could create a table like `#sql-xxxx-yyyy`, where xxxx is mysqld's pid and yyyy is the thread ID of some other thread, which prevented this thread from running ALTER TABLE. Some of the above problems are mentioned in Bug 1405, which can be closed with this patch. This patch does also contain some minor fixes for other forgotten conversions. Still known problems are reported as bugs 21370, 21373, and 21387.
20 years ago
20 years ago
5.1 version of a fix and test cases for bugs: Bug#4968 ""Stored procedure crash if cursor opened on altered table" Bug#6895 "Prepared Statements: ALTER TABLE DROP COLUMN does nothing" Bug#19182 "CREATE TABLE bar (m INT) SELECT n FROM foo; doesn't work from stored procedure." Bug#19733 "Repeated alter, or repeated create/drop, fails" Bug#22060 "ALTER TABLE x AUTO_INCREMENT=y in SP crashes server" Bug#24879 "Prepared Statements: CREATE TABLE (UTF8 KEY) produces a growing key length" (this bug is not fixed in 5.0) Re-execution of CREATE DATABASE, CREATE TABLE and ALTER TABLE statements in stored routines or as prepared statements caused incorrect results (and crashes in versions prior to 5.0.25). In 5.1 the problem occured only for CREATE DATABASE, CREATE TABLE SELECT and CREATE TABLE with INDEX/DATA DIRECTOY options). The problem of bugs 4968, 19733, 19282 and 6895 was that functions mysql_prepare_table, mysql_create_table and mysql_alter_table are not re-execution friendly: during their operation they modify contents of LEX (members create_info, alter_info, key_list, create_list), thus making the LEX unusable for the next execution. In particular, these functions removed processed columns and keys from create_list, key_list and drop_list. Search the code in sql_table.cc for drop_it.remove() and similar patterns to find evidence. The fix is to supply to these functions a usable copy of each of the above structures at every re-execution of an SQL statement. To simplify memory management, LEX::key_list and LEX::create_list were added to LEX::alter_info, a fresh copy of which is created for every execution. The problem of crashing bug 22060 stemmed from the fact that the above metnioned functions were not only modifying HA_CREATE_INFO structure in LEX, but also were changing it to point to areas in volatile memory of the execution memory root. The patch solves this problem by creating and using an on-stack copy of HA_CREATE_INFO in mysql_execute_command. Additionally, this patch splits the part of mysql_alter_table that analizes and rewrites information from the parser into a separate function - mysql_prepare_alter_table, in analogy with mysql_prepare_table, which is renamed to mysql_prepare_create_table.
19 years ago
5.1 version of a fix and test cases for bugs: Bug#4968 ""Stored procedure crash if cursor opened on altered table" Bug#6895 "Prepared Statements: ALTER TABLE DROP COLUMN does nothing" Bug#19182 "CREATE TABLE bar (m INT) SELECT n FROM foo; doesn't work from stored procedure." Bug#19733 "Repeated alter, or repeated create/drop, fails" Bug#22060 "ALTER TABLE x AUTO_INCREMENT=y in SP crashes server" Bug#24879 "Prepared Statements: CREATE TABLE (UTF8 KEY) produces a growing key length" (this bug is not fixed in 5.0) Re-execution of CREATE DATABASE, CREATE TABLE and ALTER TABLE statements in stored routines or as prepared statements caused incorrect results (and crashes in versions prior to 5.0.25). In 5.1 the problem occured only for CREATE DATABASE, CREATE TABLE SELECT and CREATE TABLE with INDEX/DATA DIRECTOY options). The problem of bugs 4968, 19733, 19282 and 6895 was that functions mysql_prepare_table, mysql_create_table and mysql_alter_table are not re-execution friendly: during their operation they modify contents of LEX (members create_info, alter_info, key_list, create_list), thus making the LEX unusable for the next execution. In particular, these functions removed processed columns and keys from create_list, key_list and drop_list. Search the code in sql_table.cc for drop_it.remove() and similar patterns to find evidence. The fix is to supply to these functions a usable copy of each of the above structures at every re-execution of an SQL statement. To simplify memory management, LEX::key_list and LEX::create_list were added to LEX::alter_info, a fresh copy of which is created for every execution. The problem of crashing bug 22060 stemmed from the fact that the above metnioned functions were not only modifying HA_CREATE_INFO structure in LEX, but also were changing it to point to areas in volatile memory of the execution memory root. The patch solves this problem by creating and using an on-stack copy of HA_CREATE_INFO in mysql_execute_command. Additionally, this patch splits the part of mysql_alter_table that analizes and rewrites information from the parser into a separate function - mysql_prepare_alter_table, in analogy with mysql_prepare_table, which is renamed to mysql_prepare_create_table.
19 years ago
5.1 version of a fix and test cases for bugs: Bug#4968 ""Stored procedure crash if cursor opened on altered table" Bug#6895 "Prepared Statements: ALTER TABLE DROP COLUMN does nothing" Bug#19182 "CREATE TABLE bar (m INT) SELECT n FROM foo; doesn't work from stored procedure." Bug#19733 "Repeated alter, or repeated create/drop, fails" Bug#22060 "ALTER TABLE x AUTO_INCREMENT=y in SP crashes server" Bug#24879 "Prepared Statements: CREATE TABLE (UTF8 KEY) produces a growing key length" (this bug is not fixed in 5.0) Re-execution of CREATE DATABASE, CREATE TABLE and ALTER TABLE statements in stored routines or as prepared statements caused incorrect results (and crashes in versions prior to 5.0.25). In 5.1 the problem occured only for CREATE DATABASE, CREATE TABLE SELECT and CREATE TABLE with INDEX/DATA DIRECTOY options). The problem of bugs 4968, 19733, 19282 and 6895 was that functions mysql_prepare_table, mysql_create_table and mysql_alter_table are not re-execution friendly: during their operation they modify contents of LEX (members create_info, alter_info, key_list, create_list), thus making the LEX unusable for the next execution. In particular, these functions removed processed columns and keys from create_list, key_list and drop_list. Search the code in sql_table.cc for drop_it.remove() and similar patterns to find evidence. The fix is to supply to these functions a usable copy of each of the above structures at every re-execution of an SQL statement. To simplify memory management, LEX::key_list and LEX::create_list were added to LEX::alter_info, a fresh copy of which is created for every execution. The problem of crashing bug 22060 stemmed from the fact that the above metnioned functions were not only modifying HA_CREATE_INFO structure in LEX, but also were changing it to point to areas in volatile memory of the execution memory root. The patch solves this problem by creating and using an on-stack copy of HA_CREATE_INFO in mysql_execute_command. Additionally, this patch splits the part of mysql_alter_table that analizes and rewrites information from the parser into a separate function - mysql_prepare_alter_table, in analogy with mysql_prepare_table, which is renamed to mysql_prepare_create_table.
19 years ago
5.1 version of a fix and test cases for bugs: Bug#4968 ""Stored procedure crash if cursor opened on altered table" Bug#6895 "Prepared Statements: ALTER TABLE DROP COLUMN does nothing" Bug#19182 "CREATE TABLE bar (m INT) SELECT n FROM foo; doesn't work from stored procedure." Bug#19733 "Repeated alter, or repeated create/drop, fails" Bug#22060 "ALTER TABLE x AUTO_INCREMENT=y in SP crashes server" Bug#24879 "Prepared Statements: CREATE TABLE (UTF8 KEY) produces a growing key length" (this bug is not fixed in 5.0) Re-execution of CREATE DATABASE, CREATE TABLE and ALTER TABLE statements in stored routines or as prepared statements caused incorrect results (and crashes in versions prior to 5.0.25). In 5.1 the problem occured only for CREATE DATABASE, CREATE TABLE SELECT and CREATE TABLE with INDEX/DATA DIRECTOY options). The problem of bugs 4968, 19733, 19282 and 6895 was that functions mysql_prepare_table, mysql_create_table and mysql_alter_table are not re-execution friendly: during their operation they modify contents of LEX (members create_info, alter_info, key_list, create_list), thus making the LEX unusable for the next execution. In particular, these functions removed processed columns and keys from create_list, key_list and drop_list. Search the code in sql_table.cc for drop_it.remove() and similar patterns to find evidence. The fix is to supply to these functions a usable copy of each of the above structures at every re-execution of an SQL statement. To simplify memory management, LEX::key_list and LEX::create_list were added to LEX::alter_info, a fresh copy of which is created for every execution. The problem of crashing bug 22060 stemmed from the fact that the above metnioned functions were not only modifying HA_CREATE_INFO structure in LEX, but also were changing it to point to areas in volatile memory of the execution memory root. The patch solves this problem by creating and using an on-stack copy of HA_CREATE_INFO in mysql_execute_command. Additionally, this patch splits the part of mysql_alter_table that analizes and rewrites information from the parser into a separate function - mysql_prepare_alter_table, in analogy with mysql_prepare_table, which is renamed to mysql_prepare_create_table.
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
Fix for BUG#11755168 '46895: test "outfile_loaddata" fails (reproducible)'. In sql_class.cc, 'row_count', of type 'ha_rows', was used as last argument for ER_TRUNCATED_WRONG_VALUE_FOR_FIELD which is "Incorrect %-.32s value: '%-.128s' for column '%.192s' at row %ld". So 'ha_rows' was used as 'long'. On SPARC32 Solaris builds, 'long' is 4 bytes and 'ha_rows' is 'longlong' i.e. 8 bytes. So the printf-like code was reading only the first 4 bytes. Because the CPU is big-endian, 1LL is 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 so the first four bytes yield 0. So the warning message had "row 0" instead of "row 1" in test outfile_loaddata.test: -Warning 1366 Incorrect string value: '\xE1\xE2\xF7' for column 'b' at row 1 +Warning 1366 Incorrect string value: '\xE1\xE2\xF7' for column 'b' at row 0 All error-messaging functions which internally invoke some printf-life function are potential candidate for such mistakes. One apparently easy way to catch such mistakes is to use ATTRIBUTE_FORMAT (from my_attribute.h). But this works only when call site has both: a) the format as a string literal b) the types of arguments. So: func(ER(ER_BLAH), 10); will silently not be checked, because ER(ER_BLAH) is not known at compile time (it is known at run-time, and depends on the chosen language). And func("%s", a va_list argument); has the same problem, as the *real* type of arguments is not known at this site at compile time (it's known in some caller). Moreover, func(ER(ER_BLAH)); though possibly correct (if ER(ER_BLAH) has no '%' markers), will not compile (gcc says "error: format not a string literal and no format arguments"). Consequences: 1) ATTRIBUTE_FORMAT is here added only to functions which in practice take "string literal" formats: "my_error_reporter" and "print_admin_msg". 2) it cannot be added to the other functions: my_error(), push_warning_printf(), Table_check_intact::report_error(), general_log_print(). To do a one-time check of functions listed in (2), the following "static code analysis" has been done: 1) replace my_error(ER_xxx, arguments for substitution in format) with the equivalent my_printf_error(ER_xxx,ER(ER_xxx), arguments for substitution in format), so that we have ER(ER_xxx) and the arguments *in the same call site* 2) add ATTRIBUTE_FORMAT to push_warning_printf(), Table_check_intact::report_error(), general_log_print() 3) replace ER(xxx) with the hard-coded English text found in errmsg.txt (like: ER(ER_UNKNOWN_ERROR) is replaced with "Unknown error"), so that a call site has the format as string literal 4) this way, ATTRIBUTE_FORMAT can effectively do its job 5) compile, fix errors detected by ATTRIBUTE_FORMAT 6) revert steps 1-2-3. The present patch has no compiler error when submitted again to the static code analysis above. It cannot catch all problems though: see Field::set_warning(), in which a call to push_warning_printf() has a variable error (thus, not replacable by a string literal); I checked set_warning() calls by hand though. See also WL 5883 for one proposal to avoid such bugs from appearing again in the future. The issues fixed in the patch are: a) mismatch in types (like 'int' passed to '%ld') b) more arguments passed than specified in the format. This patch resolves mismatches by changing the type/number of arguments, not by changing error messages of sql/share/errmsg.txt. The latter would be wrong, per the following old rule: errmsg.txt must be as stable as possible; no insertions or deletions of messages, no changes of type or number of printf-like format specifiers, are allowed, as long as the change impacts a message already released in a GA version. If this rule is not followed: - Connectors, which use error message numbers, will be confused (by insertions/deletions of messages) - using errmsg.sys of MySQL 5.1.n with mysqld of MySQL 5.1.(n+1) could produce wrong messages or crash; such usage can easily happen if installing 5.1.(n+1) while /etc/my.cnf still has --language=/path/to/5.1.n/xxx; or if copying mysqld from 5.1.(n+1) into a 5.1.n installation. When fixing b), I have verified that the superfluous arguments were not used in the format in the first 5.1 GA (5.1.30 'bteam@astra04-20081114162938-z8mctjp6st27uobm'). Had they been used, then passing them today, even if the message doesn't use them anymore, would have been necessary, as explained above.
15 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
19 years ago
20 years ago
20 years ago
20 years ago
  1. /* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of the GNU General Public License as published by
  4. the Free Software Foundation; version 2 of the License.
  5. This program is distributed in the hope that it will be useful,
  6. but WITHOUT ANY WARRANTY; without even the implied warranty of
  7. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8. GNU General Public License for more details.
  9. You should have received a copy of the GNU General Public License
  10. along with this program; if not, write to the Free Software
  11. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
  12. /*
  13. This file is a container for general functionality related
  14. to partitioning introduced in MySQL version 5.1. It contains functionality
  15. used by all handlers that support partitioning, such as
  16. the partitioning handler itself and the NDB handler.
  17. The first version was written by Mikael Ronstrom.
  18. This version supports RANGE partitioning, LIST partitioning, HASH
  19. partitioning and composite partitioning (hereafter called subpartitioning)
  20. where each RANGE/LIST partitioning is HASH partitioned. The hash function
  21. can either be supplied by the user or by only a list of fields (also
  22. called KEY partitioning), where the MySQL server will use an internal
  23. hash function.
  24. There are quite a few defaults that can be used as well.
  25. */
  26. /* Some general useful functions */
  27. #define MYSQL_LEX 1
  28. #include "mysql_priv.h"
  29. #include <errno.h>
  30. #include <m_ctype.h>
  31. #include "my_md5.h"
  32. #ifdef WITH_PARTITION_STORAGE_ENGINE
  33. #include "ha_partition.h"
  34. /*
  35. Partition related functions declarations and some static constants;
  36. */
  37. const LEX_STRING partition_keywords[]=
  38. {
  39. { C_STRING_WITH_LEN("HASH") },
  40. { C_STRING_WITH_LEN("RANGE") },
  41. { C_STRING_WITH_LEN("LIST") },
  42. { C_STRING_WITH_LEN("KEY") },
  43. { C_STRING_WITH_LEN("MAXVALUE") },
  44. { C_STRING_WITH_LEN("LINEAR ") }
  45. };
  46. static const char *part_str= "PARTITION";
  47. static const char *sub_str= "SUB";
  48. static const char *by_str= "BY";
  49. static const char *space_str= " ";
  50. static const char *equal_str= "=";
  51. static const char *end_paren_str= ")";
  52. static const char *begin_paren_str= "(";
  53. static const char *comma_str= ",";
  54. static int get_part_id_charset_func_all(partition_info *part_info,
  55. uint32 *part_id,
  56. longlong *func_value);
  57. static int get_part_id_charset_func_part(partition_info *part_info,
  58. uint32 *part_id,
  59. longlong *func_value);
  60. static int get_part_id_charset_func_subpart(partition_info *part_info,
  61. uint32 *part_id,
  62. longlong *func_value);
  63. static int get_part_part_id_charset_func(partition_info *part_info,
  64. uint32 *part_id,
  65. longlong *func_value);
  66. static int get_subpart_id_charset_func(partition_info *part_info,
  67. uint32 *part_id);
  68. int get_partition_id_list(partition_info *part_info,
  69. uint32 *part_id,
  70. longlong *func_value);
  71. int get_partition_id_range(partition_info *part_info,
  72. uint32 *part_id,
  73. longlong *func_value);
  74. int get_partition_id_hash_nosub(partition_info *part_info,
  75. uint32 *part_id,
  76. longlong *func_value);
  77. int get_partition_id_key_nosub(partition_info *part_info,
  78. uint32 *part_id,
  79. longlong *func_value);
  80. int get_partition_id_linear_hash_nosub(partition_info *part_info,
  81. uint32 *part_id,
  82. longlong *func_value);
  83. int get_partition_id_linear_key_nosub(partition_info *part_info,
  84. uint32 *part_id,
  85. longlong *func_value);
  86. int get_partition_id_range_sub_hash(partition_info *part_info,
  87. uint32 *part_id,
  88. longlong *func_value);
  89. int get_partition_id_range_sub_key(partition_info *part_info,
  90. uint32 *part_id,
  91. longlong *func_value);
  92. int get_partition_id_range_sub_linear_hash(partition_info *part_info,
  93. uint32 *part_id,
  94. longlong *func_value);
  95. int get_partition_id_range_sub_linear_key(partition_info *part_info,
  96. uint32 *part_id,
  97. longlong *func_value);
  98. int get_partition_id_list_sub_hash(partition_info *part_info,
  99. uint32 *part_id,
  100. longlong *func_value);
  101. int get_partition_id_list_sub_key(partition_info *part_info,
  102. uint32 *part_id,
  103. longlong *func_value);
  104. int get_partition_id_list_sub_linear_hash(partition_info *part_info,
  105. uint32 *part_id,
  106. longlong *func_value);
  107. int get_partition_id_list_sub_linear_key(partition_info *part_info,
  108. uint32 *part_id,
  109. longlong *func_value);
  110. int get_partition_id_hash_sub(partition_info *part_info,
  111. uint32 *part_id);
  112. int get_partition_id_key_sub(partition_info *part_info,
  113. uint32 *part_id);
  114. int get_partition_id_linear_hash_sub(partition_info *part_info,
  115. uint32 *part_id);
  116. int get_partition_id_linear_key_sub(partition_info *part_info,
  117. uint32 *part_id);
  118. static uint32 get_next_partition_via_walking(PARTITION_ITERATOR*);
  119. static void set_up_range_analysis_info(partition_info *part_info);
  120. static uint32 get_next_subpartition_via_walking(PARTITION_ITERATOR*);
  121. #endif
  122. uint32 get_next_partition_id_range(PARTITION_ITERATOR* part_iter);
  123. uint32 get_next_partition_id_list(PARTITION_ITERATOR* part_iter);
  124. int get_part_iter_for_interval_via_mapping(partition_info *part_info,
  125. bool is_subpart,
  126. uchar *min_value, uchar *max_value,
  127. uint flags,
  128. PARTITION_ITERATOR *part_iter);
  129. int get_part_iter_for_interval_via_walking(partition_info *part_info,
  130. bool is_subpart,
  131. uchar *min_value, uchar *max_value,
  132. uint flags,
  133. PARTITION_ITERATOR *part_iter);
  134. #ifdef WITH_PARTITION_STORAGE_ENGINE
  135. /*
  136. A support function to check if a name is in a list of strings
  137. SYNOPSIS
  138. is_name_in_list()
  139. name String searched for
  140. list_names A list of names searched in
  141. RETURN VALUES
  142. TRUE String found
  143. FALSE String not found
  144. */
  145. bool is_name_in_list(char *name,
  146. List<char> list_names)
  147. {
  148. List_iterator<char> names_it(list_names);
  149. uint no_names= list_names.elements;
  150. uint i= 0;
  151. do
  152. {
  153. char *list_name= names_it++;
  154. if (!(my_strcasecmp(system_charset_info, name, list_name)))
  155. return TRUE;
  156. } while (++i < no_names);
  157. return FALSE;
  158. }
  159. /*
  160. Set-up defaults for partitions.
  161. SYNOPSIS
  162. partition_default_handling()
  163. table Table object
  164. part_info Partition info to set up
  165. is_create_table_ind Is this part of a table creation
  166. normalized_path Normalized path name of table and database
  167. RETURN VALUES
  168. TRUE Error
  169. FALSE Success
  170. */
  171. bool partition_default_handling(TABLE *table, partition_info *part_info,
  172. bool is_create_table_ind,
  173. const char *normalized_path)
  174. {
  175. DBUG_ENTER("partition_default_handling");
  176. if (!is_create_table_ind)
  177. {
  178. if (part_info->use_default_no_partitions)
  179. {
  180. if (table->file->get_no_parts(normalized_path, &part_info->no_parts))
  181. {
  182. DBUG_RETURN(TRUE);
  183. }
  184. }
  185. else if (part_info->is_sub_partitioned() &&
  186. part_info->use_default_no_subpartitions)
  187. {
  188. uint no_parts;
  189. if (table->file->get_no_parts(normalized_path, &no_parts))
  190. {
  191. DBUG_RETURN(TRUE);
  192. }
  193. DBUG_ASSERT(part_info->no_parts > 0);
  194. DBUG_ASSERT((no_parts % part_info->no_parts) == 0);
  195. part_info->no_subparts= no_parts / part_info->no_parts;
  196. }
  197. }
  198. part_info->set_up_defaults_for_partitioning(table->file,
  199. (ulonglong)0, (uint)0);
  200. DBUG_RETURN(FALSE);
  201. }
  202. /*
  203. Check that the reorganized table will not have duplicate partitions.
  204. SYNOPSIS
  205. check_reorganise_list()
  206. new_part_info New partition info
  207. old_part_info Old partition info
  208. list_part_names The list of partition names that will go away and
  209. can be reused in the new table.
  210. RETURN VALUES
  211. TRUE Inacceptable name conflict detected.
  212. FALSE New names are OK.
  213. DESCRIPTION
  214. Can handle that the 'new_part_info' and 'old_part_info' the same
  215. in which case it checks that the list of names in the partitions
  216. doesn't contain any duplicated names.
  217. */
  218. bool check_reorganise_list(partition_info *new_part_info,
  219. partition_info *old_part_info,
  220. List<char> list_part_names)
  221. {
  222. uint new_count, old_count;
  223. uint no_new_parts= new_part_info->partitions.elements;
  224. uint no_old_parts= old_part_info->partitions.elements;
  225. List_iterator<partition_element> new_parts_it(new_part_info->partitions);
  226. bool same_part_info= (new_part_info == old_part_info);
  227. DBUG_ENTER("check_reorganise_list");
  228. new_count= 0;
  229. do
  230. {
  231. List_iterator<partition_element> old_parts_it(old_part_info->partitions);
  232. char *new_name= (new_parts_it++)->partition_name;
  233. new_count++;
  234. old_count= 0;
  235. do
  236. {
  237. char *old_name= (old_parts_it++)->partition_name;
  238. old_count++;
  239. if (same_part_info && old_count == new_count)
  240. break;
  241. if (!(my_strcasecmp(system_charset_info, old_name, new_name)))
  242. {
  243. if (!is_name_in_list(old_name, list_part_names))
  244. DBUG_RETURN(TRUE);
  245. }
  246. } while (old_count < no_old_parts);
  247. } while (new_count < no_new_parts);
  248. DBUG_RETURN(FALSE);
  249. }
  250. /*
  251. A useful routine used by update_row for partition handlers to calculate
  252. the partition ids of the old and the new record.
  253. SYNOPSIS
  254. get_part_for_update()
  255. old_data Buffer of old record
  256. new_data Buffer of new record
  257. rec0 Reference to table->record[0]
  258. part_info Reference to partition information
  259. out:old_part_id The returned partition id of old record
  260. out:new_part_id The returned partition id of new record
  261. RETURN VALUE
  262. 0 Success
  263. > 0 Error code
  264. */
  265. int get_parts_for_update(const uchar *old_data, uchar *new_data,
  266. const uchar *rec0, partition_info *part_info,
  267. uint32 *old_part_id, uint32 *new_part_id,
  268. longlong *new_func_value)
  269. {
  270. Field **part_field_array= part_info->full_part_field_array;
  271. int error;
  272. longlong old_func_value;
  273. DBUG_ENTER("get_parts_for_update");
  274. DBUG_ASSERT(new_data == rec0);
  275. set_field_ptr(part_field_array, old_data, rec0);
  276. error= part_info->get_partition_id(part_info, old_part_id,
  277. &old_func_value);
  278. set_field_ptr(part_field_array, rec0, old_data);
  279. if (unlikely(error)) // Should never happen
  280. {
  281. DBUG_ASSERT(0);
  282. DBUG_RETURN(error);
  283. }
  284. #ifdef NOT_NEEDED
  285. if (new_data == rec0)
  286. #endif
  287. {
  288. if (unlikely(error= part_info->get_partition_id(part_info,
  289. new_part_id,
  290. new_func_value)))
  291. {
  292. DBUG_RETURN(error);
  293. }
  294. }
  295. #ifdef NOT_NEEDED
  296. else
  297. {
  298. /*
  299. This branch should never execute but it is written anyways for
  300. future use. It will be tested by ensuring that the above
  301. condition is false in one test situation before pushing the code.
  302. */
  303. set_field_ptr(part_field_array, new_data, rec0);
  304. error= part_info->get_partition_id(part_info, new_part_id,
  305. new_func_value);
  306. set_field_ptr(part_field_array, rec0, new_data);
  307. if (unlikely(error))
  308. {
  309. DBUG_RETURN(error);
  310. }
  311. }
  312. #endif
  313. DBUG_RETURN(0);
  314. }
  315. /*
  316. A useful routine used by delete_row for partition handlers to calculate
  317. the partition id.
  318. SYNOPSIS
  319. get_part_for_delete()
  320. buf Buffer of old record
  321. rec0 Reference to table->record[0]
  322. part_info Reference to partition information
  323. out:part_id The returned partition id to delete from
  324. RETURN VALUE
  325. 0 Success
  326. > 0 Error code
  327. DESCRIPTION
  328. Dependent on whether buf is not record[0] we need to prepare the
  329. fields. Then we call the function pointer get_partition_id to
  330. calculate the partition id.
  331. */
  332. int get_part_for_delete(const uchar *buf, const uchar *rec0,
  333. partition_info *part_info, uint32 *part_id)
  334. {
  335. int error;
  336. longlong func_value;
  337. DBUG_ENTER("get_part_for_delete");
  338. if (likely(buf == rec0))
  339. {
  340. if (unlikely((error= part_info->get_partition_id(part_info, part_id,
  341. &func_value))))
  342. {
  343. DBUG_RETURN(error);
  344. }
  345. DBUG_PRINT("info", ("Delete from partition %d", *part_id));
  346. }
  347. else
  348. {
  349. Field **part_field_array= part_info->full_part_field_array;
  350. set_field_ptr(part_field_array, buf, rec0);
  351. error= part_info->get_partition_id(part_info, part_id, &func_value);
  352. set_field_ptr(part_field_array, rec0, buf);
  353. if (unlikely(error))
  354. {
  355. DBUG_RETURN(error);
  356. }
  357. DBUG_PRINT("info", ("Delete from partition %d (path2)", *part_id));
  358. }
  359. DBUG_RETURN(0);
  360. }
  361. /*
  362. This method is used to set-up both partition and subpartitioning
  363. field array and used for all types of partitioning.
  364. It is part of the logic around fix_partition_func.
  365. SYNOPSIS
  366. set_up_field_array()
  367. table TABLE object for which partition fields are set-up
  368. sub_part Is the table subpartitioned as well
  369. RETURN VALUE
  370. TRUE Error, some field didn't meet requirements
  371. FALSE Ok, partition field array set-up
  372. DESCRIPTION
  373. A great number of functions below here is part of the fix_partition_func
  374. method. It is used to set up the partition structures for execution from
  375. openfrm. It is called at the end of the openfrm when the table struct has
  376. been set-up apart from the partition information.
  377. It involves:
  378. 1) Setting arrays of fields for the partition functions.
  379. 2) Setting up binary search array for LIST partitioning
  380. 3) Setting up array for binary search for RANGE partitioning
  381. 4) Setting up key_map's to assist in quick evaluation whether one
  382. can deduce anything from a given index of what partition to use
  383. 5) Checking whether a set of partitions can be derived from a range on
  384. a field in the partition function.
  385. As part of doing this there is also a great number of error controls.
  386. This is actually the place where most of the things are checked for
  387. partition information when creating a table.
  388. Things that are checked includes
  389. 1) All fields of partition function in Primary keys and unique indexes
  390. (if not supported)
  391. Create an array of partition fields (NULL terminated). Before this method
  392. is called fix_fields or find_table_in_sef has been called to set
  393. GET_FIXED_FIELDS_FLAG on all fields that are part of the partition
  394. function.
  395. */
  396. static bool set_up_field_array(TABLE *table,
  397. bool is_sub_part)
  398. {
  399. Field **ptr, *field, **field_array;
  400. uint no_fields= 0;
  401. uint size_field_array;
  402. uint i= 0;
  403. partition_info *part_info= table->part_info;
  404. int result= FALSE;
  405. DBUG_ENTER("set_up_field_array");
  406. ptr= table->field;
  407. while ((field= *(ptr++)))
  408. {
  409. if (field->flags & GET_FIXED_FIELDS_FLAG)
  410. no_fields++;
  411. }
  412. if (no_fields == 0)
  413. {
  414. /*
  415. We are using hidden key as partitioning field
  416. */
  417. DBUG_ASSERT(!is_sub_part);
  418. DBUG_RETURN(result);
  419. }
  420. size_field_array= (no_fields+1)*sizeof(Field*);
  421. field_array= (Field**)sql_alloc(size_field_array);
  422. if (unlikely(!field_array))
  423. {
  424. mem_alloc_error(size_field_array);
  425. result= TRUE;
  426. }
  427. ptr= table->field;
  428. while ((field= *(ptr++)))
  429. {
  430. if (field->flags & GET_FIXED_FIELDS_FLAG)
  431. {
  432. field->flags&= ~GET_FIXED_FIELDS_FLAG;
  433. field->flags|= FIELD_IN_PART_FUNC_FLAG;
  434. if (likely(!result))
  435. {
  436. field_array[i++]= field;
  437. /*
  438. We check that the fields are proper. It is required for each
  439. field in a partition function to:
  440. 1) Not be a BLOB of any type
  441. A BLOB takes too long time to evaluate so we don't want it for
  442. performance reasons.
  443. */
  444. if (unlikely(field->flags & BLOB_FLAG))
  445. {
  446. my_error(ER_BLOB_FIELD_IN_PART_FUNC_ERROR, MYF(0));
  447. result= TRUE;
  448. }
  449. }
  450. }
  451. }
  452. field_array[no_fields]= 0;
  453. if (!is_sub_part)
  454. {
  455. part_info->part_field_array= field_array;
  456. part_info->no_part_fields= no_fields;
  457. }
  458. else
  459. {
  460. part_info->subpart_field_array= field_array;
  461. part_info->no_subpart_fields= no_fields;
  462. }
  463. DBUG_RETURN(result);
  464. }
  465. /*
  466. Create a field array including all fields of both the partitioning and the
  467. subpartitioning functions.
  468. SYNOPSIS
  469. create_full_part_field_array()
  470. thd Thread handle
  471. table TABLE object for which partition fields are set-up
  472. part_info Reference to partitioning data structure
  473. RETURN VALUE
  474. TRUE Memory allocation of field array failed
  475. FALSE Ok
  476. DESCRIPTION
  477. If there is no subpartitioning then the same array is used as for the
  478. partitioning. Otherwise a new array is built up using the flag
  479. FIELD_IN_PART_FUNC in the field object.
  480. This function is called from fix_partition_func
  481. */
  482. static bool create_full_part_field_array(THD *thd, TABLE *table,
  483. partition_info *part_info)
  484. {
  485. bool result= FALSE;
  486. Field **ptr;
  487. my_bitmap_map *bitmap_buf;
  488. DBUG_ENTER("create_full_part_field_array");
  489. if (!part_info->is_sub_partitioned())
  490. {
  491. part_info->full_part_field_array= part_info->part_field_array;
  492. part_info->no_full_part_fields= part_info->no_part_fields;
  493. }
  494. else
  495. {
  496. Field *field, **field_array;
  497. uint no_part_fields=0, size_field_array;
  498. ptr= table->field;
  499. while ((field= *(ptr++)))
  500. {
  501. if (field->flags & FIELD_IN_PART_FUNC_FLAG)
  502. no_part_fields++;
  503. }
  504. size_field_array= (no_part_fields+1)*sizeof(Field*);
  505. field_array= (Field**)sql_alloc(size_field_array);
  506. if (unlikely(!field_array))
  507. {
  508. mem_alloc_error(size_field_array);
  509. result= TRUE;
  510. goto end;
  511. }
  512. no_part_fields= 0;
  513. ptr= table->field;
  514. while ((field= *(ptr++)))
  515. {
  516. if (field->flags & FIELD_IN_PART_FUNC_FLAG)
  517. field_array[no_part_fields++]= field;
  518. }
  519. field_array[no_part_fields]=0;
  520. part_info->full_part_field_array= field_array;
  521. part_info->no_full_part_fields= no_part_fields;
  522. }
  523. /*
  524. Initialize the set of all fields used in partition and subpartition
  525. expression. Required for testing of partition fields in write_set
  526. when updating. We need to set all bits in read_set because the row
  527. may need to be inserted in a different [sub]partition.
  528. */
  529. if (!(bitmap_buf= (my_bitmap_map*)
  530. thd->alloc(bitmap_buffer_size(table->s->fields))))
  531. {
  532. mem_alloc_error(bitmap_buffer_size(table->s->fields));
  533. result= TRUE;
  534. goto end;
  535. }
  536. if (bitmap_init(&part_info->full_part_field_set, bitmap_buf,
  537. table->s->fields, FALSE))
  538. {
  539. mem_alloc_error(table->s->fields);
  540. result= TRUE;
  541. goto end;
  542. }
  543. /*
  544. full_part_field_array may be NULL if storage engine supports native
  545. partitioning.
  546. */
  547. if ((ptr= part_info->full_part_field_array))
  548. for (; *ptr; ptr++)
  549. bitmap_set_bit(&part_info->full_part_field_set, (*ptr)->field_index);
  550. end:
  551. DBUG_RETURN(result);
  552. }
  553. /*
  554. Clear flag GET_FIXED_FIELDS_FLAG in all fields of a key previously set by
  555. set_indicator_in_key_fields (always used in pairs).
  556. SYNOPSIS
  557. clear_indicator_in_key_fields()
  558. key_info Reference to find the key fields
  559. RETURN VALUE
  560. NONE
  561. DESCRIPTION
  562. These support routines is used to set/reset an indicator of all fields
  563. in a certain key. It is used in conjunction with another support routine
  564. that traverse all fields in the PF to find if all or some fields in the
  565. PF is part of the key. This is used to check primary keys and unique
  566. keys involve all fields in PF (unless supported) and to derive the
  567. key_map's used to quickly decide whether the index can be used to
  568. derive which partitions are needed to scan.
  569. */
  570. static void clear_indicator_in_key_fields(KEY *key_info)
  571. {
  572. KEY_PART_INFO *key_part;
  573. uint key_parts= key_info->key_parts, i;
  574. for (i= 0, key_part=key_info->key_part; i < key_parts; i++, key_part++)
  575. key_part->field->flags&= (~GET_FIXED_FIELDS_FLAG);
  576. }
  577. /*
  578. Set flag GET_FIXED_FIELDS_FLAG in all fields of a key.
  579. SYNOPSIS
  580. set_indicator_in_key_fields
  581. key_info Reference to find the key fields
  582. RETURN VALUE
  583. NONE
  584. */
  585. static void set_indicator_in_key_fields(KEY *key_info)
  586. {
  587. KEY_PART_INFO *key_part;
  588. uint key_parts= key_info->key_parts, i;
  589. for (i= 0, key_part=key_info->key_part; i < key_parts; i++, key_part++)
  590. key_part->field->flags|= GET_FIXED_FIELDS_FLAG;
  591. }
  592. /*
  593. Check if all or some fields in partition field array is part of a key
  594. previously used to tag key fields.
  595. SYNOPSIS
  596. check_fields_in_PF()
  597. ptr Partition field array
  598. out:all_fields Is all fields of partition field array used in key
  599. out:some_fields Is some fields of partition field array used in key
  600. RETURN VALUE
  601. all_fields, some_fields
  602. */
  603. static void check_fields_in_PF(Field **ptr, bool *all_fields,
  604. bool *some_fields)
  605. {
  606. DBUG_ENTER("check_fields_in_PF");
  607. *all_fields= TRUE;
  608. *some_fields= FALSE;
  609. if ((!ptr) || !(*ptr))
  610. {
  611. *all_fields= FALSE;
  612. DBUG_VOID_RETURN;
  613. }
  614. do
  615. {
  616. /* Check if the field of the PF is part of the current key investigated */
  617. if ((*ptr)->flags & GET_FIXED_FIELDS_FLAG)
  618. *some_fields= TRUE;
  619. else
  620. *all_fields= FALSE;
  621. } while (*(++ptr));
  622. DBUG_VOID_RETURN;
  623. }
  624. /*
  625. Clear flag GET_FIXED_FIELDS_FLAG in all fields of the table.
  626. This routine is used for error handling purposes.
  627. SYNOPSIS
  628. clear_field_flag()
  629. table TABLE object for which partition fields are set-up
  630. RETURN VALUE
  631. NONE
  632. */
  633. static void clear_field_flag(TABLE *table)
  634. {
  635. Field **ptr;
  636. DBUG_ENTER("clear_field_flag");
  637. for (ptr= table->field; *ptr; ptr++)
  638. (*ptr)->flags&= (~GET_FIXED_FIELDS_FLAG);
  639. DBUG_VOID_RETURN;
  640. }
  641. /*
  642. find_field_in_table_sef finds the field given its name. All fields get
  643. GET_FIXED_FIELDS_FLAG set.
  644. SYNOPSIS
  645. handle_list_of_fields()
  646. it A list of field names for the partition function
  647. table TABLE object for which partition fields are set-up
  648. part_info Reference to partitioning data structure
  649. sub_part Is the table subpartitioned as well
  650. RETURN VALUE
  651. TRUE Fields in list of fields not part of table
  652. FALSE All fields ok and array created
  653. DESCRIPTION
  654. This routine sets-up the partition field array for KEY partitioning, it
  655. also verifies that all fields in the list of fields is actually a part of
  656. the table.
  657. */
  658. static bool handle_list_of_fields(List_iterator<char> it,
  659. TABLE *table,
  660. partition_info *part_info,
  661. bool is_sub_part)
  662. {
  663. Field *field;
  664. bool result;
  665. char *field_name;
  666. bool is_list_empty= TRUE;
  667. int fields_handled = 0;
  668. char* field_name_array[MAX_KEY];
  669. DBUG_ENTER("handle_list_of_fields");
  670. while ((field_name= it++))
  671. {
  672. is_list_empty= FALSE;
  673. field= find_field_in_table_sef(table, field_name);
  674. if (likely(field != 0))
  675. field->flags|= GET_FIXED_FIELDS_FLAG;
  676. else
  677. {
  678. my_error(ER_FIELD_NOT_FOUND_PART_ERROR, MYF(0));
  679. clear_field_flag(table);
  680. result= TRUE;
  681. goto end;
  682. }
  683. /*
  684. Check for duplicate fields in the list.
  685. Assuming that there are not many fields in the partition key list.
  686. If there were, it would be better to replace the for-loop
  687. with a more efficient algorithm.
  688. */
  689. field_name_array[fields_handled] = field_name;
  690. for (int i = 0; i < fields_handled; ++i)
  691. {
  692. if (my_strcasecmp(system_charset_info,
  693. field_name_array[i], field_name) == 0)
  694. {
  695. my_error(ER_FIELD_NOT_FOUND_PART_ERROR, MYF(0));
  696. DBUG_RETURN(TRUE);
  697. }
  698. }
  699. fields_handled++;
  700. }
  701. if (is_list_empty)
  702. {
  703. uint primary_key= table->s->primary_key;
  704. if (primary_key != MAX_KEY)
  705. {
  706. uint no_key_parts= table->key_info[primary_key].key_parts, i;
  707. /*
  708. In the case of an empty list we use primary key as partition key.
  709. */
  710. for (i= 0; i < no_key_parts; i++)
  711. {
  712. Field *field= table->key_info[primary_key].key_part[i].field;
  713. field->flags|= GET_FIXED_FIELDS_FLAG;
  714. }
  715. }
  716. else
  717. {
  718. if (table->s->db_type()->partition_flags &&
  719. (table->s->db_type()->partition_flags() & HA_USE_AUTO_PARTITION) &&
  720. (table->s->db_type()->partition_flags() & HA_CAN_PARTITION))
  721. {
  722. /*
  723. This engine can handle automatic partitioning and there is no
  724. primary key. In this case we rely on that the engine handles
  725. partitioning based on a hidden key. Thus we allocate no
  726. array for partitioning fields.
  727. */
  728. DBUG_RETURN(FALSE);
  729. }
  730. else
  731. {
  732. my_error(ER_FIELD_NOT_FOUND_PART_ERROR, MYF(0));
  733. DBUG_RETURN(TRUE);
  734. }
  735. }
  736. }
  737. result= set_up_field_array(table, is_sub_part);
  738. end:
  739. DBUG_RETURN(result);
  740. }
  741. /*
  742. Support function to check if all VALUES * (expression) is of the
  743. right sign (no signed constants when unsigned partition function)
  744. SYNOPSIS
  745. check_signed_flag()
  746. part_info Partition info object
  747. RETURN VALUES
  748. 0 No errors due to sign errors
  749. >0 Sign error
  750. */
  751. int check_signed_flag(partition_info *part_info)
  752. {
  753. int error= 0;
  754. uint i= 0;
  755. if (part_info->part_type != HASH_PARTITION &&
  756. part_info->part_expr->unsigned_flag)
  757. {
  758. List_iterator<partition_element> part_it(part_info->partitions);
  759. do
  760. {
  761. partition_element *part_elem= part_it++;
  762. if (part_elem->signed_flag)
  763. {
  764. my_error(ER_PARTITION_CONST_DOMAIN_ERROR, MYF(0));
  765. error= ER_PARTITION_CONST_DOMAIN_ERROR;
  766. break;
  767. }
  768. } while (++i < part_info->no_parts);
  769. }
  770. return error;
  771. }
  772. /*
  773. The function uses a new feature in fix_fields where the flag
  774. GET_FIXED_FIELDS_FLAG is set for all fields in the item tree.
  775. This field must always be reset before returning from the function
  776. since it is used for other purposes as well.
  777. SYNOPSIS
  778. fix_fields_part_func()
  779. thd The thread object
  780. func_expr The item tree reference of the partition function
  781. table The table object
  782. part_info Reference to partitioning data structure
  783. is_sub_part Is the table subpartitioned as well
  784. is_field_to_be_setup Flag if we are to set-up field arrays
  785. is_create_table_ind Indicator of whether openfrm was called as part of
  786. CREATE or ALTER TABLE
  787. RETURN VALUE
  788. TRUE An error occurred, something was wrong with the
  789. partition function.
  790. FALSE Ok, a partition field array was created
  791. DESCRIPTION
  792. This function is used to build an array of partition fields for the
  793. partitioning function and subpartitioning function. The partitioning
  794. function is an item tree that must reference at least one field in the
  795. table. This is checked first in the parser that the function doesn't
  796. contain non-cacheable parts (like a random function) and by checking
  797. here that the function isn't a constant function.
  798. Calculate the number of fields in the partition function.
  799. Use it allocate memory for array of Field pointers.
  800. Initialise array of field pointers. Use information set when
  801. calling fix_fields and reset it immediately after.
  802. The get_fields_in_item_tree activates setting of bit in flags
  803. on the field object.
  804. */
  805. static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table,
  806. bool is_sub_part, bool is_field_to_be_setup,
  807. bool is_create_table_ind)
  808. {
  809. partition_info *part_info= table->part_info;
  810. uint dir_length, home_dir_length;
  811. bool result= TRUE;
  812. TABLE_LIST tables;
  813. TABLE_LIST *save_table_list, *save_first_table, *save_last_table;
  814. int error;
  815. Name_resolution_context *context;
  816. const char *save_where;
  817. char* db_name;
  818. char db_name_string[FN_REFLEN];
  819. DBUG_ENTER("fix_fields_part_func");
  820. if (part_info->fixed)
  821. {
  822. if (!(is_sub_part || (error= check_signed_flag(part_info))))
  823. result= FALSE;
  824. goto end;
  825. }
  826. /*
  827. Set-up the TABLE_LIST object to be a list with a single table
  828. Set the object to zero to create NULL pointers and set alias
  829. and real name to table name and get database name from file name.
  830. TODO: Consider generalizing or refactoring Lex::add_table_to_list() so
  831. it can be used in all places where we create TABLE_LIST objects.
  832. Also consider creating appropriate constructors for TABLE_LIST.
  833. */
  834. bzero((void*)&tables, sizeof(TABLE_LIST));
  835. tables.alias= tables.table_name= (char*) table->s->table_name.str;
  836. tables.table= table;
  837. tables.next_local= 0;
  838. tables.next_name_resolution_table= 0;
  839. /*
  840. Cache the table in Item_fields. All the tables can be cached except
  841. the trigger pseudo table.
  842. */
  843. tables.cacheable_table= TRUE;
  844. context= thd->lex->current_context();
  845. tables.select_lex= context->select_lex;
  846. strmov(db_name_string, table->s->normalized_path.str);
  847. dir_length= dirname_length(db_name_string);
  848. db_name_string[dir_length - 1]= 0;
  849. home_dir_length= dirname_length(db_name_string);
  850. db_name= &db_name_string[home_dir_length];
  851. tables.db= db_name;
  852. table->map= 1; //To ensure correct calculation of const item
  853. table->get_fields_in_item_tree= TRUE;
  854. save_table_list= context->table_list;
  855. save_first_table= context->first_name_resolution_table;
  856. save_last_table= context->last_name_resolution_table;
  857. context->table_list= &tables;
  858. context->first_name_resolution_table= &tables;
  859. context->last_name_resolution_table= NULL;
  860. func_expr->walk(&Item::change_context_processor, 0, (uchar*) context);
  861. save_where= thd->where;
  862. thd->where= "partition function";
  863. /*
  864. In execution we must avoid the use of thd->change_item_tree since
  865. we might release memory before statement is completed. We do this
  866. by temporarily setting the stmt_arena->mem_root to be the mem_root
  867. of the table object, this also ensures that any memory allocated
  868. during fix_fields will not be released at end of execution of this
  869. statement. Thus the item tree will remain valid also in subsequent
  870. executions of this table object. We do however not at the moment
  871. support allocations during execution of val_int so any item class
  872. that does this during val_int must be disallowed as partition
  873. function.
  874. SEE Bug #21658
  875. */
  876. /*
  877. This is a tricky call to prepare for since it can have a large number
  878. of interesting side effects, both desirable and undesirable.
  879. */
  880. {
  881. const bool save_use_only_table_context= thd->lex->use_only_table_context;
  882. thd->lex->use_only_table_context= TRUE;
  883. thd->lex->current_select->cur_pos_in_select_list= UNDEF_POS;
  884. const bool save_agg_field= thd->lex->current_select->non_agg_field_used();
  885. const bool save_agg_func= thd->lex->current_select->agg_func_used();
  886. const nesting_map saved_allow_sum_func= thd->lex->allow_sum_func;
  887. thd->lex->allow_sum_func= 0;
  888. error= func_expr->fix_fields(thd, (Item**)&func_expr);
  889. /*
  890. Restore agg_field/agg_func and allow_sum_func,
  891. fix_fields should not affect mysql_select later, see Bug#46923.
  892. */
  893. thd->lex->current_select->set_non_agg_field_used(save_agg_field);
  894. thd->lex->current_select->set_agg_func_used(save_agg_func);
  895. thd->lex->allow_sum_func= saved_allow_sum_func;
  896. thd->lex->use_only_table_context= save_use_only_table_context;
  897. }
  898. context->table_list= save_table_list;
  899. context->first_name_resolution_table= save_first_table;
  900. context->last_name_resolution_table= save_last_table;
  901. if (unlikely(error))
  902. {
  903. DBUG_PRINT("info", ("Field in partition function not part of table"));
  904. if (is_field_to_be_setup)
  905. clear_field_flag(table);
  906. goto end;
  907. }
  908. thd->where= save_where;
  909. if (unlikely(func_expr->const_item()))
  910. {
  911. my_error(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR, MYF(0));
  912. clear_field_flag(table);
  913. goto end;
  914. }
  915. /*
  916. We don't allow creating partitions with expressions with non matching
  917. arguments as a (sub)partitioning function,
  918. but we want to allow such expressions when opening existing tables for
  919. easier maintenance. This exception should be deprecated at some point
  920. in future so that we always throw an error.
  921. */
  922. if (func_expr->walk(&Item::check_valid_arguments_processor,
  923. 0, NULL))
  924. {
  925. if (is_create_table_ind)
  926. {
  927. my_error(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR, MYF(0));
  928. goto end;
  929. }
  930. else
  931. push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
  932. ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR,
  933. ER(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR));
  934. }
  935. if ((!is_sub_part) && (error= check_signed_flag(part_info)))
  936. goto end;
  937. result= FALSE;
  938. if (is_field_to_be_setup)
  939. result= set_up_field_array(table, is_sub_part);
  940. if (!is_sub_part)
  941. part_info->fixed= TRUE;
  942. end:
  943. table->get_fields_in_item_tree= FALSE;
  944. table->map= 0; //Restore old value
  945. DBUG_RETURN(result);
  946. }
  947. /*
  948. Check that the primary key contains all partition fields if defined
  949. SYNOPSIS
  950. check_primary_key()
  951. table TABLE object for which partition fields are set-up
  952. RETURN VALUES
  953. TRUE Not all fields in partitioning function was part
  954. of primary key
  955. FALSE Ok, all fields of partitioning function were part
  956. of primary key
  957. DESCRIPTION
  958. This function verifies that if there is a primary key that it contains
  959. all the fields of the partition function.
  960. This is a temporary limitation that will hopefully be removed after a
  961. while.
  962. */
  963. static bool check_primary_key(TABLE *table)
  964. {
  965. uint primary_key= table->s->primary_key;
  966. bool all_fields, some_fields;
  967. bool result= FALSE;
  968. DBUG_ENTER("check_primary_key");
  969. if (primary_key < MAX_KEY)
  970. {
  971. set_indicator_in_key_fields(table->key_info+primary_key);
  972. check_fields_in_PF(table->part_info->full_part_field_array,
  973. &all_fields, &some_fields);
  974. clear_indicator_in_key_fields(table->key_info+primary_key);
  975. if (unlikely(!all_fields))
  976. {
  977. my_error(ER_UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF,MYF(0),"PRIMARY KEY");
  978. result= TRUE;
  979. }
  980. }
  981. DBUG_RETURN(result);
  982. }
  983. /*
  984. Check that unique keys contains all partition fields
  985. SYNOPSIS
  986. check_unique_keys()
  987. table TABLE object for which partition fields are set-up
  988. RETURN VALUES
  989. TRUE Not all fields in partitioning function was part
  990. of all unique keys
  991. FALSE Ok, all fields of partitioning function were part
  992. of unique keys
  993. DESCRIPTION
  994. This function verifies that if there is a unique index that it contains
  995. all the fields of the partition function.
  996. This is a temporary limitation that will hopefully be removed after a
  997. while.
  998. */
  999. static bool check_unique_keys(TABLE *table)
  1000. {
  1001. bool all_fields, some_fields;
  1002. bool result= FALSE;
  1003. uint keys= table->s->keys;
  1004. uint i;
  1005. DBUG_ENTER("check_unique_keys");
  1006. for (i= 0; i < keys; i++)
  1007. {
  1008. if (table->key_info[i].flags & HA_NOSAME) //Unique index
  1009. {
  1010. set_indicator_in_key_fields(table->key_info+i);
  1011. check_fields_in_PF(table->part_info->full_part_field_array,
  1012. &all_fields, &some_fields);
  1013. clear_indicator_in_key_fields(table->key_info+i);
  1014. if (unlikely(!all_fields))
  1015. {
  1016. my_error(ER_UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF,MYF(0),"UNIQUE INDEX");
  1017. result= TRUE;
  1018. break;
  1019. }
  1020. }
  1021. }
  1022. DBUG_RETURN(result);
  1023. }
  1024. /*
  1025. An important optimisation is whether a range on a field can select a subset
  1026. of the partitions.
  1027. A prerequisite for this to happen is that the PF is a growing function OR
  1028. a shrinking function.
  1029. This can never happen for a multi-dimensional PF. Thus this can only happen
  1030. with PF with at most one field involved in the PF.
  1031. The idea is that if the function is a growing function and you know that
  1032. the field of the PF is 4 <= A <= 6 then we can convert this to a range
  1033. in the PF instead by setting the range to PF(4) <= PF(A) <= PF(6). In the
  1034. case of RANGE PARTITIONING and LIST PARTITIONING this can be used to
  1035. calculate a set of partitions rather than scanning all of them.
  1036. Thus the following prerequisites are there to check if sets of partitions
  1037. can be found.
  1038. 1) Only possible for RANGE and LIST partitioning (not for subpartitioning)
  1039. 2) Only possible if PF only contains 1 field
  1040. 3) Possible if PF is a growing function of the field
  1041. 4) Possible if PF is a shrinking function of the field
  1042. OBSERVATION:
  1043. 1) IF f1(A) is a growing function AND f2(A) is a growing function THEN
  1044. f1(A) + f2(A) is a growing function
  1045. f1(A) * f2(A) is a growing function if f1(A) >= 0 and f2(A) >= 0
  1046. 2) IF f1(A) is a growing function and f2(A) is a shrinking function THEN
  1047. f1(A) / f2(A) is a growing function if f1(A) >= 0 and f2(A) > 0
  1048. 3) IF A is a growing function then a function f(A) that removes the
  1049. least significant portion of A is a growing function
  1050. E.g. DATE(datetime) is a growing function
  1051. MONTH(datetime) is not a growing/shrinking function
  1052. 4) IF f1(A) is a growing function and f2(A) is a growing function THEN
  1053. f1(f2(A)) and f2(f1(A)) are also growing functions
  1054. 5) IF f1(A) is a shrinking function and f2(A) is a growing function THEN
  1055. f1(f2(A)) is a shrinking function and f2(f1(A)) is a shrinking function
  1056. 6) f1(A) = A is a growing function
  1057. 7) f1(A) = A*a + b (where a and b are constants) is a growing function
  1058. By analysing the item tree of the PF we can use these deducements and
  1059. derive whether the PF is a growing function or a shrinking function or
  1060. neither of it.
  1061. If the PF is range capable then a flag is set on the table object
  1062. indicating this to notify that we can use also ranges on the field
  1063. of the PF to deduce a set of partitions if the fields of the PF were
  1064. not all fully bound.
  1065. SYNOPSIS
  1066. check_range_capable_PF()
  1067. table TABLE object for which partition fields are set-up
  1068. DESCRIPTION
  1069. Support for this is not implemented yet.
  1070. */
  1071. void check_range_capable_PF(TABLE *table)
  1072. {
  1073. DBUG_ENTER("check_range_capable_PF");
  1074. DBUG_VOID_RETURN;
  1075. }
  1076. /*
  1077. Set up partition bitmap
  1078. SYNOPSIS
  1079. set_up_partition_bitmap()
  1080. thd Thread object
  1081. part_info Reference to partitioning data structure
  1082. RETURN VALUE
  1083. TRUE Memory allocation failure
  1084. FALSE Success
  1085. DESCRIPTION
  1086. Allocate memory for bitmap of the partitioned table
  1087. and initialise it.
  1088. */
  1089. static bool set_up_partition_bitmap(THD *thd, partition_info *part_info)
  1090. {
  1091. uint32 *bitmap_buf;
  1092. uint bitmap_bits= part_info->no_subparts?
  1093. (part_info->no_subparts* part_info->no_parts):
  1094. part_info->no_parts;
  1095. uint bitmap_bytes= bitmap_buffer_size(bitmap_bits);
  1096. DBUG_ENTER("set_up_partition_bitmap");
  1097. if (!(bitmap_buf= (uint32*)thd->alloc(bitmap_bytes)))
  1098. {
  1099. mem_alloc_error(bitmap_bytes);
  1100. DBUG_RETURN(TRUE);
  1101. }
  1102. bitmap_init(&part_info->used_partitions, bitmap_buf, bitmap_bytes*8, FALSE);
  1103. bitmap_set_all(&part_info->used_partitions);
  1104. DBUG_RETURN(FALSE);
  1105. }
  1106. /*
  1107. Set up partition key maps
  1108. SYNOPSIS
  1109. set_up_partition_key_maps()
  1110. table TABLE object for which partition fields are set-up
  1111. part_info Reference to partitioning data structure
  1112. RETURN VALUES
  1113. None
  1114. DESCRIPTION
  1115. This function sets up a couple of key maps to be able to quickly check
  1116. if an index ever can be used to deduce the partition fields or even
  1117. a part of the fields of the partition function.
  1118. We set up the following key_map's.
  1119. PF = Partition Function
  1120. 1) All fields of the PF is set even by equal on the first fields in the
  1121. key
  1122. 2) All fields of the PF is set if all fields of the key is set
  1123. 3) At least one field in the PF is set if all fields is set
  1124. 4) At least one field in the PF is part of the key
  1125. */
  1126. static void set_up_partition_key_maps(TABLE *table,
  1127. partition_info *part_info)
  1128. {
  1129. uint keys= table->s->keys;
  1130. uint i;
  1131. bool all_fields, some_fields;
  1132. DBUG_ENTER("set_up_partition_key_maps");
  1133. part_info->all_fields_in_PF.clear_all();
  1134. part_info->all_fields_in_PPF.clear_all();
  1135. part_info->all_fields_in_SPF.clear_all();
  1136. part_info->some_fields_in_PF.clear_all();
  1137. for (i= 0; i < keys; i++)
  1138. {
  1139. set_indicator_in_key_fields(table->key_info+i);
  1140. check_fields_in_PF(part_info->full_part_field_array,
  1141. &all_fields, &some_fields);
  1142. if (all_fields)
  1143. part_info->all_fields_in_PF.set_bit(i);
  1144. if (some_fields)
  1145. part_info->some_fields_in_PF.set_bit(i);
  1146. if (part_info->is_sub_partitioned())
  1147. {
  1148. check_fields_in_PF(part_info->part_field_array,
  1149. &all_fields, &some_fields);
  1150. if (all_fields)
  1151. part_info->all_fields_in_PPF.set_bit(i);
  1152. check_fields_in_PF(part_info->subpart_field_array,
  1153. &all_fields, &some_fields);
  1154. if (all_fields)
  1155. part_info->all_fields_in_SPF.set_bit(i);
  1156. }
  1157. clear_indicator_in_key_fields(table->key_info+i);
  1158. }
  1159. DBUG_VOID_RETURN;
  1160. }
  1161. /*
  1162. Set up function pointers for partition function
  1163. SYNOPSIS
  1164. set_up_partition_func_pointers()
  1165. part_info Reference to partitioning data structure
  1166. RETURN VALUE
  1167. NONE
  1168. DESCRIPTION
  1169. Set-up all function pointers for calculation of partition id,
  1170. subpartition id and the upper part in subpartitioning. This is to speed up
  1171. execution of get_partition_id which is executed once every record to be
  1172. written and deleted and twice for updates.
  1173. */
  1174. static void set_up_partition_func_pointers(partition_info *part_info)
  1175. {
  1176. DBUG_ENTER("set_up_partition_func_pointers");
  1177. if (part_info->is_sub_partitioned())
  1178. {
  1179. if (part_info->part_type == RANGE_PARTITION)
  1180. {
  1181. part_info->get_part_partition_id= get_partition_id_range;
  1182. if (part_info->list_of_subpart_fields)
  1183. {
  1184. if (part_info->linear_hash_ind)
  1185. {
  1186. part_info->get_partition_id= get_partition_id_range_sub_linear_key;
  1187. part_info->get_subpartition_id= get_partition_id_linear_key_sub;
  1188. }
  1189. else
  1190. {
  1191. part_info->get_partition_id= get_partition_id_range_sub_key;
  1192. part_info->get_subpartition_id= get_partition_id_key_sub;
  1193. }
  1194. }
  1195. else
  1196. {
  1197. if (part_info->linear_hash_ind)
  1198. {
  1199. part_info->get_partition_id= get_partition_id_range_sub_linear_hash;
  1200. part_info->get_subpartition_id= get_partition_id_linear_hash_sub;
  1201. }
  1202. else
  1203. {
  1204. part_info->get_partition_id= get_partition_id_range_sub_hash;
  1205. part_info->get_subpartition_id= get_partition_id_hash_sub;
  1206. }
  1207. }
  1208. }
  1209. else /* LIST Partitioning */
  1210. {
  1211. part_info->get_part_partition_id= get_partition_id_list;
  1212. if (part_info->list_of_subpart_fields)
  1213. {
  1214. if (part_info->linear_hash_ind)
  1215. {
  1216. part_info->get_partition_id= get_partition_id_list_sub_linear_key;
  1217. part_info->get_subpartition_id= get_partition_id_linear_key_sub;
  1218. }
  1219. else
  1220. {
  1221. part_info->get_partition_id= get_partition_id_list_sub_key;
  1222. part_info->get_subpartition_id= get_partition_id_key_sub;
  1223. }
  1224. }
  1225. else
  1226. {
  1227. if (part_info->linear_hash_ind)
  1228. {
  1229. part_info->get_partition_id= get_partition_id_list_sub_linear_hash;
  1230. part_info->get_subpartition_id= get_partition_id_linear_hash_sub;
  1231. }
  1232. else
  1233. {
  1234. part_info->get_partition_id= get_partition_id_list_sub_hash;
  1235. part_info->get_subpartition_id= get_partition_id_hash_sub;
  1236. }
  1237. }
  1238. }
  1239. }
  1240. else /* No subpartitioning */
  1241. {
  1242. part_info->get_part_partition_id= NULL;
  1243. part_info->get_subpartition_id= NULL;
  1244. if (part_info->part_type == RANGE_PARTITION)
  1245. part_info->get_partition_id= get_partition_id_range;
  1246. else if (part_info->part_type == LIST_PARTITION)
  1247. part_info->get_partition_id= get_partition_id_list;
  1248. else /* HASH partitioning */
  1249. {
  1250. if (part_info->list_of_part_fields)
  1251. {
  1252. if (part_info->linear_hash_ind)
  1253. part_info->get_partition_id= get_partition_id_linear_key_nosub;
  1254. else
  1255. part_info->get_partition_id= get_partition_id_key_nosub;
  1256. }
  1257. else
  1258. {
  1259. if (part_info->linear_hash_ind)
  1260. part_info->get_partition_id= get_partition_id_linear_hash_nosub;
  1261. else
  1262. part_info->get_partition_id= get_partition_id_hash_nosub;
  1263. }
  1264. }
  1265. }
  1266. if (part_info->full_part_charset_field_array)
  1267. {
  1268. DBUG_ASSERT(part_info->get_partition_id);
  1269. part_info->get_partition_id_charset= part_info->get_partition_id;
  1270. if (part_info->part_charset_field_array &&
  1271. part_info->subpart_charset_field_array)
  1272. part_info->get_partition_id= get_part_id_charset_func_all;
  1273. else if (part_info->part_charset_field_array)
  1274. part_info->get_partition_id= get_part_id_charset_func_part;
  1275. else
  1276. part_info->get_partition_id= get_part_id_charset_func_subpart;
  1277. }
  1278. if (part_info->part_charset_field_array &&
  1279. part_info->is_sub_partitioned())
  1280. {
  1281. DBUG_ASSERT(part_info->get_part_partition_id);
  1282. part_info->get_part_partition_id_charset=
  1283. part_info->get_part_partition_id;
  1284. part_info->get_part_partition_id= get_part_part_id_charset_func;
  1285. }
  1286. if (part_info->subpart_charset_field_array)
  1287. {
  1288. DBUG_ASSERT(part_info->get_subpartition_id);
  1289. part_info->get_subpartition_id_charset=
  1290. part_info->get_subpartition_id;
  1291. part_info->get_subpartition_id= get_subpart_id_charset_func;
  1292. }
  1293. DBUG_VOID_RETURN;
  1294. }
  1295. /*
  1296. For linear hashing we need a mask which is on the form 2**n - 1 where
  1297. 2**n >= no_parts. Thus if no_parts is 6 then mask is 2**3 - 1 = 8 - 1 = 7.
  1298. SYNOPSIS
  1299. set_linear_hash_mask()
  1300. part_info Reference to partitioning data structure
  1301. no_parts Number of parts in linear hash partitioning
  1302. RETURN VALUE
  1303. NONE
  1304. */
  1305. void set_linear_hash_mask(partition_info *part_info, uint no_parts)
  1306. {
  1307. uint mask;
  1308. for (mask= 1; mask < no_parts; mask<<=1)
  1309. ;
  1310. part_info->linear_hash_mask= mask - 1;
  1311. }
  1312. /*
  1313. This function calculates the partition id provided the result of the hash
  1314. function using linear hashing parameters, mask and number of partitions.
  1315. SYNOPSIS
  1316. get_part_id_from_linear_hash()
  1317. hash_value Hash value calculated by HASH function or KEY function
  1318. mask Mask calculated previously by set_linear_hash_mask
  1319. no_parts Number of partitions in HASH partitioned part
  1320. RETURN VALUE
  1321. part_id The calculated partition identity (starting at 0)
  1322. DESCRIPTION
  1323. The partition is calculated according to the theory of linear hashing.
  1324. See e.g. Linear hashing: a new tool for file and table addressing,
  1325. Reprinted from VLDB-80 in Readings Database Systems, 2nd ed, M. Stonebraker
  1326. (ed.), Morgan Kaufmann 1994.
  1327. */
  1328. static uint32 get_part_id_from_linear_hash(longlong hash_value, uint mask,
  1329. uint no_parts)
  1330. {
  1331. uint32 part_id= (uint32)(hash_value & mask);
  1332. if (part_id >= no_parts)
  1333. {
  1334. uint new_mask= ((mask + 1) >> 1) - 1;
  1335. part_id= (uint32)(hash_value & new_mask);
  1336. }
  1337. return part_id;
  1338. }
  1339. /*
  1340. Check if a particular field is in need of character set
  1341. handling for partition functions.
  1342. SYNOPSIS
  1343. field_is_partition_charset()
  1344. field The field to check
  1345. RETURN VALUES
  1346. FALSE Not in need of character set handling
  1347. TRUE In need of character set handling
  1348. */
  1349. bool field_is_partition_charset(Field *field)
  1350. {
  1351. if (!(field->type() == MYSQL_TYPE_STRING) &&
  1352. !(field->type() == MYSQL_TYPE_VARCHAR))
  1353. return FALSE;
  1354. {
  1355. CHARSET_INFO *cs= ((Field_str*)field)->charset();
  1356. if (!(field->type() == MYSQL_TYPE_STRING) ||
  1357. !(cs->state & MY_CS_BINSORT))
  1358. return TRUE;
  1359. return FALSE;
  1360. }
  1361. }
  1362. /*
  1363. Check that partition function doesn't contain any forbidden
  1364. character sets and collations.
  1365. SYNOPSIS
  1366. check_part_func_fields()
  1367. ptr Array of Field pointers
  1368. ok_with_charsets Will we report allowed charset
  1369. fields as ok
  1370. RETURN VALUES
  1371. FALSE Success
  1372. TRUE Error
  1373. DESCRIPTION
  1374. We will check in this routine that the fields of the partition functions
  1375. do not contain unallowed parts. It can also be used to check if there
  1376. are fields that require special care by calling my_strnxfrm before
  1377. calling the functions to calculate partition id.
  1378. */
  1379. bool check_part_func_fields(Field **ptr, bool ok_with_charsets)
  1380. {
  1381. Field *field;
  1382. DBUG_ENTER("check_part_func_fields");
  1383. while ((field= *(ptr++)))
  1384. {
  1385. /*
  1386. For CHAR/VARCHAR fields we need to take special precautions.
  1387. Binary collation with CHAR is automatically supported. Other
  1388. types need some kind of standardisation function handling
  1389. */
  1390. if (field_is_partition_charset(field))
  1391. {
  1392. CHARSET_INFO *cs= ((Field_str*)field)->charset();
  1393. if (!ok_with_charsets ||
  1394. cs->mbmaxlen > 1 ||
  1395. cs->strxfrm_multiply > 1)
  1396. {
  1397. DBUG_RETURN(TRUE);
  1398. }
  1399. }
  1400. }
  1401. DBUG_RETURN(FALSE);
  1402. }
  1403. /*
  1404. fix partition functions
  1405. SYNOPSIS
  1406. fix_partition_func()
  1407. thd The thread object
  1408. table TABLE object for which partition fields are set-up
  1409. is_create_table_ind Indicator of whether openfrm was called as part of
  1410. CREATE or ALTER TABLE
  1411. RETURN VALUE
  1412. TRUE Error
  1413. FALSE Success
  1414. DESCRIPTION
  1415. The name parameter contains the full table name and is used to get the
  1416. database name of the table which is used to set-up a correct
  1417. TABLE_LIST object for use in fix_fields.
  1418. NOTES
  1419. This function is called as part of opening the table by opening the .frm
  1420. file. It is a part of CREATE TABLE to do this so it is quite permissible
  1421. that errors due to erroneus syntax isn't found until we come here.
  1422. If the user has used a non-existing field in the table is one such example
  1423. of an error that is not discovered until here.
  1424. */
  1425. bool fix_partition_func(THD *thd, TABLE *table,
  1426. bool is_create_table_ind)
  1427. {
  1428. bool result= TRUE;
  1429. partition_info *part_info= table->part_info;
  1430. enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
  1431. DBUG_ENTER("fix_partition_func");
  1432. if (part_info->fixed)
  1433. {
  1434. DBUG_RETURN(FALSE);
  1435. }
  1436. thd->mark_used_columns= MARK_COLUMNS_NONE;
  1437. DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
  1438. if (!is_create_table_ind ||
  1439. thd->lex->sql_command != SQLCOM_CREATE_TABLE)
  1440. {
  1441. if (partition_default_handling(table, part_info,
  1442. is_create_table_ind,
  1443. table->s->normalized_path.str))
  1444. {
  1445. DBUG_RETURN(TRUE);
  1446. }
  1447. }
  1448. if (part_info->is_sub_partitioned())
  1449. {
  1450. DBUG_ASSERT(part_info->subpart_type == HASH_PARTITION);
  1451. /*
  1452. Subpartition is defined. We need to verify that subpartitioning
  1453. function is correct.
  1454. */
  1455. if (part_info->linear_hash_ind)
  1456. set_linear_hash_mask(part_info, part_info->no_subparts);
  1457. if (part_info->list_of_subpart_fields)
  1458. {
  1459. List_iterator<char> it(part_info->subpart_field_list);
  1460. if (unlikely(handle_list_of_fields(it, table, part_info, TRUE)))
  1461. goto end;
  1462. }
  1463. else
  1464. {
  1465. if (unlikely(fix_fields_part_func(thd, part_info->subpart_expr,
  1466. table, TRUE, TRUE,
  1467. is_create_table_ind)))
  1468. goto end;
  1469. if (unlikely(part_info->subpart_expr->result_type() != INT_RESULT))
  1470. {
  1471. my_error(ER_PARTITION_FUNC_NOT_ALLOWED_ERROR, MYF(0),
  1472. "SUBPARTITION");
  1473. goto end;
  1474. }
  1475. }
  1476. }
  1477. DBUG_ASSERT(part_info->part_type != NOT_A_PARTITION);
  1478. /*
  1479. Partition is defined. We need to verify that partitioning
  1480. function is correct.
  1481. */
  1482. if (part_info->part_type == HASH_PARTITION)
  1483. {
  1484. if (part_info->linear_hash_ind)
  1485. set_linear_hash_mask(part_info, part_info->no_parts);
  1486. if (part_info->list_of_part_fields)
  1487. {
  1488. List_iterator<char> it(part_info->part_field_list);
  1489. if (unlikely(handle_list_of_fields(it, table, part_info, FALSE)))
  1490. goto end;
  1491. }
  1492. else
  1493. {
  1494. if (unlikely(fix_fields_part_func(thd, part_info->part_expr,
  1495. table, FALSE, TRUE,
  1496. is_create_table_ind)))
  1497. goto end;
  1498. if (unlikely(part_info->part_expr->result_type() != INT_RESULT))
  1499. {
  1500. my_error(ER_PARTITION_FUNC_NOT_ALLOWED_ERROR, MYF(0), part_str);
  1501. goto end;
  1502. }
  1503. part_info->part_result_type= INT_RESULT;
  1504. }
  1505. }
  1506. else
  1507. {
  1508. const char *error_str;
  1509. if (unlikely(fix_fields_part_func(thd, part_info->part_expr,
  1510. table, FALSE, TRUE,
  1511. is_create_table_ind)))
  1512. goto end;
  1513. if (part_info->part_type == RANGE_PARTITION)
  1514. {
  1515. error_str= partition_keywords[PKW_RANGE].str;
  1516. if (unlikely(part_info->check_range_constants()))
  1517. goto end;
  1518. }
  1519. else if (part_info->part_type == LIST_PARTITION)
  1520. {
  1521. error_str= partition_keywords[PKW_LIST].str;
  1522. if (unlikely(part_info->check_list_constants()))
  1523. goto end;
  1524. }
  1525. else
  1526. {
  1527. DBUG_ASSERT(0);
  1528. my_error(ER_INCONSISTENT_PARTITION_INFO_ERROR, MYF(0));
  1529. goto end;
  1530. }
  1531. if (unlikely(part_info->no_parts < 1))
  1532. {
  1533. my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), error_str);
  1534. goto end;
  1535. }
  1536. if (unlikely(part_info->part_expr->result_type() != INT_RESULT))
  1537. {
  1538. my_error(ER_PARTITION_FUNC_NOT_ALLOWED_ERROR, MYF(0), part_str);
  1539. goto end;
  1540. }
  1541. }
  1542. if (((part_info->part_type != HASH_PARTITION ||
  1543. part_info->list_of_part_fields == FALSE) &&
  1544. check_part_func_fields(part_info->part_field_array, TRUE)) ||
  1545. (part_info->list_of_subpart_fields == FALSE &&
  1546. part_info->is_sub_partitioned() &&
  1547. check_part_func_fields(part_info->subpart_field_array, TRUE)))
  1548. {
  1549. my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
  1550. goto end;
  1551. }
  1552. if (unlikely(create_full_part_field_array(thd, table, part_info)))
  1553. goto end;
  1554. if (unlikely(check_primary_key(table)))
  1555. goto end;
  1556. if (unlikely((!(table->s->db_type()->partition_flags &&
  1557. (table->s->db_type()->partition_flags() & HA_CAN_PARTITION_UNIQUE))) &&
  1558. check_unique_keys(table)))
  1559. goto end;
  1560. if (unlikely(set_up_partition_bitmap(thd, part_info)))
  1561. goto end;
  1562. if (unlikely(part_info->set_up_charset_field_preps()))
  1563. {
  1564. my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
  1565. goto end;
  1566. }
  1567. check_range_capable_PF(table);
  1568. set_up_partition_key_maps(table, part_info);
  1569. set_up_partition_func_pointers(part_info);
  1570. set_up_range_analysis_info(part_info);
  1571. result= FALSE;
  1572. end:
  1573. thd->mark_used_columns= save_mark_used_columns;
  1574. DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
  1575. DBUG_RETURN(result);
  1576. }
  1577. /*
  1578. The code below is support routines for the reverse parsing of the
  1579. partitioning syntax. This feature is very useful to generate syntax for
  1580. all default values to avoid all default checking when opening the frm
  1581. file. It is also used when altering the partitioning by use of various
  1582. ALTER TABLE commands. Finally it is used for SHOW CREATE TABLES.
  1583. */
  1584. static int add_write(File fptr, const char *buf, uint len)
  1585. {
  1586. uint len_written= my_write(fptr, (const uchar*)buf, len, MYF(0));
  1587. if (likely(len == len_written))
  1588. return 0;
  1589. else
  1590. return 1;
  1591. }
  1592. static int add_string_object(File fptr, String *string)
  1593. {
  1594. return add_write(fptr, string->ptr(), string->length());
  1595. }
  1596. static int add_string(File fptr, const char *string)
  1597. {
  1598. return add_write(fptr, string, strlen(string));
  1599. }
  1600. static int add_string_len(File fptr, const char *string, uint len)
  1601. {
  1602. return add_write(fptr, string, len);
  1603. }
  1604. static int add_space(File fptr)
  1605. {
  1606. return add_string(fptr, space_str);
  1607. }
  1608. static int add_comma(File fptr)
  1609. {
  1610. return add_string(fptr, comma_str);
  1611. }
  1612. static int add_equal(File fptr)
  1613. {
  1614. return add_string(fptr, equal_str);
  1615. }
  1616. static int add_end_parenthesis(File fptr)
  1617. {
  1618. return add_string(fptr, end_paren_str);
  1619. }
  1620. static int add_begin_parenthesis(File fptr)
  1621. {
  1622. return add_string(fptr, begin_paren_str);
  1623. }
  1624. static int add_part_key_word(File fptr, const char *key_string)
  1625. {
  1626. int err= add_string(fptr, key_string);
  1627. err+= add_space(fptr);
  1628. return err + add_begin_parenthesis(fptr);
  1629. }
  1630. static int add_hash(File fptr)
  1631. {
  1632. return add_part_key_word(fptr, partition_keywords[PKW_HASH].str);
  1633. }
  1634. static int add_partition(File fptr)
  1635. {
  1636. char buff[22];
  1637. strxmov(buff, part_str, space_str, NullS);
  1638. return add_string(fptr, buff);
  1639. }
  1640. static int add_subpartition(File fptr)
  1641. {
  1642. int err= add_string(fptr, sub_str);
  1643. return err + add_partition(fptr);
  1644. }
  1645. static int add_partition_by(File fptr)
  1646. {
  1647. char buff[22];
  1648. strxmov(buff, part_str, space_str, by_str, space_str, NullS);
  1649. return add_string(fptr, buff);
  1650. }
  1651. static int add_subpartition_by(File fptr)
  1652. {
  1653. int err= add_string(fptr, sub_str);
  1654. return err + add_partition_by(fptr);
  1655. }
  1656. static int add_key_partition(File fptr, List<char> field_list)
  1657. {
  1658. uint i, no_fields;
  1659. int err;
  1660. List_iterator<char> part_it(field_list);
  1661. err= add_part_key_word(fptr, partition_keywords[PKW_KEY].str);
  1662. no_fields= field_list.elements;
  1663. i= 0;
  1664. while (i < no_fields)
  1665. {
  1666. const char *field_str= part_it++;
  1667. String field_string("", 0, system_charset_info);
  1668. THD *thd= current_thd;
  1669. ulonglong save_options= thd->options;
  1670. thd->options= 0;
  1671. append_identifier(thd, &field_string, field_str,
  1672. strlen(field_str));
  1673. thd->options= save_options;
  1674. err+= add_string_object(fptr, &field_string);
  1675. if (i != (no_fields-1))
  1676. err+= add_comma(fptr);
  1677. i++;
  1678. }
  1679. return err;
  1680. }
  1681. static int add_name_string(File fptr, const char *name)
  1682. {
  1683. int err;
  1684. String name_string("", 0, system_charset_info);
  1685. THD *thd= current_thd;
  1686. ulonglong save_options= thd->options;
  1687. thd->options= 0;
  1688. append_identifier(thd, &name_string, name,
  1689. strlen(name));
  1690. thd->options= save_options;
  1691. err= add_string_object(fptr, &name_string);
  1692. return err;
  1693. }
  1694. static int add_int(File fptr, longlong number)
  1695. {
  1696. char buff[32];
  1697. llstr(number, buff);
  1698. return add_string(fptr, buff);
  1699. }
  1700. static int add_uint(File fptr, ulonglong number)
  1701. {
  1702. char buff[32];
  1703. longlong2str(number, buff, 10);
  1704. return add_string(fptr, buff);
  1705. }
  1706. /*
  1707. Must escape strings in partitioned tables frm-files,
  1708. parsing it later with mysql_unpack_partition will fail otherwise.
  1709. */
  1710. static int add_quoted_string(File fptr, const char *quotestr)
  1711. {
  1712. String orgstr(quotestr, system_charset_info);
  1713. String escapedstr;
  1714. int err= add_string(fptr, "'");
  1715. err+= append_escaped(&escapedstr, &orgstr);
  1716. err+= add_string(fptr, escapedstr.c_ptr_safe());
  1717. return err + add_string(fptr, "'");
  1718. }
  1719. static int add_keyword_string(File fptr, const char *keyword,
  1720. bool should_use_quotes,
  1721. const char *keystr)
  1722. {
  1723. int err= add_string(fptr, keyword);
  1724. err+= add_space(fptr);
  1725. err+= add_equal(fptr);
  1726. err+= add_space(fptr);
  1727. if (should_use_quotes)
  1728. err+= add_quoted_string(fptr, keystr);
  1729. else
  1730. err+= add_string(fptr, keystr);
  1731. return err + add_space(fptr);
  1732. }
  1733. static int add_keyword_int(File fptr, const char *keyword, longlong num)
  1734. {
  1735. int err= add_string(fptr, keyword);
  1736. err+= add_space(fptr);
  1737. err+= add_equal(fptr);
  1738. err+= add_space(fptr);
  1739. err+= add_int(fptr, num);
  1740. return err + add_space(fptr);
  1741. }
  1742. static int add_engine(File fptr, handlerton *engine_type)
  1743. {
  1744. const char *engine_str= ha_resolve_storage_engine_name(engine_type);
  1745. DBUG_PRINT("info", ("ENGINE: %s", engine_str));
  1746. int err= add_string(fptr, "ENGINE = ");
  1747. return err + add_string(fptr, engine_str);
  1748. }
  1749. static int add_partition_options(File fptr, partition_element *p_elem)
  1750. {
  1751. int err= 0;
  1752. err+= add_space(fptr);
  1753. if (p_elem->tablespace_name)
  1754. err+= add_keyword_string(fptr,"TABLESPACE", FALSE,
  1755. p_elem->tablespace_name);
  1756. if (p_elem->nodegroup_id != UNDEF_NODEGROUP)
  1757. err+= add_keyword_int(fptr,"NODEGROUP",(longlong)p_elem->nodegroup_id);
  1758. if (p_elem->part_max_rows)
  1759. err+= add_keyword_int(fptr,"MAX_ROWS",(longlong)p_elem->part_max_rows);
  1760. if (p_elem->part_min_rows)
  1761. err+= add_keyword_int(fptr,"MIN_ROWS",(longlong)p_elem->part_min_rows);
  1762. if (!(current_thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
  1763. {
  1764. if (p_elem->data_file_name)
  1765. err+= add_keyword_string(fptr, "DATA DIRECTORY", TRUE,
  1766. p_elem->data_file_name);
  1767. if (p_elem->index_file_name)
  1768. err+= add_keyword_string(fptr, "INDEX DIRECTORY", TRUE,
  1769. p_elem->index_file_name);
  1770. }
  1771. if (p_elem->part_comment)
  1772. err+= add_keyword_string(fptr, "COMMENT", TRUE, p_elem->part_comment);
  1773. return err + add_engine(fptr,p_elem->engine_type);
  1774. }
  1775. static int add_partition_values(File fptr, partition_info *part_info, partition_element *p_elem)
  1776. {
  1777. int err= 0;
  1778. if (part_info->part_type == RANGE_PARTITION)
  1779. {
  1780. err+= add_string(fptr, " VALUES LESS THAN ");
  1781. if (!p_elem->max_value)
  1782. {
  1783. err+= add_begin_parenthesis(fptr);
  1784. if (p_elem->signed_flag)
  1785. err+= add_int(fptr, p_elem->range_value);
  1786. else
  1787. err+= add_uint(fptr, p_elem->range_value);
  1788. err+= add_end_parenthesis(fptr);
  1789. }
  1790. else
  1791. err+= add_string(fptr, partition_keywords[PKW_MAXVALUE].str);
  1792. }
  1793. else if (part_info->part_type == LIST_PARTITION)
  1794. {
  1795. uint i;
  1796. List_iterator<part_elem_value> list_val_it(p_elem->list_val_list);
  1797. err+= add_string(fptr, " VALUES IN ");
  1798. uint no_items= p_elem->list_val_list.elements;
  1799. err+= add_begin_parenthesis(fptr);
  1800. if (p_elem->has_null_value)
  1801. {
  1802. err+= add_string(fptr, "NULL");
  1803. if (no_items == 0)
  1804. {
  1805. err+= add_end_parenthesis(fptr);
  1806. goto end;
  1807. }
  1808. err+= add_comma(fptr);
  1809. }
  1810. i= 0;
  1811. do
  1812. {
  1813. part_elem_value *list_value= list_val_it++;
  1814. if (!list_value->unsigned_flag)
  1815. err+= add_int(fptr, list_value->value);
  1816. else
  1817. err+= add_uint(fptr, list_value->value);
  1818. if (i != (no_items-1))
  1819. err+= add_comma(fptr);
  1820. } while (++i < no_items);
  1821. err+= add_end_parenthesis(fptr);
  1822. }
  1823. end:
  1824. return err;
  1825. }
  1826. /*
  1827. Generate the partition syntax from the partition data structure.
  1828. Useful for support of generating defaults, SHOW CREATE TABLES
  1829. and easy partition management.
  1830. SYNOPSIS
  1831. generate_partition_syntax()
  1832. part_info The partitioning data structure
  1833. buf_length A pointer to the returned buffer length
  1834. use_sql_alloc Allocate buffer from sql_alloc if true
  1835. otherwise use my_malloc
  1836. show_partition_options Should we display partition options
  1837. RETURN VALUES
  1838. NULL error
  1839. buf, buf_length Buffer and its length
  1840. DESCRIPTION
  1841. Here we will generate the full syntax for the given command where all
  1842. defaults have been expanded. By so doing the it is also possible to
  1843. make lots of checks of correctness while at it.
  1844. This could will also be reused for SHOW CREATE TABLES and also for all
  1845. type ALTER TABLE commands focusing on changing the PARTITION structure
  1846. in any fashion.
  1847. The implementation writes the syntax to a temporary file (essentially
  1848. an abstraction of a dynamic array) and if all writes goes well it
  1849. allocates a buffer and writes the syntax into this one and returns it.
  1850. As a security precaution the file is deleted before writing into it. This
  1851. means that no other processes on the machine can open and read the file
  1852. while this processing is ongoing.
  1853. The code is optimised for minimal code size since it is not used in any
  1854. common queries.
  1855. */
  1856. char *generate_partition_syntax(partition_info *part_info,
  1857. uint *buf_length,
  1858. bool use_sql_alloc,
  1859. bool show_partition_options)
  1860. {
  1861. uint i,j, tot_no_parts, no_subparts;
  1862. partition_element *part_elem;
  1863. ulonglong buffer_length;
  1864. char path[FN_REFLEN];
  1865. int err= 0;
  1866. List_iterator<partition_element> part_it(part_info->partitions);
  1867. File fptr;
  1868. char *buf= NULL; //Return buffer
  1869. DBUG_ENTER("generate_partition_syntax");
  1870. if (unlikely(((fptr= create_temp_file(path,mysql_tmpdir,"psy",
  1871. O_RDWR | O_BINARY | O_TRUNC |
  1872. O_TEMPORARY, MYF(MY_WME)))) < 0))
  1873. DBUG_RETURN(NULL);
  1874. #ifndef __WIN__
  1875. unlink(path);
  1876. #endif
  1877. err+= add_space(fptr);
  1878. err+= add_partition_by(fptr);
  1879. switch (part_info->part_type)
  1880. {
  1881. case RANGE_PARTITION:
  1882. err+= add_part_key_word(fptr, partition_keywords[PKW_RANGE].str);
  1883. break;
  1884. case LIST_PARTITION:
  1885. err+= add_part_key_word(fptr, partition_keywords[PKW_LIST].str);
  1886. break;
  1887. case HASH_PARTITION:
  1888. if (part_info->linear_hash_ind)
  1889. err+= add_string(fptr, partition_keywords[PKW_LINEAR].str);
  1890. if (part_info->list_of_part_fields)
  1891. err+= add_key_partition(fptr, part_info->part_field_list);
  1892. else
  1893. err+= add_hash(fptr);
  1894. break;
  1895. default:
  1896. DBUG_ASSERT(0);
  1897. /* We really shouldn't get here, no use in continuing from here */
  1898. my_error(ER_OUT_OF_RESOURCES, MYF(0));
  1899. current_thd->fatal_error();
  1900. DBUG_RETURN(NULL);
  1901. }
  1902. if (part_info->part_expr)
  1903. err+= add_string_len(fptr, part_info->part_func_string,
  1904. part_info->part_func_len);
  1905. err+= add_end_parenthesis(fptr);
  1906. if ((!part_info->use_default_no_partitions) &&
  1907. part_info->use_default_partitions)
  1908. {
  1909. err+= add_string(fptr, "\n");
  1910. err+= add_string(fptr, "PARTITIONS ");
  1911. err+= add_int(fptr, part_info->no_parts);
  1912. }
  1913. if (part_info->is_sub_partitioned())
  1914. {
  1915. err+= add_string(fptr, "\n");
  1916. err+= add_subpartition_by(fptr);
  1917. /* Must be hash partitioning for subpartitioning */
  1918. if (part_info->linear_hash_ind)
  1919. err+= add_string(fptr, partition_keywords[PKW_LINEAR].str);
  1920. if (part_info->list_of_subpart_fields)
  1921. err+= add_key_partition(fptr, part_info->subpart_field_list);
  1922. else
  1923. err+= add_hash(fptr);
  1924. if (part_info->subpart_expr)
  1925. err+= add_string_len(fptr, part_info->subpart_func_string,
  1926. part_info->subpart_func_len);
  1927. err+= add_end_parenthesis(fptr);
  1928. if ((!part_info->use_default_no_subpartitions) &&
  1929. part_info->use_default_subpartitions)
  1930. {
  1931. err+= add_string(fptr, "\n");
  1932. err+= add_string(fptr, "SUBPARTITIONS ");
  1933. err+= add_int(fptr, part_info->no_subparts);
  1934. }
  1935. }
  1936. tot_no_parts= part_info->partitions.elements;
  1937. no_subparts= part_info->no_subparts;
  1938. if (!part_info->use_default_partitions)
  1939. {
  1940. bool first= TRUE;
  1941. err+= add_string(fptr, "\n");
  1942. err+= add_begin_parenthesis(fptr);
  1943. i= 0;
  1944. do
  1945. {
  1946. part_elem= part_it++;
  1947. if (part_elem->part_state != PART_TO_BE_DROPPED &&
  1948. part_elem->part_state != PART_REORGED_DROPPED)
  1949. {
  1950. if (!first)
  1951. {
  1952. err+= add_comma(fptr);
  1953. err+= add_string(fptr, "\n");
  1954. err+= add_space(fptr);
  1955. }
  1956. first= FALSE;
  1957. err+= add_partition(fptr);
  1958. err+= add_name_string(fptr, part_elem->partition_name);
  1959. err+= add_partition_values(fptr, part_info, part_elem);
  1960. if (!part_info->is_sub_partitioned() ||
  1961. part_info->use_default_subpartitions)
  1962. {
  1963. if (show_partition_options)
  1964. err+= add_partition_options(fptr, part_elem);
  1965. }
  1966. else
  1967. {
  1968. err+= add_string(fptr, "\n");
  1969. err+= add_space(fptr);
  1970. err+= add_begin_parenthesis(fptr);
  1971. List_iterator<partition_element> sub_it(part_elem->subpartitions);
  1972. j= 0;
  1973. do
  1974. {
  1975. part_elem= sub_it++;
  1976. err+= add_subpartition(fptr);
  1977. err+= add_name_string(fptr, part_elem->partition_name);
  1978. if (show_partition_options)
  1979. err+= add_partition_options(fptr, part_elem);
  1980. if (j != (no_subparts-1))
  1981. {
  1982. err+= add_comma(fptr);
  1983. err+= add_string(fptr, "\n");
  1984. err+= add_space(fptr);
  1985. err+= add_space(fptr);
  1986. }
  1987. else
  1988. err+= add_end_parenthesis(fptr);
  1989. } while (++j < no_subparts);
  1990. }
  1991. }
  1992. if (i == (tot_no_parts-1))
  1993. err+= add_end_parenthesis(fptr);
  1994. } while (++i < tot_no_parts);
  1995. }
  1996. if (err)
  1997. goto close_file;
  1998. buffer_length= my_seek(fptr, 0L,MY_SEEK_END,MYF(0));
  1999. if (unlikely(buffer_length == MY_FILEPOS_ERROR))
  2000. goto close_file;
  2001. if (unlikely(my_seek(fptr, 0L, MY_SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR))
  2002. goto close_file;
  2003. *buf_length= (uint)buffer_length;
  2004. if (use_sql_alloc)
  2005. buf= (char*) sql_alloc(*buf_length+1);
  2006. else
  2007. buf= (char*) my_malloc(*buf_length+1, MYF(MY_WME));
  2008. if (!buf)
  2009. goto close_file;
  2010. if (unlikely(my_read(fptr, (uchar*)buf, *buf_length, MYF(MY_FNABP))))
  2011. {
  2012. if (!use_sql_alloc)
  2013. my_free(buf, MYF(0));
  2014. else
  2015. buf= NULL;
  2016. }
  2017. else
  2018. buf[*buf_length]= 0;
  2019. close_file:
  2020. my_close(fptr, MYF(0));
  2021. DBUG_RETURN(buf);
  2022. }
  2023. /*
  2024. Check if partition key fields are modified and if it can be handled by the
  2025. underlying storage engine.
  2026. SYNOPSIS
  2027. partition_key_modified
  2028. table TABLE object for which partition fields are set-up
  2029. fields Bitmap representing fields to be modified
  2030. RETURN VALUES
  2031. TRUE Need special handling of UPDATE
  2032. FALSE Normal UPDATE handling is ok
  2033. */
  2034. bool partition_key_modified(TABLE *table, const MY_BITMAP *fields)
  2035. {
  2036. Field **fld;
  2037. partition_info *part_info= table->part_info;
  2038. DBUG_ENTER("partition_key_modified");
  2039. if (!part_info)
  2040. DBUG_RETURN(FALSE);
  2041. if (table->s->db_type()->partition_flags &&
  2042. (table->s->db_type()->partition_flags() & HA_CAN_UPDATE_PARTITION_KEY))
  2043. DBUG_RETURN(FALSE);
  2044. for (fld= part_info->full_part_field_array; *fld; fld++)
  2045. if (bitmap_is_set(fields, (*fld)->field_index))
  2046. DBUG_RETURN(TRUE);
  2047. DBUG_RETURN(FALSE);
  2048. }
  2049. /*
  2050. A function to handle correct handling of NULL values in partition
  2051. functions.
  2052. SYNOPSIS
  2053. part_val_int()
  2054. item_expr The item expression to evaluate
  2055. out:result The value of the partition function,
  2056. LONGLONG_MIN if any null value in function
  2057. RETURN VALUES
  2058. TRUE Error in val_int()
  2059. FALSE ok
  2060. */
  2061. static inline int part_val_int(Item *item_expr, longlong *result)
  2062. {
  2063. *result= item_expr->val_int();
  2064. if (item_expr->null_value)
  2065. {
  2066. if (current_thd->is_error())
  2067. return TRUE;
  2068. else
  2069. *result= LONGLONG_MIN;
  2070. }
  2071. return FALSE;
  2072. }
  2073. /*
  2074. The next set of functions are used to calculate the partition identity.
  2075. A handler sets up a variable that corresponds to one of these functions
  2076. to be able to quickly call it whenever the partition id needs to calculated
  2077. based on the record in table->record[0] (or set up to fake that).
  2078. There are 4 functions for hash partitioning and 2 for RANGE/LIST partitions.
  2079. In addition there are 4 variants for RANGE subpartitioning and 4 variants
  2080. for LIST subpartitioning thus in total there are 14 variants of this
  2081. function.
  2082. We have a set of support functions for these 14 variants. There are 4
  2083. variants of hash functions and there is a function for each. The KEY
  2084. partitioning uses the function calculate_key_value to calculate the hash
  2085. value based on an array of fields. The linear hash variants uses the
  2086. method get_part_id_from_linear_hash to get the partition id using the
  2087. hash value and some parameters calculated from the number of partitions.
  2088. */
  2089. /*
  2090. Calculate hash value for KEY partitioning using an array of fields.
  2091. SYNOPSIS
  2092. calculate_key_value()
  2093. field_array An array of the fields in KEY partitioning
  2094. RETURN VALUE
  2095. hash_value calculated
  2096. DESCRIPTION
  2097. Uses the hash function on the character set of the field. Integer and
  2098. floating point fields use the binary character set by default.
  2099. */
  2100. static uint32 calculate_key_value(Field **field_array)
  2101. {
  2102. ulong nr1= 1;
  2103. ulong nr2= 4;
  2104. do
  2105. {
  2106. Field *field= *field_array;
  2107. field->hash(&nr1, &nr2);
  2108. } while (*(++field_array));
  2109. return (uint32) nr1;
  2110. }
  2111. /*
  2112. A simple support function to calculate part_id given local part and
  2113. sub part.
  2114. SYNOPSIS
  2115. get_part_id_for_sub()
  2116. loc_part_id Local partition id
  2117. sub_part_id Subpartition id
  2118. no_subparts Number of subparts
  2119. */
  2120. inline
  2121. static uint32 get_part_id_for_sub(uint32 loc_part_id, uint32 sub_part_id,
  2122. uint no_subparts)
  2123. {
  2124. return (uint32)((loc_part_id * no_subparts) + sub_part_id);
  2125. }
  2126. /*
  2127. Calculate part_id for (SUB)PARTITION BY HASH
  2128. SYNOPSIS
  2129. get_part_id_hash()
  2130. no_parts Number of hash partitions
  2131. part_expr Item tree of hash function
  2132. out:part_id The returned partition id
  2133. out:func_value Value of hash function
  2134. RETURN VALUE
  2135. != 0 Error code
  2136. FALSE Success
  2137. */
  2138. static int get_part_id_hash(uint no_parts,
  2139. Item *part_expr,
  2140. uint32 *part_id,
  2141. longlong *func_value)
  2142. {
  2143. longlong int_hash_id;
  2144. DBUG_ENTER("get_part_id_hash");
  2145. if (part_val_int(part_expr, func_value))
  2146. DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
  2147. int_hash_id= *func_value % no_parts;
  2148. *part_id= int_hash_id < 0 ? (uint32) -int_hash_id : (uint32) int_hash_id;
  2149. DBUG_RETURN(FALSE);
  2150. }
  2151. /*
  2152. Calculate part_id for (SUB)PARTITION BY LINEAR HASH
  2153. SYNOPSIS
  2154. get_part_id_linear_hash()
  2155. part_info A reference to the partition_info struct where all the
  2156. desired information is given
  2157. no_parts Number of hash partitions
  2158. part_expr Item tree of hash function
  2159. out:part_id The returned partition id
  2160. out:func_value Value of hash function
  2161. RETURN VALUE
  2162. != 0 Error code
  2163. 0 OK
  2164. */
  2165. static int get_part_id_linear_hash(partition_info *part_info,
  2166. uint no_parts,
  2167. Item *part_expr,
  2168. uint32 *part_id,
  2169. longlong *func_value)
  2170. {
  2171. DBUG_ENTER("get_part_id_linear_hash");
  2172. if (part_val_int(part_expr, func_value))
  2173. DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
  2174. *part_id= get_part_id_from_linear_hash(*func_value,
  2175. part_info->linear_hash_mask,
  2176. no_parts);
  2177. DBUG_RETURN(FALSE);
  2178. }
  2179. /*
  2180. Calculate part_id for (SUB)PARTITION BY KEY
  2181. SYNOPSIS
  2182. get_part_id_key()
  2183. field_array Array of fields for PARTTION KEY
  2184. no_parts Number of KEY partitions
  2185. RETURN VALUE
  2186. Calculated partition id
  2187. */
  2188. inline
  2189. static uint32 get_part_id_key(Field **field_array,
  2190. uint no_parts,
  2191. longlong *func_value)
  2192. {
  2193. DBUG_ENTER("get_part_id_key");
  2194. *func_value= calculate_key_value(field_array);
  2195. DBUG_RETURN((uint32) (*func_value % no_parts));
  2196. }
  2197. /*
  2198. Calculate part_id for (SUB)PARTITION BY LINEAR KEY
  2199. SYNOPSIS
  2200. get_part_id_linear_key()
  2201. part_info A reference to the partition_info struct where all the
  2202. desired information is given
  2203. field_array Array of fields for PARTTION KEY
  2204. no_parts Number of KEY partitions
  2205. RETURN VALUE
  2206. Calculated partition id
  2207. */
  2208. inline
  2209. static uint32 get_part_id_linear_key(partition_info *part_info,
  2210. Field **field_array,
  2211. uint no_parts,
  2212. longlong *func_value)
  2213. {
  2214. DBUG_ENTER("get_partition_id_linear_key");
  2215. *func_value= calculate_key_value(field_array);
  2216. DBUG_RETURN(get_part_id_from_linear_hash(*func_value,
  2217. part_info->linear_hash_mask,
  2218. no_parts));
  2219. }
  2220. /*
  2221. Copy to field buffers and set up field pointers
  2222. SYNOPSIS
  2223. copy_to_part_field_buffers()
  2224. ptr Array of fields to copy
  2225. field_bufs Array of field buffers to copy to
  2226. restore_ptr Array of pointers to restore to
  2227. RETURN VALUES
  2228. NONE
  2229. DESCRIPTION
  2230. This routine is used to take the data from field pointer, convert
  2231. it to a standard format and store this format in a field buffer
  2232. allocated for this purpose. Next the field pointers are moved to
  2233. point to the field buffers. There is a separate to restore the
  2234. field pointers after this call.
  2235. */
  2236. static void copy_to_part_field_buffers(Field **ptr,
  2237. uchar **field_bufs,
  2238. uchar **restore_ptr)
  2239. {
  2240. Field *field;
  2241. while ((field= *(ptr++)))
  2242. {
  2243. *restore_ptr= field->ptr;
  2244. restore_ptr++;
  2245. if (!field->maybe_null() || !field->is_null())
  2246. {
  2247. CHARSET_INFO *cs= ((Field_str*)field)->charset();
  2248. uint len= field->pack_length();
  2249. uchar *field_buf= *field_bufs;
  2250. /*
  2251. We only use the field buffer for VARCHAR and CHAR strings
  2252. which isn't of a binary collation. We also only use the
  2253. field buffer for fields which are not currently NULL.
  2254. The field buffer will store a normalised string. We use
  2255. the strnxfrm method to normalise the string.
  2256. */
  2257. if (field->type() == MYSQL_TYPE_VARCHAR)
  2258. {
  2259. uint len_bytes= ((Field_varstring*)field)->length_bytes;
  2260. my_strnxfrm(cs, field_buf + len_bytes, (len - len_bytes),
  2261. field->ptr + len_bytes, field->field_length);
  2262. if (len_bytes == 1)
  2263. *field_buf= (uchar) field->field_length;
  2264. else
  2265. int2store(field_buf, field->field_length);
  2266. }
  2267. else
  2268. {
  2269. my_strnxfrm(cs, field_buf, len,
  2270. field->ptr, field->field_length);
  2271. }
  2272. field->ptr= field_buf;
  2273. }
  2274. field_bufs++;
  2275. }
  2276. return;
  2277. }
  2278. /*
  2279. Restore field pointers
  2280. SYNOPSIS
  2281. restore_part_field_pointers()
  2282. ptr Array of fields to restore
  2283. restore_ptr Array of field pointers to restore to
  2284. RETURN VALUES
  2285. */
  2286. static void restore_part_field_pointers(Field **ptr, uchar **restore_ptr)
  2287. {
  2288. Field *field;
  2289. while ((field= *(ptr++)))
  2290. {
  2291. field->ptr= *restore_ptr;
  2292. restore_ptr++;
  2293. }
  2294. return;
  2295. }
  2296. /*
  2297. This function is used to calculate the main partition to use in the case of
  2298. subpartitioning and we don't know enough to get the partition identity in
  2299. total.
  2300. SYNOPSIS
  2301. get_part_partition_id()
  2302. part_info A reference to the partition_info struct where all the
  2303. desired information is given
  2304. out:part_id The partition id is returned through this pointer
  2305. out:func_value The value calculated by partition function
  2306. RETURN VALUE
  2307. HA_ERR_NO_PARTITION_FOUND The fields of the partition function didn't
  2308. fit into any partition and thus the values of
  2309. the PF-fields are not allowed.
  2310. 0 OK
  2311. DESCRIPTION
  2312. It is actually 6 different variants of this function which are called
  2313. through a function pointer.
  2314. get_partition_id_list
  2315. get_partition_id_range
  2316. get_partition_id_hash_nosub
  2317. get_partition_id_key_nosub
  2318. get_partition_id_linear_hash_nosub
  2319. get_partition_id_linear_key_nosub
  2320. */
  2321. static int get_part_id_charset_func_subpart(partition_info *part_info,
  2322. uint32 *part_id,
  2323. longlong *func_value)
  2324. {
  2325. int res;
  2326. copy_to_part_field_buffers(part_info->subpart_charset_field_array,
  2327. part_info->subpart_field_buffers,
  2328. part_info->restore_subpart_field_ptrs);
  2329. res= part_info->get_partition_id_charset(part_info, part_id, func_value);
  2330. restore_part_field_pointers(part_info->subpart_charset_field_array,
  2331. part_info->restore_subpart_field_ptrs);
  2332. return res;
  2333. }
  2334. static int get_part_id_charset_func_part(partition_info *part_info,
  2335. uint32 *part_id,
  2336. longlong *func_value)
  2337. {
  2338. int res;
  2339. copy_to_part_field_buffers(part_info->part_charset_field_array,
  2340. part_info->part_field_buffers,
  2341. part_info->restore_part_field_ptrs);
  2342. res= part_info->get_partition_id_charset(part_info, part_id, func_value);
  2343. restore_part_field_pointers(part_info->part_charset_field_array,
  2344. part_info->restore_part_field_ptrs);
  2345. return res;
  2346. }
  2347. static int get_part_id_charset_func_all(partition_info *part_info,
  2348. uint32 *part_id,
  2349. longlong *func_value)
  2350. {
  2351. int res;
  2352. copy_to_part_field_buffers(part_info->full_part_field_array,
  2353. part_info->full_part_field_buffers,
  2354. part_info->restore_full_part_field_ptrs);
  2355. res= part_info->get_partition_id_charset(part_info, part_id, func_value);
  2356. restore_part_field_pointers(part_info->full_part_field_array,
  2357. part_info->restore_full_part_field_ptrs);
  2358. return res;
  2359. }
  2360. static int get_part_part_id_charset_func(partition_info *part_info,
  2361. uint32 *part_id,
  2362. longlong *func_value)
  2363. {
  2364. int res;
  2365. copy_to_part_field_buffers(part_info->part_charset_field_array,
  2366. part_info->part_field_buffers,
  2367. part_info->restore_part_field_ptrs);
  2368. res= part_info->get_part_partition_id_charset(part_info,
  2369. part_id, func_value);
  2370. restore_part_field_pointers(part_info->part_charset_field_array,
  2371. part_info->restore_part_field_ptrs);
  2372. return res;
  2373. }
  2374. static int get_subpart_id_charset_func(partition_info *part_info,
  2375. uint32 *part_id)
  2376. {
  2377. int res;
  2378. copy_to_part_field_buffers(part_info->subpart_charset_field_array,
  2379. part_info->subpart_field_buffers,
  2380. part_info->restore_subpart_field_ptrs);
  2381. res= part_info->get_subpartition_id_charset(part_info, part_id);
  2382. restore_part_field_pointers(part_info->subpart_charset_field_array,
  2383. part_info->restore_subpart_field_ptrs);
  2384. return res;
  2385. }
  2386. int get_partition_id_list(partition_info *part_info,
  2387. uint32 *part_id,
  2388. longlong *func_value)
  2389. {
  2390. LIST_PART_ENTRY *list_array= part_info->list_array;
  2391. int list_index;
  2392. int min_list_index= 0;
  2393. int max_list_index= part_info->no_list_values - 1;
  2394. longlong part_func_value;
  2395. int error= part_val_int(part_info->part_expr, &part_func_value);
  2396. longlong list_value;
  2397. bool unsigned_flag= part_info->part_expr->unsigned_flag;
  2398. DBUG_ENTER("get_partition_id_list");
  2399. if (error)
  2400. goto notfound;
  2401. if (part_info->part_expr->null_value)
  2402. {
  2403. if (part_info->has_null_value)
  2404. {
  2405. *part_id= part_info->has_null_part_id;
  2406. DBUG_RETURN(0);
  2407. }
  2408. goto notfound;
  2409. }
  2410. *func_value= part_func_value;
  2411. if (unsigned_flag)
  2412. part_func_value-= 0x8000000000000000ULL;
  2413. while (max_list_index >= min_list_index)
  2414. {
  2415. list_index= (max_list_index + min_list_index) >> 1;
  2416. list_value= list_array[list_index].list_value;
  2417. if (list_value < part_func_value)
  2418. min_list_index= list_index + 1;
  2419. else if (list_value > part_func_value)
  2420. {
  2421. if (!list_index)
  2422. goto notfound;
  2423. max_list_index= list_index - 1;
  2424. }
  2425. else
  2426. {
  2427. *part_id= (uint32)list_array[list_index].partition_id;
  2428. DBUG_RETURN(0);
  2429. }
  2430. }
  2431. notfound:
  2432. *part_id= 0;
  2433. DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
  2434. }
  2435. /*
  2436. Find the sub-array part_info->list_array that corresponds to given interval
  2437. SYNOPSIS
  2438. get_list_array_idx_for_endpoint()
  2439. part_info Partitioning info (partitioning type must be LIST)
  2440. left_endpoint TRUE - the interval is [a; +inf) or (a; +inf)
  2441. FALSE - the interval is (-inf; a] or (-inf; a)
  2442. include_endpoint TRUE iff the interval includes the endpoint
  2443. DESCRIPTION
  2444. This function finds the sub-array of part_info->list_array where values of
  2445. list_array[idx].list_value are contained within the specifed interval.
  2446. list_array is ordered by list_value, so
  2447. 1. For [a; +inf) or (a; +inf)-type intervals (left_endpoint==TRUE), the
  2448. sought sub-array starts at some index idx and continues till array end.
  2449. The function returns first number idx, such that
  2450. list_array[idx].list_value is contained within the passed interval.
  2451. 2. For (-inf; a] or (-inf; a)-type intervals (left_endpoint==FALSE), the
  2452. sought sub-array starts at array start and continues till some last
  2453. index idx.
  2454. The function returns first number idx, such that
  2455. list_array[idx].list_value is NOT contained within the passed interval.
  2456. If all array elements are contained, part_info->no_list_values is
  2457. returned.
  2458. NOTE
  2459. The caller will call this function and then will run along the sub-array of
  2460. list_array to collect partition ids. If the number of list values is
  2461. significantly higher then number of partitions, this could be slow and
  2462. we could invent some other approach. The "run over list array" part is
  2463. already wrapped in a get_next()-like function.
  2464. RETURN
  2465. The edge of corresponding sub-array of part_info->list_array
  2466. */
  2467. uint32 get_list_array_idx_for_endpoint_charset(partition_info *part_info,
  2468. bool left_endpoint,
  2469. bool include_endpoint)
  2470. {
  2471. uint32 res;
  2472. copy_to_part_field_buffers(part_info->part_field_array,
  2473. part_info->part_field_buffers,
  2474. part_info->restore_part_field_ptrs);
  2475. res= get_list_array_idx_for_endpoint(part_info, left_endpoint,
  2476. include_endpoint);
  2477. restore_part_field_pointers(part_info->part_field_array,
  2478. part_info->restore_part_field_ptrs);
  2479. return res;
  2480. }
  2481. uint32 get_list_array_idx_for_endpoint(partition_info *part_info,
  2482. bool left_endpoint,
  2483. bool include_endpoint)
  2484. {
  2485. LIST_PART_ENTRY *list_array= part_info->list_array;
  2486. uint list_index;
  2487. uint min_list_index= 0, max_list_index= part_info->no_list_values - 1;
  2488. longlong list_value;
  2489. /* Get the partitioning function value for the endpoint */
  2490. longlong part_func_value=
  2491. part_info->part_expr->val_int_endpoint(left_endpoint, &include_endpoint);
  2492. bool unsigned_flag= part_info->part_expr->unsigned_flag;
  2493. DBUG_ENTER("get_list_array_idx_for_endpoint");
  2494. if (part_info->part_expr->null_value)
  2495. {
  2496. /*
  2497. Special handling for MONOTONIC functions that can return NULL for
  2498. values that are comparable. I.e.
  2499. '2000-00-00' can be compared to '2000-01-01' but TO_DAYS('2000-00-00')
  2500. returns NULL which cannot be compared used <, >, <=, >= etc.
  2501. Otherwise, just return the the first index (lowest value).
  2502. */
  2503. enum_monotonicity_info monotonic;
  2504. monotonic= part_info->part_expr->get_monotonicity_info();
  2505. if (monotonic != MONOTONIC_INCREASING_NOT_NULL &&
  2506. monotonic != MONOTONIC_STRICT_INCREASING_NOT_NULL)
  2507. {
  2508. /* F(col) can not return NULL, return index with lowest value */
  2509. DBUG_RETURN(0);
  2510. }
  2511. }
  2512. if (unsigned_flag)
  2513. part_func_value-= 0x8000000000000000ULL;
  2514. DBUG_ASSERT(part_info->no_list_values);
  2515. do
  2516. {
  2517. list_index= (max_list_index + min_list_index) >> 1;
  2518. list_value= list_array[list_index].list_value;
  2519. if (list_value < part_func_value)
  2520. min_list_index= list_index + 1;
  2521. else if (list_value > part_func_value)
  2522. {
  2523. if (!list_index)
  2524. goto notfound;
  2525. max_list_index= list_index - 1;
  2526. }
  2527. else
  2528. {
  2529. DBUG_RETURN(list_index + test(left_endpoint ^ include_endpoint));
  2530. }
  2531. } while (max_list_index >= min_list_index);
  2532. notfound:
  2533. if (list_value < part_func_value)
  2534. list_index++;
  2535. DBUG_RETURN(list_index);
  2536. }
  2537. int get_partition_id_range(partition_info *part_info,
  2538. uint32 *part_id,
  2539. longlong *func_value)
  2540. {
  2541. longlong *range_array= part_info->range_int_array;
  2542. uint max_partition= part_info->no_parts - 1;
  2543. uint min_part_id= 0;
  2544. uint max_part_id= max_partition;
  2545. uint loc_part_id;
  2546. longlong part_func_value;
  2547. int error= part_val_int(part_info->part_expr, &part_func_value);
  2548. bool unsigned_flag= part_info->part_expr->unsigned_flag;
  2549. DBUG_ENTER("get_partition_id_range");
  2550. if (error)
  2551. DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
  2552. if (part_info->part_expr->null_value)
  2553. {
  2554. *part_id= 0;
  2555. DBUG_RETURN(0);
  2556. }
  2557. *func_value= part_func_value;
  2558. if (unsigned_flag)
  2559. part_func_value-= 0x8000000000000000ULL;
  2560. /* Search for the partition containing part_func_value */
  2561. while (max_part_id > min_part_id)
  2562. {
  2563. loc_part_id= (max_part_id + min_part_id) / 2;
  2564. if (range_array[loc_part_id] <= part_func_value)
  2565. min_part_id= loc_part_id + 1;
  2566. else
  2567. max_part_id= loc_part_id;
  2568. }
  2569. loc_part_id= max_part_id;
  2570. *part_id= (uint32)loc_part_id;
  2571. if (loc_part_id == max_partition &&
  2572. part_func_value >= range_array[loc_part_id] &&
  2573. !part_info->defined_max_value)
  2574. DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
  2575. DBUG_PRINT("exit",("partition: %d", *part_id));
  2576. DBUG_RETURN(0);
  2577. }
  2578. /*
  2579. Find the sub-array of part_info->range_int_array that covers given interval
  2580. SYNOPSIS
  2581. get_partition_id_range_for_endpoint()
  2582. part_info Partitioning info (partitioning type must be RANGE)
  2583. left_endpoint TRUE - the interval is [a; +inf) or (a; +inf)
  2584. FALSE - the interval is (-inf; a] or (-inf; a).
  2585. include_endpoint TRUE <=> the endpoint itself is included in the
  2586. interval
  2587. DESCRIPTION
  2588. This function finds the sub-array of part_info->range_int_array where the
  2589. elements have non-empty intersections with the given interval.
  2590. A range_int_array element at index idx represents the interval
  2591. [range_int_array[idx-1], range_int_array[idx]),
  2592. intervals are disjoint and ordered by their right bound, so
  2593. 1. For [a; +inf) or (a; +inf)-type intervals (left_endpoint==TRUE), the
  2594. sought sub-array starts at some index idx and continues till array end.
  2595. The function returns first number idx, such that the interval
  2596. represented by range_int_array[idx] has non empty intersection with
  2597. the passed interval.
  2598. 2. For (-inf; a] or (-inf; a)-type intervals (left_endpoint==FALSE), the
  2599. sought sub-array starts at array start and continues till some last
  2600. index idx.
  2601. The function returns first number idx, such that the interval
  2602. represented by range_int_array[idx] has EMPTY intersection with the
  2603. passed interval.
  2604. If the interval represented by the last array element has non-empty
  2605. intersection with the passed interval, part_info->no_parts is
  2606. returned.
  2607. RETURN
  2608. The edge of corresponding part_info->range_int_array sub-array.
  2609. */
  2610. static uint32
  2611. get_partition_id_range_for_endpoint_charset(partition_info *part_info,
  2612. bool left_endpoint,
  2613. bool include_endpoint)
  2614. {
  2615. uint32 res;
  2616. copy_to_part_field_buffers(part_info->part_field_array,
  2617. part_info->part_field_buffers,
  2618. part_info->restore_part_field_ptrs);
  2619. res= get_partition_id_range_for_endpoint(part_info, left_endpoint,
  2620. include_endpoint);
  2621. restore_part_field_pointers(part_info->part_field_array,
  2622. part_info->restore_part_field_ptrs);
  2623. return res;
  2624. }
  2625. uint32 get_partition_id_range_for_endpoint(partition_info *part_info,
  2626. bool left_endpoint,
  2627. bool include_endpoint)
  2628. {
  2629. longlong *range_array= part_info->range_int_array;
  2630. longlong part_end_val;
  2631. uint max_partition= part_info->no_parts - 1;
  2632. uint min_part_id= 0, max_part_id= max_partition, loc_part_id;
  2633. /* Get the partitioning function value for the endpoint */
  2634. longlong part_func_value=
  2635. part_info->part_expr->val_int_endpoint(left_endpoint, &include_endpoint);
  2636. bool unsigned_flag= part_info->part_expr->unsigned_flag;
  2637. DBUG_ENTER("get_partition_id_range_for_endpoint");
  2638. if (part_info->part_expr->null_value)
  2639. {
  2640. /*
  2641. Special handling for MONOTONIC functions that can return NULL for
  2642. values that are comparable. I.e.
  2643. '2000-00-00' can be compared to '2000-01-01' but TO_DAYS('2000-00-00')
  2644. returns NULL which cannot be compared used <, >, <=, >= etc.
  2645. Otherwise, just return the first partition
  2646. (may be included if not left endpoint)
  2647. */
  2648. enum_monotonicity_info monotonic;
  2649. monotonic= part_info->part_expr->get_monotonicity_info();
  2650. if (monotonic != MONOTONIC_INCREASING_NOT_NULL &&
  2651. monotonic != MONOTONIC_STRICT_INCREASING_NOT_NULL)
  2652. {
  2653. /* F(col) can not return NULL, return partition with lowest value */
  2654. if (!left_endpoint && include_endpoint)
  2655. DBUG_RETURN(1);
  2656. DBUG_RETURN(0);
  2657. }
  2658. }
  2659. if (unsigned_flag)
  2660. part_func_value-= 0x8000000000000000ULL;
  2661. if (left_endpoint && !include_endpoint)
  2662. part_func_value++;
  2663. /*
  2664. Search for the partition containing part_func_value
  2665. (including the right endpoint).
  2666. */
  2667. while (max_part_id > min_part_id)
  2668. {
  2669. loc_part_id= (max_part_id + min_part_id) / 2;
  2670. if (range_array[loc_part_id] < part_func_value)
  2671. min_part_id= loc_part_id + 1;
  2672. else
  2673. max_part_id= loc_part_id;
  2674. }
  2675. loc_part_id= max_part_id;
  2676. /* Adjust for endpoints */
  2677. part_end_val= range_array[loc_part_id];
  2678. if (left_endpoint)
  2679. {
  2680. DBUG_ASSERT(part_func_value > part_end_val ?
  2681. (loc_part_id == max_partition &&
  2682. !part_info->defined_max_value) :
  2683. 1);
  2684. /*
  2685. In case of PARTITION p VALUES LESS THAN MAXVALUE
  2686. the maximum value is in the current (last) partition.
  2687. If value is equal or greater than the endpoint,
  2688. the range starts from the next partition.
  2689. */
  2690. if (part_func_value >= part_end_val &&
  2691. (loc_part_id < max_partition || !part_info->defined_max_value))
  2692. loc_part_id++;
  2693. }
  2694. else
  2695. {
  2696. /* if 'WHERE <= X' and partition is LESS THAN (X) include next partition */
  2697. if (include_endpoint && loc_part_id < max_partition &&
  2698. part_func_value == part_end_val)
  2699. loc_part_id++;
  2700. /* Right endpoint, set end after correct partition */
  2701. loc_part_id++;
  2702. }
  2703. DBUG_RETURN(loc_part_id);
  2704. }
  2705. int get_partition_id_hash_nosub(partition_info *part_info,
  2706. uint32 *part_id,
  2707. longlong *func_value)
  2708. {
  2709. return get_part_id_hash(part_info->no_parts, part_info->part_expr,
  2710. part_id, func_value);
  2711. }
  2712. int get_partition_id_linear_hash_nosub(partition_info *part_info,
  2713. uint32 *part_id,
  2714. longlong *func_value)
  2715. {
  2716. return get_part_id_linear_hash(part_info, part_info->no_parts,
  2717. part_info->part_expr, part_id, func_value);
  2718. }
  2719. int get_partition_id_key_nosub(partition_info *part_info,
  2720. uint32 *part_id,
  2721. longlong *func_value)
  2722. {
  2723. *part_id= get_part_id_key(part_info->part_field_array,
  2724. part_info->no_parts, func_value);
  2725. return 0;
  2726. }
  2727. int get_partition_id_linear_key_nosub(partition_info *part_info,
  2728. uint32 *part_id,
  2729. longlong *func_value)
  2730. {
  2731. *part_id= get_part_id_linear_key(part_info,
  2732. part_info->part_field_array,
  2733. part_info->no_parts, func_value);
  2734. return 0;
  2735. }
  2736. int get_partition_id_range_sub_hash(partition_info *part_info,
  2737. uint32 *part_id,
  2738. longlong *func_value)
  2739. {
  2740. uint32 loc_part_id, sub_part_id;
  2741. uint no_subparts;
  2742. longlong local_func_value;
  2743. int error;
  2744. DBUG_ENTER("get_partition_id_range_sub_hash");
  2745. LINT_INIT(loc_part_id);
  2746. LINT_INIT(sub_part_id);
  2747. if (unlikely((error= get_partition_id_range(part_info, &loc_part_id,
  2748. func_value))))
  2749. {
  2750. DBUG_RETURN(error);
  2751. }
  2752. no_subparts= part_info->no_subparts;
  2753. if (unlikely((error= get_part_id_hash(no_subparts, part_info->subpart_expr,
  2754. &sub_part_id, &local_func_value))))
  2755. {
  2756. DBUG_RETURN(error);
  2757. }
  2758. *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
  2759. DBUG_RETURN(0);
  2760. }
  2761. int get_partition_id_range_sub_linear_hash(partition_info *part_info,
  2762. uint32 *part_id,
  2763. longlong *func_value)
  2764. {
  2765. uint32 loc_part_id, sub_part_id;
  2766. uint no_subparts;
  2767. longlong local_func_value;
  2768. int error;
  2769. DBUG_ENTER("get_partition_id_range_sub_linear_hash");
  2770. LINT_INIT(loc_part_id);
  2771. LINT_INIT(sub_part_id);
  2772. if (unlikely((error= get_partition_id_range(part_info, &loc_part_id,
  2773. func_value))))
  2774. {
  2775. DBUG_RETURN(error);
  2776. }
  2777. no_subparts= part_info->no_subparts;
  2778. if (unlikely((error= get_part_id_linear_hash(part_info, no_subparts,
  2779. part_info->subpart_expr,
  2780. &sub_part_id,
  2781. &local_func_value))))
  2782. {
  2783. DBUG_RETURN(error);
  2784. }
  2785. *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
  2786. DBUG_RETURN(0);
  2787. }
  2788. int get_partition_id_range_sub_key(partition_info *part_info,
  2789. uint32 *part_id,
  2790. longlong *func_value)
  2791. {
  2792. uint32 loc_part_id, sub_part_id;
  2793. uint no_subparts;
  2794. longlong local_func_value;
  2795. int error;
  2796. DBUG_ENTER("get_partition_id_range_sub_key");
  2797. LINT_INIT(loc_part_id);
  2798. if (unlikely((error= get_partition_id_range(part_info, &loc_part_id,
  2799. func_value))))
  2800. {
  2801. DBUG_RETURN(error);
  2802. }
  2803. no_subparts= part_info->no_subparts;
  2804. sub_part_id= get_part_id_key(part_info->subpart_field_array,
  2805. no_subparts, &local_func_value);
  2806. *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
  2807. DBUG_RETURN(0);
  2808. }
  2809. int get_partition_id_range_sub_linear_key(partition_info *part_info,
  2810. uint32 *part_id,
  2811. longlong *func_value)
  2812. {
  2813. uint32 loc_part_id, sub_part_id;
  2814. uint no_subparts;
  2815. longlong local_func_value;
  2816. int error;
  2817. DBUG_ENTER("get_partition_id_range_sub_linear_key");
  2818. LINT_INIT(loc_part_id);
  2819. if (unlikely((error= get_partition_id_range(part_info, &loc_part_id,
  2820. func_value))))
  2821. {
  2822. DBUG_RETURN(error);
  2823. }
  2824. no_subparts= part_info->no_subparts;
  2825. sub_part_id= get_part_id_linear_key(part_info,
  2826. part_info->subpart_field_array,
  2827. no_subparts, &local_func_value);
  2828. *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
  2829. DBUG_RETURN(0);
  2830. }
  2831. int get_partition_id_list_sub_hash(partition_info *part_info,
  2832. uint32 *part_id,
  2833. longlong *func_value)
  2834. {
  2835. uint32 loc_part_id, sub_part_id;
  2836. uint no_subparts;
  2837. longlong local_func_value;
  2838. int error;
  2839. DBUG_ENTER("get_partition_id_list_sub_hash");
  2840. LINT_INIT(sub_part_id);
  2841. if (unlikely((error= get_partition_id_list(part_info, &loc_part_id,
  2842. func_value))))
  2843. {
  2844. DBUG_RETURN(error);
  2845. }
  2846. no_subparts= part_info->no_subparts;
  2847. if (unlikely((error= get_part_id_hash(no_subparts, part_info->subpart_expr,
  2848. &sub_part_id, &local_func_value))))
  2849. {
  2850. DBUG_RETURN(error);
  2851. }
  2852. *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
  2853. DBUG_RETURN(0);
  2854. }
  2855. int get_partition_id_list_sub_linear_hash(partition_info *part_info,
  2856. uint32 *part_id,
  2857. longlong *func_value)
  2858. {
  2859. uint32 loc_part_id, sub_part_id;
  2860. uint no_subparts;
  2861. longlong local_func_value;
  2862. int error;
  2863. DBUG_ENTER("get_partition_id_list_sub_linear_hash");
  2864. LINT_INIT(sub_part_id);
  2865. if (unlikely((error= get_partition_id_list(part_info, &loc_part_id,
  2866. func_value))))
  2867. {
  2868. DBUG_RETURN(error);
  2869. }
  2870. no_subparts= part_info->no_subparts;
  2871. if (unlikely((error= get_part_id_linear_hash(part_info, no_subparts,
  2872. part_info->subpart_expr,
  2873. &sub_part_id,
  2874. &local_func_value))))
  2875. {
  2876. DBUG_RETURN(error);
  2877. }
  2878. *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
  2879. DBUG_RETURN(0);
  2880. }
  2881. int get_partition_id_list_sub_key(partition_info *part_info,
  2882. uint32 *part_id,
  2883. longlong *func_value)
  2884. {
  2885. uint32 loc_part_id, sub_part_id;
  2886. uint no_subparts;
  2887. longlong local_func_value;
  2888. int error;
  2889. DBUG_ENTER("get_partition_id_range_sub_key");
  2890. if (unlikely((error= get_partition_id_list(part_info, &loc_part_id,
  2891. func_value))))
  2892. {
  2893. DBUG_RETURN(error);
  2894. }
  2895. no_subparts= part_info->no_subparts;
  2896. sub_part_id= get_part_id_key(part_info->subpart_field_array,
  2897. no_subparts, &local_func_value);
  2898. *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
  2899. DBUG_RETURN(0);
  2900. }
  2901. int get_partition_id_list_sub_linear_key(partition_info *part_info,
  2902. uint32 *part_id,
  2903. longlong *func_value)
  2904. {
  2905. uint32 loc_part_id, sub_part_id;
  2906. uint no_subparts;
  2907. longlong local_func_value;
  2908. int error;
  2909. DBUG_ENTER("get_partition_id_list_sub_linear_key");
  2910. if (unlikely((error= get_partition_id_list(part_info, &loc_part_id,
  2911. func_value))))
  2912. {
  2913. DBUG_RETURN(error);
  2914. }
  2915. no_subparts= part_info->no_subparts;
  2916. sub_part_id= get_part_id_linear_key(part_info,
  2917. part_info->subpart_field_array,
  2918. no_subparts, &local_func_value);
  2919. *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
  2920. DBUG_RETURN(0);
  2921. }
  2922. /*
  2923. This function is used to calculate the subpartition id
  2924. SYNOPSIS
  2925. get_subpartition_id()
  2926. part_info A reference to the partition_info struct where all the
  2927. desired information is given
  2928. RETURN VALUE
  2929. part_id The subpartition identity
  2930. DESCRIPTION
  2931. A routine used in some SELECT's when only partial knowledge of the
  2932. partitions is known.
  2933. It is actually 4 different variants of this function which are called
  2934. through a function pointer.
  2935. get_partition_id_hash_sub
  2936. get_partition_id_key_sub
  2937. get_partition_id_linear_hash_sub
  2938. get_partition_id_linear_key_sub
  2939. */
  2940. int get_partition_id_hash_sub(partition_info *part_info,
  2941. uint32 *part_id)
  2942. {
  2943. longlong func_value;
  2944. return get_part_id_hash(part_info->no_subparts, part_info->subpart_expr,
  2945. part_id, &func_value);
  2946. }
  2947. int get_partition_id_linear_hash_sub(partition_info *part_info,
  2948. uint32 *part_id)
  2949. {
  2950. longlong func_value;
  2951. return get_part_id_linear_hash(part_info, part_info->no_subparts,
  2952. part_info->subpart_expr, part_id,
  2953. &func_value);
  2954. }
  2955. int get_partition_id_key_sub(partition_info *part_info,
  2956. uint32 *part_id)
  2957. {
  2958. longlong func_value;
  2959. *part_id= get_part_id_key(part_info->subpart_field_array,
  2960. part_info->no_subparts, &func_value);
  2961. return FALSE;
  2962. }
  2963. int get_partition_id_linear_key_sub(partition_info *part_info,
  2964. uint32 *part_id)
  2965. {
  2966. longlong func_value;
  2967. *part_id= get_part_id_linear_key(part_info,
  2968. part_info->subpart_field_array,
  2969. part_info->no_subparts, &func_value);
  2970. return FALSE;
  2971. }
  2972. /*
  2973. Set an indicator on all partition fields that are set by the key
  2974. SYNOPSIS
  2975. set_PF_fields_in_key()
  2976. key_info Information about the index
  2977. key_length Length of key
  2978. RETURN VALUE
  2979. TRUE Found partition field set by key
  2980. FALSE No partition field set by key
  2981. */
  2982. static bool set_PF_fields_in_key(KEY *key_info, uint key_length)
  2983. {
  2984. KEY_PART_INFO *key_part;
  2985. bool found_part_field= FALSE;
  2986. DBUG_ENTER("set_PF_fields_in_key");
  2987. for (key_part= key_info->key_part; (int)key_length > 0; key_part++)
  2988. {
  2989. if (key_part->null_bit)
  2990. key_length--;
  2991. if (key_part->type == HA_KEYTYPE_BIT)
  2992. {
  2993. if (((Field_bit*)key_part->field)->bit_len)
  2994. key_length--;
  2995. }
  2996. if (key_part->key_part_flag & (HA_BLOB_PART + HA_VAR_LENGTH_PART))
  2997. {
  2998. key_length-= HA_KEY_BLOB_LENGTH;
  2999. }
  3000. if (key_length < key_part->length)
  3001. break;
  3002. key_length-= key_part->length;
  3003. if (key_part->field->flags & FIELD_IN_PART_FUNC_FLAG)
  3004. {
  3005. found_part_field= TRUE;
  3006. key_part->field->flags|= GET_FIXED_FIELDS_FLAG;
  3007. }
  3008. }
  3009. DBUG_RETURN(found_part_field);
  3010. }
  3011. /*
  3012. We have found that at least one partition field was set by a key, now
  3013. check if a partition function has all its fields bound or not.
  3014. SYNOPSIS
  3015. check_part_func_bound()
  3016. ptr Array of fields NULL terminated (partition fields)
  3017. RETURN VALUE
  3018. TRUE All fields in partition function are set
  3019. FALSE Not all fields in partition function are set
  3020. */
  3021. static bool check_part_func_bound(Field **ptr)
  3022. {
  3023. bool result= TRUE;
  3024. DBUG_ENTER("check_part_func_bound");
  3025. for (; *ptr; ptr++)
  3026. {
  3027. if (!((*ptr)->flags & GET_FIXED_FIELDS_FLAG))
  3028. {
  3029. result= FALSE;
  3030. break;
  3031. }
  3032. }
  3033. DBUG_RETURN(result);
  3034. }
  3035. /*
  3036. Get the id of the subpartitioning part by using the key buffer of the
  3037. index scan.
  3038. SYNOPSIS
  3039. get_sub_part_id_from_key()
  3040. table The table object
  3041. buf A buffer that can be used to evaluate the partition function
  3042. key_info The index object
  3043. key_spec A key_range containing key and key length
  3044. out:part_id The returned partition id
  3045. RETURN VALUES
  3046. TRUE All fields in partition function are set
  3047. FALSE Not all fields in partition function are set
  3048. DESCRIPTION
  3049. Use key buffer to set-up record in buf, move field pointers and
  3050. get the partition identity and restore field pointers afterwards.
  3051. */
  3052. static int get_sub_part_id_from_key(const TABLE *table,uchar *buf,
  3053. KEY *key_info,
  3054. const key_range *key_spec,
  3055. uint32 *part_id)
  3056. {
  3057. uchar *rec0= table->record[0];
  3058. partition_info *part_info= table->part_info;
  3059. int res;
  3060. DBUG_ENTER("get_sub_part_id_from_key");
  3061. key_restore(buf, (uchar*)key_spec->key, key_info, key_spec->length);
  3062. if (likely(rec0 == buf))
  3063. {
  3064. res= part_info->get_subpartition_id(part_info, part_id);
  3065. }
  3066. else
  3067. {
  3068. Field **part_field_array= part_info->subpart_field_array;
  3069. set_field_ptr(part_field_array, buf, rec0);
  3070. res= part_info->get_subpartition_id(part_info, part_id);
  3071. set_field_ptr(part_field_array, rec0, buf);
  3072. }
  3073. DBUG_RETURN(res);
  3074. }
  3075. /*
  3076. Get the id of the partitioning part by using the key buffer of the
  3077. index scan.
  3078. SYNOPSIS
  3079. get_part_id_from_key()
  3080. table The table object
  3081. buf A buffer that can be used to evaluate the partition function
  3082. key_info The index object
  3083. key_spec A key_range containing key and key length
  3084. out:part_id Partition to use
  3085. RETURN VALUES
  3086. TRUE Partition to use not found
  3087. FALSE Ok, part_id indicates partition to use
  3088. DESCRIPTION
  3089. Use key buffer to set-up record in buf, move field pointers and
  3090. get the partition identity and restore field pointers afterwards.
  3091. */
  3092. bool get_part_id_from_key(const TABLE *table, uchar *buf, KEY *key_info,
  3093. const key_range *key_spec, uint32 *part_id)
  3094. {
  3095. bool result;
  3096. uchar *rec0= table->record[0];
  3097. partition_info *part_info= table->part_info;
  3098. longlong func_value;
  3099. DBUG_ENTER("get_part_id_from_key");
  3100. key_restore(buf, (uchar*)key_spec->key, key_info, key_spec->length);
  3101. if (likely(rec0 == buf))
  3102. {
  3103. result= part_info->get_part_partition_id(part_info, part_id,
  3104. &func_value);
  3105. }
  3106. else
  3107. {
  3108. Field **part_field_array= part_info->part_field_array;
  3109. set_field_ptr(part_field_array, buf, rec0);
  3110. result= part_info->get_part_partition_id(part_info, part_id,
  3111. &func_value);
  3112. set_field_ptr(part_field_array, rec0, buf);
  3113. }
  3114. DBUG_RETURN(result);
  3115. }
  3116. /*
  3117. Get the partitioning id of the full PF by using the key buffer of the
  3118. index scan.
  3119. SYNOPSIS
  3120. get_full_part_id_from_key()
  3121. table The table object
  3122. buf A buffer that is used to evaluate the partition function
  3123. key_info The index object
  3124. key_spec A key_range containing key and key length
  3125. out:part_spec A partition id containing start part and end part
  3126. RETURN VALUES
  3127. part_spec
  3128. No partitions to scan is indicated by end_part > start_part when returning
  3129. DESCRIPTION
  3130. Use key buffer to set-up record in buf, move field pointers if needed and
  3131. get the partition identity and restore field pointers afterwards.
  3132. */
  3133. void get_full_part_id_from_key(const TABLE *table, uchar *buf,
  3134. KEY *key_info,
  3135. const key_range *key_spec,
  3136. part_id_range *part_spec)
  3137. {
  3138. bool result;
  3139. partition_info *part_info= table->part_info;
  3140. uchar *rec0= table->record[0];
  3141. longlong func_value;
  3142. DBUG_ENTER("get_full_part_id_from_key");
  3143. key_restore(buf, (uchar*)key_spec->key, key_info, key_spec->length);
  3144. if (likely(rec0 == buf))
  3145. {
  3146. result= part_info->get_partition_id(part_info, &part_spec->start_part,
  3147. &func_value);
  3148. }
  3149. else
  3150. {
  3151. Field **part_field_array= part_info->full_part_field_array;
  3152. set_field_ptr(part_field_array, buf, rec0);
  3153. result= part_info->get_partition_id(part_info, &part_spec->start_part,
  3154. &func_value);
  3155. set_field_ptr(part_field_array, rec0, buf);
  3156. }
  3157. part_spec->end_part= part_spec->start_part;
  3158. if (unlikely(result))
  3159. part_spec->start_part++;
  3160. DBUG_VOID_RETURN;
  3161. }
  3162. /*
  3163. Prune the set of partitions to use in query
  3164. SYNOPSIS
  3165. prune_partition_set()
  3166. table The table object
  3167. out:part_spec Contains start part, end part
  3168. DESCRIPTION
  3169. This function is called to prune the range of partitions to scan by
  3170. checking the used_partitions bitmap.
  3171. If start_part > end_part at return it means no partition needs to be
  3172. scanned. If start_part == end_part it always means a single partition
  3173. needs to be scanned.
  3174. RETURN VALUE
  3175. part_spec
  3176. */
  3177. void prune_partition_set(const TABLE *table, part_id_range *part_spec)
  3178. {
  3179. int last_partition= -1;
  3180. uint i;
  3181. partition_info *part_info= table->part_info;
  3182. DBUG_ENTER("prune_partition_set");
  3183. for (i= part_spec->start_part; i <= part_spec->end_part; i++)
  3184. {
  3185. if (bitmap_is_set(&(part_info->used_partitions), i))
  3186. {
  3187. DBUG_PRINT("info", ("Partition %d is set", i));
  3188. if (last_partition == -1)
  3189. /* First partition found in set and pruned bitmap */
  3190. part_spec->start_part= i;
  3191. last_partition= i;
  3192. }
  3193. }
  3194. if (last_partition == -1)
  3195. /* No partition found in pruned bitmap */
  3196. part_spec->start_part= part_spec->end_part + 1;
  3197. else //if (last_partition != -1)
  3198. part_spec->end_part= last_partition;
  3199. DBUG_VOID_RETURN;
  3200. }
  3201. /*
  3202. Get the set of partitions to use in query.
  3203. SYNOPSIS
  3204. get_partition_set()
  3205. table The table object
  3206. buf A buffer that can be used to evaluate the partition function
  3207. index The index of the key used, if MAX_KEY no index used
  3208. key_spec A key_range containing key and key length
  3209. out:part_spec Contains start part, end part and indicator if bitmap is
  3210. used for which partitions to scan
  3211. DESCRIPTION
  3212. This function is called to discover which partitions to use in an index
  3213. scan or a full table scan.
  3214. It returns a range of partitions to scan. If there are holes in this
  3215. range with partitions that are not needed to scan a bit array is used
  3216. to signal which partitions to use and which not to use.
  3217. If start_part > end_part at return it means no partition needs to be
  3218. scanned. If start_part == end_part it always means a single partition
  3219. needs to be scanned.
  3220. RETURN VALUE
  3221. part_spec
  3222. */
  3223. void get_partition_set(const TABLE *table, uchar *buf, const uint index,
  3224. const key_range *key_spec, part_id_range *part_spec)
  3225. {
  3226. partition_info *part_info= table->part_info;
  3227. uint no_parts= part_info->get_tot_partitions();
  3228. uint i, part_id;
  3229. uint sub_part= no_parts;
  3230. uint32 part_part= no_parts;
  3231. KEY *key_info= NULL;
  3232. bool found_part_field= FALSE;
  3233. DBUG_ENTER("get_partition_set");
  3234. part_spec->start_part= 0;
  3235. part_spec->end_part= no_parts - 1;
  3236. if ((index < MAX_KEY) &&
  3237. key_spec->flag == (uint)HA_READ_KEY_EXACT &&
  3238. part_info->some_fields_in_PF.is_set(index))
  3239. {
  3240. key_info= table->key_info+index;
  3241. /*
  3242. The index can potentially provide at least one PF-field (field in the
  3243. partition function). Thus it is interesting to continue our probe.
  3244. */
  3245. if (key_spec->length == key_info->key_length)
  3246. {
  3247. /*
  3248. The entire key is set so we can check whether we can immediately
  3249. derive either the complete PF or if we can derive either
  3250. the top PF or the subpartitioning PF. This can be established by
  3251. checking precalculated bits on each index.
  3252. */
  3253. if (part_info->all_fields_in_PF.is_set(index))
  3254. {
  3255. /*
  3256. We can derive the exact partition to use, no more than this one
  3257. is needed.
  3258. */
  3259. get_full_part_id_from_key(table,buf,key_info,key_spec,part_spec);
  3260. /*
  3261. Check if range can be adjusted by looking in used_partitions
  3262. */
  3263. prune_partition_set(table, part_spec);
  3264. DBUG_VOID_RETURN;
  3265. }
  3266. else if (part_info->is_sub_partitioned())
  3267. {
  3268. if (part_info->all_fields_in_SPF.is_set(index))
  3269. {
  3270. if (get_sub_part_id_from_key(table, buf, key_info, key_spec, &sub_part))
  3271. {
  3272. part_spec->start_part= no_parts;
  3273. DBUG_VOID_RETURN;
  3274. }
  3275. }
  3276. else if (part_info->all_fields_in_PPF.is_set(index))
  3277. {
  3278. if (get_part_id_from_key(table,buf,key_info,
  3279. key_spec,(uint32*)&part_part))
  3280. {
  3281. /*
  3282. The value of the RANGE or LIST partitioning was outside of
  3283. allowed values. Thus it is certain that the result of this
  3284. scan will be empty.
  3285. */
  3286. part_spec->start_part= no_parts;
  3287. DBUG_VOID_RETURN;
  3288. }
  3289. }
  3290. }
  3291. }
  3292. else
  3293. {
  3294. /*
  3295. Set an indicator on all partition fields that are bound.
  3296. If at least one PF-field was bound it pays off to check whether
  3297. the PF or PPF or SPF has been bound.
  3298. (PF = Partition Function, SPF = Subpartition Function and
  3299. PPF = Partition Function part of subpartitioning)
  3300. */
  3301. if ((found_part_field= set_PF_fields_in_key(key_info,
  3302. key_spec->length)))
  3303. {
  3304. if (check_part_func_bound(part_info->full_part_field_array))
  3305. {
  3306. /*
  3307. We were able to bind all fields in the partition function even
  3308. by using only a part of the key. Calculate the partition to use.
  3309. */
  3310. get_full_part_id_from_key(table,buf,key_info,key_spec,part_spec);
  3311. clear_indicator_in_key_fields(key_info);
  3312. /*
  3313. Check if range can be adjusted by looking in used_partitions
  3314. */
  3315. prune_partition_set(table, part_spec);
  3316. DBUG_VOID_RETURN;
  3317. }
  3318. else if (part_info->is_sub_partitioned())
  3319. {
  3320. if (check_part_func_bound(part_info->subpart_field_array))
  3321. {
  3322. if (get_sub_part_id_from_key(table, buf, key_info, key_spec, &sub_part))
  3323. {
  3324. part_spec->start_part= no_parts;
  3325. clear_indicator_in_key_fields(key_info);
  3326. DBUG_VOID_RETURN;
  3327. }
  3328. }
  3329. else if (check_part_func_bound(part_info->part_field_array))
  3330. {
  3331. if (get_part_id_from_key(table,buf,key_info,key_spec,&part_part))
  3332. {
  3333. part_spec->start_part= no_parts;
  3334. clear_indicator_in_key_fields(key_info);
  3335. DBUG_VOID_RETURN;
  3336. }
  3337. }
  3338. }
  3339. }
  3340. }
  3341. }
  3342. {
  3343. /*
  3344. The next step is to analyse the table condition to see whether any
  3345. information about which partitions to scan can be derived from there.
  3346. Currently not implemented.
  3347. */
  3348. }
  3349. /*
  3350. If we come here we have found a range of sorts we have either discovered
  3351. nothing or we have discovered a range of partitions with possible holes
  3352. in it. We need a bitvector to further the work here.
  3353. */
  3354. if (!(part_part == no_parts && sub_part == no_parts))
  3355. {
  3356. /*
  3357. We can only arrive here if we are using subpartitioning.
  3358. */
  3359. if (part_part != no_parts)
  3360. {
  3361. /*
  3362. We know the top partition and need to scan all underlying
  3363. subpartitions. This is a range without holes.
  3364. */
  3365. DBUG_ASSERT(sub_part == no_parts);
  3366. part_spec->start_part= part_part * part_info->no_subparts;
  3367. part_spec->end_part= part_spec->start_part+part_info->no_subparts - 1;
  3368. }
  3369. else
  3370. {
  3371. DBUG_ASSERT(sub_part != no_parts);
  3372. part_spec->start_part= sub_part;
  3373. part_spec->end_part=sub_part+
  3374. (part_info->no_subparts*(part_info->no_parts-1));
  3375. for (i= 0, part_id= sub_part; i < part_info->no_parts;
  3376. i++, part_id+= part_info->no_subparts)
  3377. ; //Set bit part_id in bit array
  3378. }
  3379. }
  3380. if (found_part_field)
  3381. clear_indicator_in_key_fields(key_info);
  3382. /*
  3383. Check if range can be adjusted by looking in used_partitions
  3384. */
  3385. prune_partition_set(table, part_spec);
  3386. DBUG_VOID_RETURN;
  3387. }
  3388. /*
  3389. If the table is partitioned we will read the partition info into the
  3390. .frm file here.
  3391. -------------------------------
  3392. | Fileinfo 64 bytes |
  3393. -------------------------------
  3394. | Formnames 7 bytes |
  3395. -------------------------------
  3396. | Not used 4021 bytes |
  3397. -------------------------------
  3398. | Keyinfo + record |
  3399. -------------------------------
  3400. | Padded to next multiple |
  3401. | of IO_SIZE |
  3402. -------------------------------
  3403. | Forminfo 288 bytes |
  3404. -------------------------------
  3405. | Screen buffer, to make |
  3406. |field names readable |
  3407. -------------------------------
  3408. | Packed field info |
  3409. |17 + 1 + strlen(field_name) |
  3410. | + 1 end of file character |
  3411. -------------------------------
  3412. | Partition info |
  3413. -------------------------------
  3414. We provide the length of partition length in Fileinfo[55-58].
  3415. Read the partition syntax from the frm file and parse it to get the
  3416. data structures of the partitioning.
  3417. SYNOPSIS
  3418. mysql_unpack_partition()
  3419. thd Thread object
  3420. part_buf Partition info from frm file
  3421. part_info_len Length of partition syntax
  3422. table Table object of partitioned table
  3423. create_table_ind Is it called from CREATE TABLE
  3424. default_db_type What is the default engine of the table
  3425. work_part_info_used Flag is raised if we don't create new
  3426. part_info, but used thd->work_part_info
  3427. RETURN VALUE
  3428. TRUE Error
  3429. FALSE Sucess
  3430. DESCRIPTION
  3431. Read the partition syntax from the current position in the frm file.
  3432. Initiate a LEX object, save the list of item tree objects to free after
  3433. the query is done. Set-up partition info object such that parser knows
  3434. it is called from internally. Call parser to create data structures
  3435. (best possible recreation of item trees and so forth since there is no
  3436. serialisation of these objects other than in parseable text format).
  3437. We need to save the text of the partition functions since it is not
  3438. possible to retrace this given an item tree.
  3439. */
  3440. bool mysql_unpack_partition(THD *thd,
  3441. char *part_buf, uint part_info_len,
  3442. const char *part_state, uint part_state_len,
  3443. TABLE* table, bool is_create_table_ind,
  3444. handlerton *default_db_type,
  3445. bool *work_part_info_used)
  3446. {
  3447. bool result= TRUE;
  3448. partition_info *part_info;
  3449. CHARSET_INFO *old_character_set_client= thd->variables.character_set_client;
  3450. LEX *old_lex= thd->lex;
  3451. LEX lex;
  3452. DBUG_ENTER("mysql_unpack_partition");
  3453. thd->lex= &lex;
  3454. thd->variables.character_set_client= system_charset_info;
  3455. Parser_state parser_state;
  3456. if (parser_state.init(thd, part_buf, part_info_len))
  3457. goto end;
  3458. lex_start(thd);
  3459. *work_part_info_used= false;
  3460. /*
  3461. We need to use the current SELECT_LEX since I need to keep the
  3462. Name_resolution_context object which is referenced from the
  3463. Item_field objects.
  3464. This is not a nice solution since if the parser uses current_select
  3465. for anything else it will corrupt the current LEX object.
  3466. Also, we need to make sure there even is a select -- if the statement
  3467. was a "USE ...", current_select will be NULL, but we may still end up
  3468. here if we try to log to a partitioned table. This is currently
  3469. unsupported, but should still fail rather than crash!
  3470. */
  3471. if (!(thd->lex->current_select= old_lex->current_select))
  3472. goto end;
  3473. /*
  3474. All Items created is put into a free list on the THD object. This list
  3475. is used to free all Item objects after completing a query. We don't
  3476. want that to happen with the Item tree created as part of the partition
  3477. info. This should be attached to the table object and remain so until
  3478. the table object is released.
  3479. Thus we move away the current list temporarily and start a new list that
  3480. we then save in the partition info structure.
  3481. */
  3482. lex.part_info= new partition_info();/* Indicates MYSQLparse from this place */
  3483. if (!lex.part_info)
  3484. {
  3485. mem_alloc_error(sizeof(partition_info));
  3486. goto end;
  3487. }
  3488. lex.part_info->part_state= part_state;
  3489. lex.part_info->part_state_len= part_state_len;
  3490. DBUG_PRINT("info", ("Parse: %s", part_buf));
  3491. if (parse_sql(thd, & parser_state, NULL))
  3492. {
  3493. thd->free_items();
  3494. goto end;
  3495. }
  3496. /*
  3497. The parsed syntax residing in the frm file can still contain defaults.
  3498. The reason is that the frm file is sometimes saved outside of this
  3499. MySQL Server and used in backup and restore of clusters or partitioned
  3500. tables. It is not certain that the restore will restore exactly the
  3501. same default partitioning.
  3502. The easiest manner of handling this is to simply continue using the
  3503. part_info we already built up during mysql_create_table if we are
  3504. in the process of creating a table. If the table already exists we
  3505. need to discover the number of partitions for the default parts. Since
  3506. the handler object hasn't been created here yet we need to postpone this
  3507. to the fix_partition_func method.
  3508. */
  3509. DBUG_PRINT("info", ("Successful parse"));
  3510. part_info= lex.part_info;
  3511. DBUG_PRINT("info", ("default engine = %s, default_db_type = %s",
  3512. ha_resolve_storage_engine_name(part_info->default_engine_type),
  3513. ha_resolve_storage_engine_name(default_db_type)));
  3514. if (is_create_table_ind && old_lex->sql_command == SQLCOM_CREATE_TABLE)
  3515. {
  3516. if (old_lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
  3517. {
  3518. /*
  3519. This code is executed when we create table in CREATE TABLE t1 LIKE t2.
  3520. old_lex->query_tables contains table list element for t2 and the table
  3521. we are opening has name t1.
  3522. */
  3523. if (partition_default_handling(table, part_info, FALSE,
  3524. old_lex->query_tables->table->s->path.str))
  3525. {
  3526. result= TRUE;
  3527. goto end;
  3528. }
  3529. }
  3530. else
  3531. {
  3532. /*
  3533. When we come here we are doing a create table. In this case we
  3534. have already done some preparatory work on the old part_info
  3535. object. We don't really need this new partition_info object.
  3536. Thus we go back to the old partition info object.
  3537. We need to free any memory objects allocated on item_free_list
  3538. by the parser since we are keeping the old info from the first
  3539. parser call in CREATE TABLE.
  3540. We'll ensure that this object isn't put into table cache also
  3541. just to ensure we don't get into strange situations with the
  3542. item objects.
  3543. */
  3544. thd->free_items();
  3545. part_info= thd->work_part_info;
  3546. table->s->version= 0UL;
  3547. *work_part_info_used= true;
  3548. }
  3549. }
  3550. table->part_info= part_info;
  3551. table->file->set_part_info(part_info);
  3552. if (!part_info->default_engine_type)
  3553. part_info->default_engine_type= default_db_type;
  3554. DBUG_ASSERT(part_info->default_engine_type == default_db_type);
  3555. DBUG_ASSERT(part_info->default_engine_type->db_type != DB_TYPE_UNKNOWN);
  3556. DBUG_ASSERT(part_info->default_engine_type != partition_hton);
  3557. {
  3558. /*
  3559. This code part allocates memory for the serialised item information for
  3560. the partition functions. In most cases this is not needed but if the
  3561. table is used for SHOW CREATE TABLES or ALTER TABLE that modifies
  3562. partition information it is needed and the info is lost if we don't
  3563. save it here so unfortunately we have to do it here even if in most
  3564. cases it is not needed. This is a consequence of that item trees are
  3565. not serialisable.
  3566. */
  3567. uint part_func_len= part_info->part_func_len;
  3568. uint subpart_func_len= part_info->subpart_func_len;
  3569. char *part_func_string= NULL;
  3570. char *subpart_func_string= NULL;
  3571. if ((part_func_len &&
  3572. !((part_func_string= (char*) thd->alloc(part_func_len)))) ||
  3573. (subpart_func_len &&
  3574. !((subpart_func_string= (char*) thd->alloc(subpart_func_len)))))
  3575. {
  3576. mem_alloc_error(part_func_len);
  3577. thd->free_items();
  3578. goto end;
  3579. }
  3580. if (part_func_len)
  3581. memcpy(part_func_string, part_info->part_func_string, part_func_len);
  3582. if (subpart_func_len)
  3583. memcpy(subpart_func_string, part_info->subpart_func_string,
  3584. subpart_func_len);
  3585. part_info->part_func_string= part_func_string;
  3586. part_info->subpart_func_string= subpart_func_string;
  3587. }
  3588. result= FALSE;
  3589. end:
  3590. lex_end(thd->lex);
  3591. thd->lex= old_lex;
  3592. thd->variables.character_set_client= old_character_set_client;
  3593. DBUG_RETURN(result);
  3594. }
  3595. /*
  3596. Set engine type on all partition element objects
  3597. SYNOPSIS
  3598. set_engine_all_partitions()
  3599. part_info Partition info
  3600. engine_type Handlerton reference of engine
  3601. RETURN VALUES
  3602. NONE
  3603. */
  3604. static
  3605. void
  3606. set_engine_all_partitions(partition_info *part_info,
  3607. handlerton *engine_type)
  3608. {
  3609. uint i= 0;
  3610. List_iterator<partition_element> part_it(part_info->partitions);
  3611. do
  3612. {
  3613. partition_element *part_elem= part_it++;
  3614. part_elem->engine_type= engine_type;
  3615. if (part_info->is_sub_partitioned())
  3616. {
  3617. List_iterator<partition_element> sub_it(part_elem->subpartitions);
  3618. uint j= 0;
  3619. do
  3620. {
  3621. partition_element *sub_elem= sub_it++;
  3622. sub_elem->engine_type= engine_type;
  3623. } while (++j < part_info->no_subparts);
  3624. }
  3625. } while (++i < part_info->no_parts);
  3626. }
  3627. /*
  3628. SYNOPSIS
  3629. fast_end_partition()
  3630. thd Thread object
  3631. out:copied Number of records copied
  3632. out:deleted Number of records deleted
  3633. table_list Table list with the one table in it
  3634. empty Has nothing been done
  3635. lpt Struct to be used by error handler
  3636. RETURN VALUES
  3637. FALSE Success
  3638. TRUE Failure
  3639. DESCRIPTION
  3640. Support routine to handle the successful cases for partition
  3641. management.
  3642. */
  3643. static int fast_end_partition(THD *thd, ulonglong copied,
  3644. ulonglong deleted,
  3645. TABLE *table,
  3646. TABLE_LIST *table_list, bool is_empty,
  3647. ALTER_PARTITION_PARAM_TYPE *lpt,
  3648. bool written_bin_log)
  3649. {
  3650. int error;
  3651. char tmp_name[80];
  3652. DBUG_ENTER("fast_end_partition");
  3653. thd->proc_info="end";
  3654. if (!is_empty)
  3655. query_cache_invalidate3(thd, table_list, 0);
  3656. error= ha_autocommit_or_rollback(thd, 0);
  3657. if (end_active_trans(thd))
  3658. error= 1;
  3659. if (error)
  3660. {
  3661. /* If error during commit, no need to rollback, it's done. */
  3662. table->file->print_error(error, MYF(0));
  3663. DBUG_RETURN(TRUE);
  3664. }
  3665. if ((!is_empty) && (!written_bin_log) &&
  3666. (!thd->lex->no_write_to_binlog) &&
  3667. write_bin_log(thd, FALSE, thd->query(), thd->query_length()))
  3668. DBUG_RETURN(TRUE);
  3669. my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
  3670. (ulong) (copied + deleted),
  3671. (ulong) deleted,
  3672. (ulong) 0);
  3673. my_ok(thd, (ha_rows) (copied+deleted),0L, tmp_name);
  3674. DBUG_RETURN(FALSE);
  3675. }
  3676. /*
  3677. We need to check if engine used by all partitions can handle
  3678. partitioning natively.
  3679. SYNOPSIS
  3680. check_native_partitioned()
  3681. create_info Create info in CREATE TABLE
  3682. out:ret_val Return value
  3683. part_info Partition info
  3684. thd Thread object
  3685. RETURN VALUES
  3686. Value returned in bool ret_value
  3687. TRUE Native partitioning supported by engine
  3688. FALSE Need to use partition handler
  3689. Return value from function
  3690. TRUE Error
  3691. FALSE Success
  3692. */
  3693. static bool check_native_partitioned(HA_CREATE_INFO *create_info,bool *ret_val,
  3694. partition_info *part_info, THD *thd)
  3695. {
  3696. bool table_engine_set;
  3697. handlerton *engine_type= part_info->default_engine_type;
  3698. handlerton *old_engine_type= engine_type;
  3699. DBUG_ENTER("check_native_partitioned");
  3700. if (create_info->used_fields & HA_CREATE_USED_ENGINE)
  3701. {
  3702. table_engine_set= TRUE;
  3703. engine_type= create_info->db_type;
  3704. }
  3705. else
  3706. {
  3707. table_engine_set= FALSE;
  3708. if (thd->lex->sql_command != SQLCOM_CREATE_TABLE)
  3709. {
  3710. table_engine_set= TRUE;
  3711. DBUG_ASSERT(engine_type && engine_type != partition_hton);
  3712. }
  3713. }
  3714. DBUG_PRINT("info", ("engine_type = %s, table_engine_set = %u",
  3715. ha_resolve_storage_engine_name(engine_type),
  3716. table_engine_set));
  3717. if (part_info->check_engine_mix(engine_type, table_engine_set))
  3718. goto error;
  3719. /*
  3720. All engines are of the same type. Check if this engine supports
  3721. native partitioning.
  3722. */
  3723. if (!engine_type)
  3724. engine_type= old_engine_type;
  3725. DBUG_PRINT("info", ("engine_type = %s",
  3726. ha_resolve_storage_engine_name(engine_type)));
  3727. if (engine_type->partition_flags &&
  3728. (engine_type->partition_flags() & HA_CAN_PARTITION))
  3729. {
  3730. create_info->db_type= engine_type;
  3731. DBUG_PRINT("info", ("Changed to native partitioning"));
  3732. *ret_val= TRUE;
  3733. }
  3734. DBUG_RETURN(FALSE);
  3735. error:
  3736. /*
  3737. Mixed engines not yet supported but when supported it will need
  3738. the partition handler
  3739. */
  3740. my_error(ER_MIX_HANDLER_ERROR, MYF(0));
  3741. *ret_val= FALSE;
  3742. DBUG_RETURN(TRUE);
  3743. }
  3744. /*
  3745. Sets which partitions to be used in the command
  3746. */
  3747. uint set_part_state(Alter_info *alter_info, partition_info *tab_part_info,
  3748. enum partition_state part_state)
  3749. {
  3750. uint part_count= 0;
  3751. uint no_parts_found= 0;
  3752. List_iterator<partition_element> part_it(tab_part_info->partitions);
  3753. do
  3754. {
  3755. partition_element *part_elem= part_it++;
  3756. if ((alter_info->flags & ALTER_ALL_PARTITION) ||
  3757. (is_name_in_list(part_elem->partition_name,
  3758. alter_info->partition_names)))
  3759. {
  3760. /*
  3761. Mark the partition.
  3762. I.e mark the partition as a partition to be "changed" by
  3763. analyzing/optimizing/rebuilding/checking/repairing
  3764. */
  3765. no_parts_found++;
  3766. part_elem->part_state= part_state;
  3767. DBUG_PRINT("info", ("Setting part_state to %u for partition %s",
  3768. part_state, part_elem->partition_name));
  3769. }
  3770. } while (++part_count < tab_part_info->no_parts);
  3771. return no_parts_found;
  3772. }
  3773. /*
  3774. Prepare for ALTER TABLE of partition structure
  3775. SYNOPSIS
  3776. prep_alter_part_table()
  3777. thd Thread object
  3778. table Table object
  3779. inout:alter_info Alter information
  3780. inout:create_info Create info for CREATE TABLE
  3781. old_db_type Old engine type
  3782. out:partition_changed Boolean indicating whether partition changed
  3783. out:fast_alter_partition Boolean indicating whether fast partition
  3784. change is requested
  3785. RETURN VALUES
  3786. TRUE Error
  3787. FALSE Success
  3788. partition_changed
  3789. fast_alter_partition
  3790. DESCRIPTION
  3791. This method handles all preparations for ALTER TABLE for partitioned
  3792. tables
  3793. We need to handle both partition management command such as Add Partition
  3794. and others here as well as an ALTER TABLE that completely changes the
  3795. partitioning and yet others that don't change anything at all. We start
  3796. by checking the partition management variants and then check the general
  3797. change patterns.
  3798. */
  3799. uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
  3800. HA_CREATE_INFO *create_info,
  3801. handlerton *old_db_type,
  3802. bool *partition_changed,
  3803. uint *fast_alter_partition)
  3804. {
  3805. DBUG_ENTER("prep_alter_part_table");
  3806. /* Foreign keys on partitioned tables are not supported, waits for WL#148 */
  3807. if (table->part_info && (alter_info->flags & ALTER_FOREIGN_KEY))
  3808. {
  3809. my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0));
  3810. DBUG_RETURN(TRUE);
  3811. }
  3812. /*
  3813. We are going to manipulate the partition info on the table object
  3814. so we need to ensure that the data structure of the table object
  3815. is freed by setting version to 0. table->s->version= 0 forces a
  3816. flush of the table object in close_thread_tables().
  3817. */
  3818. if (table->part_info)
  3819. table->s->version= 0L;
  3820. thd->work_part_info= thd->lex->part_info;
  3821. if (thd->work_part_info &&
  3822. !(thd->work_part_info= thd->lex->part_info->get_clone()))
  3823. DBUG_RETURN(TRUE);
  3824. /* ALTER_ADMIN_PARTITION is handled in mysql_admin_table */
  3825. DBUG_ASSERT(!(alter_info->flags & ALTER_ADMIN_PARTITION));
  3826. if (alter_info->flags &
  3827. (ALTER_ADD_PARTITION | ALTER_DROP_PARTITION |
  3828. ALTER_COALESCE_PARTITION | ALTER_REORGANIZE_PARTITION |
  3829. ALTER_TABLE_REORG | ALTER_REBUILD_PARTITION))
  3830. {
  3831. partition_info *tab_part_info= table->part_info;
  3832. partition_info *alt_part_info= thd->work_part_info;
  3833. uint flags= 0;
  3834. if (!tab_part_info)
  3835. {
  3836. my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
  3837. DBUG_RETURN(TRUE);
  3838. }
  3839. if (alter_info->flags & ALTER_TABLE_REORG)
  3840. {
  3841. uint new_part_no, curr_part_no;
  3842. if (tab_part_info->part_type != HASH_PARTITION ||
  3843. tab_part_info->use_default_no_partitions)
  3844. {
  3845. my_error(ER_REORG_NO_PARAM_ERROR, MYF(0));
  3846. DBUG_RETURN(TRUE);
  3847. }
  3848. new_part_no= table->file->get_default_no_partitions(create_info);
  3849. curr_part_no= tab_part_info->no_parts;
  3850. if (new_part_no == curr_part_no)
  3851. {
  3852. /*
  3853. No change is needed, we will have the same number of partitions
  3854. after the change as before. Thus we can reply ok immediately
  3855. without any changes at all.
  3856. */
  3857. *fast_alter_partition= TRUE;
  3858. DBUG_RETURN(FALSE);
  3859. }
  3860. else if (new_part_no > curr_part_no)
  3861. {
  3862. /*
  3863. We will add more partitions, we use the ADD PARTITION without
  3864. setting the flag for no default number of partitions
  3865. */
  3866. alter_info->flags|= ALTER_ADD_PARTITION;
  3867. thd->work_part_info->no_parts= new_part_no - curr_part_no;
  3868. }
  3869. else
  3870. {
  3871. /*
  3872. We will remove hash partitions, we use the COALESCE PARTITION
  3873. without setting the flag for no default number of partitions
  3874. */
  3875. alter_info->flags|= ALTER_COALESCE_PARTITION;
  3876. alter_info->no_parts= curr_part_no - new_part_no;
  3877. }
  3878. }
  3879. if (!(flags= table->file->alter_table_flags(alter_info->flags)))
  3880. {
  3881. my_error(ER_PARTITION_FUNCTION_FAILURE, MYF(0));
  3882. DBUG_RETURN(1);
  3883. }
  3884. *fast_alter_partition=
  3885. ((flags & (HA_FAST_CHANGE_PARTITION | HA_PARTITION_ONE_PHASE)) != 0);
  3886. DBUG_PRINT("info", ("*fast_alter_partition: %d flags: 0x%x",
  3887. *fast_alter_partition, flags));
  3888. if (((alter_info->flags & ALTER_ADD_PARTITION) ||
  3889. (alter_info->flags & ALTER_REORGANIZE_PARTITION)) &&
  3890. (thd->work_part_info->part_type != tab_part_info->part_type) &&
  3891. (thd->work_part_info->part_type != NOT_A_PARTITION))
  3892. {
  3893. if (thd->work_part_info->part_type == RANGE_PARTITION)
  3894. {
  3895. my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
  3896. "RANGE", "LESS THAN");
  3897. }
  3898. else if (thd->work_part_info->part_type == LIST_PARTITION)
  3899. {
  3900. DBUG_ASSERT(thd->work_part_info->part_type == LIST_PARTITION);
  3901. my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
  3902. "LIST", "IN");
  3903. }
  3904. else if (tab_part_info->part_type == RANGE_PARTITION)
  3905. {
  3906. my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0),
  3907. "RANGE", "LESS THAN");
  3908. }
  3909. else
  3910. {
  3911. DBUG_ASSERT(tab_part_info->part_type == LIST_PARTITION);
  3912. my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0),
  3913. "LIST", "IN");
  3914. }
  3915. DBUG_RETURN(TRUE);
  3916. }
  3917. if (alter_info->flags & ALTER_ADD_PARTITION)
  3918. {
  3919. /*
  3920. We start by moving the new partitions to the list of temporary
  3921. partitions. We will then check that the new partitions fit in the
  3922. partitioning scheme as currently set-up.
  3923. Partitions are always added at the end in ADD PARTITION.
  3924. */
  3925. uint no_new_partitions= alt_part_info->no_parts;
  3926. uint no_orig_partitions= tab_part_info->no_parts;
  3927. uint check_total_partitions= no_new_partitions + no_orig_partitions;
  3928. uint new_total_partitions= check_total_partitions;
  3929. /*
  3930. We allow quite a lot of values to be supplied by defaults, however we
  3931. must know the number of new partitions in this case.
  3932. */
  3933. if (thd->lex->no_write_to_binlog &&
  3934. tab_part_info->part_type != HASH_PARTITION)
  3935. {
  3936. my_error(ER_NO_BINLOG_ERROR, MYF(0));
  3937. DBUG_RETURN(TRUE);
  3938. }
  3939. if (tab_part_info->defined_max_value)
  3940. {
  3941. my_error(ER_PARTITION_MAXVALUE_ERROR, MYF(0));
  3942. DBUG_RETURN(TRUE);
  3943. }
  3944. if (no_new_partitions == 0)
  3945. {
  3946. my_error(ER_ADD_PARTITION_NO_NEW_PARTITION, MYF(0));
  3947. DBUG_RETURN(TRUE);
  3948. }
  3949. if (tab_part_info->is_sub_partitioned())
  3950. {
  3951. if (alt_part_info->no_subparts == 0)
  3952. alt_part_info->no_subparts= tab_part_info->no_subparts;
  3953. else if (alt_part_info->no_subparts != tab_part_info->no_subparts)
  3954. {
  3955. my_error(ER_ADD_PARTITION_SUBPART_ERROR, MYF(0));
  3956. DBUG_RETURN(TRUE);
  3957. }
  3958. check_total_partitions= new_total_partitions*
  3959. alt_part_info->no_subparts;
  3960. }
  3961. if (check_total_partitions > MAX_PARTITIONS)
  3962. {
  3963. my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0));
  3964. DBUG_RETURN(TRUE);
  3965. }
  3966. alt_part_info->part_type= tab_part_info->part_type;
  3967. alt_part_info->subpart_type= tab_part_info->subpart_type;
  3968. if (alt_part_info->set_up_defaults_for_partitioning(table->file,
  3969. ULL(0),
  3970. tab_part_info->no_parts))
  3971. {
  3972. DBUG_RETURN(TRUE);
  3973. }
  3974. /*
  3975. Handling of on-line cases:
  3976. ADD PARTITION for RANGE/LIST PARTITIONING:
  3977. ------------------------------------------
  3978. For range and list partitions add partition is simply adding a
  3979. new empty partition to the table. If the handler support this we
  3980. will use the simple method of doing this. The figure below shows
  3981. an example of this and the states involved in making this change.
  3982. Existing partitions New added partitions
  3983. ------ ------ ------ ------ | ------ ------
  3984. | | | | | | | | | | | | |
  3985. | p0 | | p1 | | p2 | | p3 | | | p4 | | p5 |
  3986. ------ ------ ------ ------ | ------ ------
  3987. PART_NORMAL PART_NORMAL PART_NORMAL PART_NORMAL PART_TO_BE_ADDED*2
  3988. PART_NORMAL PART_NORMAL PART_NORMAL PART_NORMAL PART_IS_ADDED*2
  3989. The first line is the states before adding the new partitions and the
  3990. second line is after the new partitions are added. All the partitions are
  3991. in the partitions list, no partitions are placed in the temp_partitions
  3992. list.
  3993. ADD PARTITION for HASH PARTITIONING
  3994. -----------------------------------
  3995. This little figure tries to show the various partitions involved when
  3996. adding two new partitions to a linear hash based partitioned table with
  3997. four partitions to start with, which lists are used and the states they
  3998. pass through. Adding partitions to a normal hash based is similar except
  3999. that it is always all the existing partitions that are reorganised not
  4000. only a subset of them.
  4001. Existing partitions New added partitions
  4002. ------ ------ ------ ------ | ------ ------
  4003. | | | | | | | | | | | | |
  4004. | p0 | | p1 | | p2 | | p3 | | | p4 | | p5 |
  4005. ------ ------ ------ ------ | ------ ------
  4006. PART_CHANGED PART_CHANGED PART_NORMAL PART_NORMAL PART_TO_BE_ADDED
  4007. PART_IS_CHANGED*2 PART_NORMAL PART_NORMAL PART_IS_ADDED
  4008. PART_NORMAL PART_NORMAL PART_NORMAL PART_NORMAL PART_IS_ADDED
  4009. Reorganised existing partitions
  4010. ------ ------
  4011. | | | |
  4012. | p0'| | p1'|
  4013. ------ ------
  4014. p0 - p5 will be in the partitions list of partitions.
  4015. p0' and p1' will actually not exist as separate objects, there presence can
  4016. be deduced from the state of the partition and also the names of those
  4017. partitions can be deduced this way.
  4018. After adding the partitions and copying the partition data to p0', p1',
  4019. p4 and p5 from p0 and p1 the states change to adapt for the new situation
  4020. where p0 and p1 is dropped and replaced by p0' and p1' and the new p4 and
  4021. p5 are in the table again.
  4022. The first line above shows the states of the partitions before we start
  4023. adding and copying partitions, the second after completing the adding
  4024. and copying and finally the third line after also dropping the partitions
  4025. that are reorganised.
  4026. */
  4027. if (*fast_alter_partition &&
  4028. tab_part_info->part_type == HASH_PARTITION)
  4029. {
  4030. uint part_no= 0, start_part= 1, start_sec_part= 1;
  4031. uint end_part= 0, end_sec_part= 0;
  4032. uint upper_2n= tab_part_info->linear_hash_mask + 1;
  4033. uint lower_2n= upper_2n >> 1;
  4034. bool all_parts= TRUE;
  4035. if (tab_part_info->linear_hash_ind &&
  4036. no_new_partitions < upper_2n)
  4037. {
  4038. /*
  4039. An analysis of which parts needs reorganisation shows that it is
  4040. divided into two intervals. The first interval is those parts
  4041. that are reorganised up until upper_2n - 1. From upper_2n and
  4042. onwards it starts again from partition 0 and goes on until
  4043. it reaches p(upper_2n - 1). If the last new partition reaches
  4044. beyond upper_2n - 1 then the first interval will end with
  4045. p(lower_2n - 1) and start with p(no_orig_partitions - lower_2n).
  4046. If lower_2n partitions are added then p0 to p(lower_2n - 1) will
  4047. be reorganised which means that the two interval becomes one
  4048. interval at this point. Thus only when adding less than
  4049. lower_2n partitions and going beyond a total of upper_2n we
  4050. actually get two intervals.
  4051. To exemplify this assume we have 6 partitions to start with and
  4052. add 1, 2, 3, 5, 6, 7, 8, 9 partitions.
  4053. The first to add after p5 is p6 = 110 in bit numbers. Thus we
  4054. can see that 10 = p2 will be partition to reorganise if only one
  4055. partition.
  4056. If 2 partitions are added we reorganise [p2, p3]. Those two
  4057. cases are covered by the second if part below.
  4058. If 3 partitions are added we reorganise [p2, p3] U [p0,p0]. This
  4059. part is covered by the else part below.
  4060. If 5 partitions are added we get [p2,p3] U [p0, p2] = [p0, p3].
  4061. This is covered by the first if part where we need the max check
  4062. to here use lower_2n - 1.
  4063. If 7 partitions are added we get [p2,p3] U [p0, p4] = [p0, p4].
  4064. This is covered by the first if part but here we use the first
  4065. calculated end_part.
  4066. Finally with 9 new partitions we would also reorganise p6 if we
  4067. used the method below but we cannot reorganise more partitions
  4068. than what we had from the start and thus we simply set all_parts
  4069. to TRUE. In this case we don't get into this if-part at all.
  4070. */
  4071. all_parts= FALSE;
  4072. if (no_new_partitions >= lower_2n)
  4073. {
  4074. /*
  4075. In this case there is only one interval since the two intervals
  4076. overlap and this starts from zero to last_part_no - upper_2n
  4077. */
  4078. start_part= 0;
  4079. end_part= new_total_partitions - (upper_2n + 1);
  4080. end_part= max(lower_2n - 1, end_part);
  4081. }
  4082. else if (new_total_partitions <= upper_2n)
  4083. {
  4084. /*
  4085. Also in this case there is only one interval since we are not
  4086. going over a 2**n boundary
  4087. */
  4088. start_part= no_orig_partitions - lower_2n;
  4089. end_part= start_part + (no_new_partitions - 1);
  4090. }
  4091. else
  4092. {
  4093. /* We have two non-overlapping intervals since we are not
  4094. passing a 2**n border and we have not at least lower_2n
  4095. new parts that would ensure that the intervals become
  4096. overlapping.
  4097. */
  4098. start_part= no_orig_partitions - lower_2n;
  4099. end_part= upper_2n - 1;
  4100. start_sec_part= 0;
  4101. end_sec_part= new_total_partitions - (upper_2n + 1);
  4102. }
  4103. }
  4104. List_iterator<partition_element> tab_it(tab_part_info->partitions);
  4105. part_no= 0;
  4106. do
  4107. {
  4108. partition_element *p_elem= tab_it++;
  4109. if (all_parts ||
  4110. (part_no >= start_part && part_no <= end_part) ||
  4111. (part_no >= start_sec_part && part_no <= end_sec_part))
  4112. {
  4113. p_elem->part_state= PART_CHANGED;
  4114. }
  4115. } while (++part_no < no_orig_partitions);
  4116. }
  4117. /*
  4118. Need to concatenate the lists here to make it possible to check the
  4119. partition info for correctness using check_partition_info.
  4120. For on-line add partition we set the state of this partition to
  4121. PART_TO_BE_ADDED to ensure that it is known that it is not yet
  4122. usable (becomes usable when partition is created and the switch of
  4123. partition configuration is made.
  4124. */
  4125. {
  4126. List_iterator<partition_element> alt_it(alt_part_info->partitions);
  4127. uint part_count= 0;
  4128. do
  4129. {
  4130. partition_element *part_elem= alt_it++;
  4131. if (*fast_alter_partition)
  4132. part_elem->part_state= PART_TO_BE_ADDED;
  4133. if (tab_part_info->partitions.push_back(part_elem))
  4134. {
  4135. mem_alloc_error(1);
  4136. DBUG_RETURN(TRUE);
  4137. }
  4138. } while (++part_count < no_new_partitions);
  4139. tab_part_info->no_parts+= no_new_partitions;
  4140. }
  4141. /*
  4142. If we specify partitions explicitly we don't use defaults anymore.
  4143. Using ADD PARTITION also means that we don't have the default number
  4144. of partitions anymore. We use this code also for Table reorganisations
  4145. and here we don't set any default flags to FALSE.
  4146. */
  4147. if (!(alter_info->flags & ALTER_TABLE_REORG))
  4148. {
  4149. if (!alt_part_info->use_default_partitions)
  4150. {
  4151. DBUG_PRINT("info", ("part_info: 0x%lx", (long) tab_part_info));
  4152. tab_part_info->use_default_partitions= FALSE;
  4153. }
  4154. tab_part_info->use_default_no_partitions= FALSE;
  4155. tab_part_info->is_auto_partitioned= FALSE;
  4156. }
  4157. }
  4158. else if (alter_info->flags & ALTER_DROP_PARTITION)
  4159. {
  4160. /*
  4161. Drop a partition from a range partition and list partitioning is
  4162. always safe and can be made more or less immediate. It is necessary
  4163. however to ensure that the partition to be removed is safely removed
  4164. and that REPAIR TABLE can remove the partition if for some reason the
  4165. command to drop the partition failed in the middle.
  4166. */
  4167. uint part_count= 0;
  4168. uint no_parts_dropped= alter_info->partition_names.elements;
  4169. uint no_parts_found= 0;
  4170. List_iterator<partition_element> part_it(tab_part_info->partitions);
  4171. tab_part_info->is_auto_partitioned= FALSE;
  4172. if (!(tab_part_info->part_type == RANGE_PARTITION ||
  4173. tab_part_info->part_type == LIST_PARTITION))
  4174. {
  4175. my_error(ER_ONLY_ON_RANGE_LIST_PARTITION, MYF(0), "DROP");
  4176. DBUG_RETURN(TRUE);
  4177. }
  4178. if (no_parts_dropped >= tab_part_info->no_parts)
  4179. {
  4180. my_error(ER_DROP_LAST_PARTITION, MYF(0));
  4181. DBUG_RETURN(TRUE);
  4182. }
  4183. do
  4184. {
  4185. partition_element *part_elem= part_it++;
  4186. if (is_name_in_list(part_elem->partition_name,
  4187. alter_info->partition_names))
  4188. {
  4189. /*
  4190. Set state to indicate that the partition is to be dropped.
  4191. */
  4192. no_parts_found++;
  4193. part_elem->part_state= PART_TO_BE_DROPPED;
  4194. }
  4195. } while (++part_count < tab_part_info->no_parts);
  4196. if (no_parts_found != no_parts_dropped)
  4197. {
  4198. my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "DROP");
  4199. DBUG_RETURN(TRUE);
  4200. }
  4201. if (table->file->is_fk_defined_on_table_or_index(MAX_KEY))
  4202. {
  4203. my_error(ER_ROW_IS_REFERENCED, MYF(0));
  4204. DBUG_RETURN(TRUE);
  4205. }
  4206. tab_part_info->no_parts-= no_parts_dropped;
  4207. }
  4208. else if (alter_info->flags & ALTER_REBUILD_PARTITION)
  4209. {
  4210. uint no_parts_found;
  4211. uint no_parts_opt= alter_info->partition_names.elements;
  4212. no_parts_found= set_part_state(alter_info, tab_part_info, PART_CHANGED);
  4213. if (no_parts_found != no_parts_opt &&
  4214. (!(alter_info->flags & ALTER_ALL_PARTITION)))
  4215. {
  4216. my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "REBUILD");
  4217. DBUG_RETURN(TRUE);
  4218. }
  4219. if (!(*fast_alter_partition))
  4220. {
  4221. table->file->print_error(HA_ERR_WRONG_COMMAND, MYF(0));
  4222. DBUG_RETURN(TRUE);
  4223. }
  4224. }
  4225. else if (alter_info->flags & ALTER_COALESCE_PARTITION)
  4226. {
  4227. uint no_parts_coalesced= alter_info->no_parts;
  4228. uint no_parts_remain= tab_part_info->no_parts - no_parts_coalesced;
  4229. List_iterator<partition_element> part_it(tab_part_info->partitions);
  4230. if (tab_part_info->part_type != HASH_PARTITION)
  4231. {
  4232. my_error(ER_COALESCE_ONLY_ON_HASH_PARTITION, MYF(0));
  4233. DBUG_RETURN(TRUE);
  4234. }
  4235. if (no_parts_coalesced == 0)
  4236. {
  4237. my_error(ER_COALESCE_PARTITION_NO_PARTITION, MYF(0));
  4238. DBUG_RETURN(TRUE);
  4239. }
  4240. if (no_parts_coalesced >= tab_part_info->no_parts)
  4241. {
  4242. my_error(ER_DROP_LAST_PARTITION, MYF(0));
  4243. DBUG_RETURN(TRUE);
  4244. }
  4245. /*
  4246. Online handling:
  4247. COALESCE PARTITION:
  4248. -------------------
  4249. The figure below shows the manner in which partitions are handled when
  4250. performing an on-line coalesce partition and which states they go through
  4251. at start, after adding and copying partitions and finally after dropping
  4252. the partitions to drop. The figure shows an example using four partitions
  4253. to start with, using linear hash and coalescing one partition (always the
  4254. last partition).
  4255. Using linear hash then all remaining partitions will have a new reorganised
  4256. part.
  4257. Existing partitions Coalesced partition
  4258. ------ ------ ------ | ------
  4259. | | | | | | | | |
  4260. | p0 | | p1 | | p2 | | | p3 |
  4261. ------ ------ ------ | ------
  4262. PART_NORMAL PART_CHANGED PART_NORMAL PART_REORGED_DROPPED
  4263. PART_NORMAL PART_IS_CHANGED PART_NORMAL PART_TO_BE_DROPPED
  4264. PART_NORMAL PART_NORMAL PART_NORMAL PART_IS_DROPPED
  4265. Reorganised existing partitions
  4266. ------
  4267. | |
  4268. | p1'|
  4269. ------
  4270. p0 - p3 is in the partitions list.
  4271. The p1' partition will actually not be in any list it is deduced from the
  4272. state of p1.
  4273. */
  4274. {
  4275. uint part_count= 0, start_part= 1, start_sec_part= 1;
  4276. uint end_part= 0, end_sec_part= 0;
  4277. bool all_parts= TRUE;
  4278. if (*fast_alter_partition &&
  4279. tab_part_info->linear_hash_ind)
  4280. {
  4281. uint upper_2n= tab_part_info->linear_hash_mask + 1;
  4282. uint lower_2n= upper_2n >> 1;
  4283. all_parts= FALSE;
  4284. if (no_parts_coalesced >= lower_2n)
  4285. {
  4286. all_parts= TRUE;
  4287. }
  4288. else if (no_parts_remain >= lower_2n)
  4289. {
  4290. end_part= tab_part_info->no_parts - (lower_2n + 1);
  4291. start_part= no_parts_remain - lower_2n;
  4292. }
  4293. else
  4294. {
  4295. start_part= 0;
  4296. end_part= tab_part_info->no_parts - (lower_2n + 1);
  4297. end_sec_part= (lower_2n >> 1) - 1;
  4298. start_sec_part= end_sec_part - (lower_2n - (no_parts_remain + 1));
  4299. }
  4300. }
  4301. do
  4302. {
  4303. partition_element *p_elem= part_it++;
  4304. if (*fast_alter_partition &&
  4305. (all_parts ||
  4306. (part_count >= start_part && part_count <= end_part) ||
  4307. (part_count >= start_sec_part && part_count <= end_sec_part)))
  4308. p_elem->part_state= PART_CHANGED;
  4309. if (++part_count > no_parts_remain)
  4310. {
  4311. if (*fast_alter_partition)
  4312. p_elem->part_state= PART_REORGED_DROPPED;
  4313. else
  4314. part_it.remove();
  4315. }
  4316. } while (part_count < tab_part_info->no_parts);
  4317. tab_part_info->no_parts= no_parts_remain;
  4318. }
  4319. if (!(alter_info->flags & ALTER_TABLE_REORG))
  4320. {
  4321. tab_part_info->use_default_no_partitions= FALSE;
  4322. tab_part_info->is_auto_partitioned= FALSE;
  4323. }
  4324. }
  4325. else if (alter_info->flags & ALTER_REORGANIZE_PARTITION)
  4326. {
  4327. /*
  4328. Reorganise partitions takes a number of partitions that are next
  4329. to each other (at least for RANGE PARTITIONS) and then uses those
  4330. to create a set of new partitions. So data is copied from those
  4331. partitions into the new set of partitions. Those new partitions
  4332. can have more values in the LIST value specifications or less both
  4333. are allowed. The ranges can be different but since they are
  4334. changing a set of consecutive partitions they must cover the same
  4335. range as those changed from.
  4336. This command can be used on RANGE and LIST partitions.
  4337. */
  4338. uint no_parts_reorged= alter_info->partition_names.elements;
  4339. uint no_parts_new= thd->work_part_info->partitions.elements;
  4340. partition_info *alt_part_info= thd->work_part_info;
  4341. uint check_total_partitions;
  4342. tab_part_info->is_auto_partitioned= FALSE;
  4343. if (no_parts_reorged > tab_part_info->no_parts)
  4344. {
  4345. my_error(ER_REORG_PARTITION_NOT_EXIST, MYF(0));
  4346. DBUG_RETURN(TRUE);
  4347. }
  4348. if (!(tab_part_info->part_type == RANGE_PARTITION ||
  4349. tab_part_info->part_type == LIST_PARTITION) &&
  4350. (no_parts_new != no_parts_reorged))
  4351. {
  4352. my_error(ER_REORG_HASH_ONLY_ON_SAME_NO, MYF(0));
  4353. DBUG_RETURN(TRUE);
  4354. }
  4355. if (tab_part_info->is_sub_partitioned() &&
  4356. alt_part_info->no_subparts &&
  4357. alt_part_info->no_subparts != tab_part_info->no_subparts)
  4358. {
  4359. my_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR, MYF(0));
  4360. DBUG_RETURN(TRUE);
  4361. }
  4362. check_total_partitions= tab_part_info->no_parts + no_parts_new;
  4363. check_total_partitions-= no_parts_reorged;
  4364. if (check_total_partitions > MAX_PARTITIONS)
  4365. {
  4366. my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0));
  4367. DBUG_RETURN(TRUE);
  4368. }
  4369. alt_part_info->part_type= tab_part_info->part_type;
  4370. alt_part_info->subpart_type= tab_part_info->subpart_type;
  4371. alt_part_info->no_subparts= tab_part_info->no_subparts;
  4372. DBUG_ASSERT(!alt_part_info->use_default_partitions);
  4373. if (alt_part_info->set_up_defaults_for_partitioning(table->file,
  4374. ULL(0),
  4375. 0))
  4376. {
  4377. DBUG_RETURN(TRUE);
  4378. }
  4379. /*
  4380. Online handling:
  4381. REORGANIZE PARTITION:
  4382. ---------------------
  4383. The figure exemplifies the handling of partitions, their state changes and
  4384. how they are organised. It exemplifies four partitions where two of the
  4385. partitions are reorganised (p1 and p2) into two new partitions (p4 and p5).
  4386. The reason of this change could be to change range limits, change list
  4387. values or for hash partitions simply reorganise the partition which could
  4388. also involve moving them to new disks or new node groups (MySQL Cluster).
  4389. Existing partitions
  4390. ------ ------ ------ ------
  4391. | | | | | | | |
  4392. | p0 | | p1 | | p2 | | p3 |
  4393. ------ ------ ------ ------
  4394. PART_NORMAL PART_TO_BE_REORGED PART_NORMAL
  4395. PART_NORMAL PART_TO_BE_DROPPED PART_NORMAL
  4396. PART_NORMAL PART_IS_DROPPED PART_NORMAL
  4397. Reorganised new partitions (replacing p1 and p2)
  4398. ------ ------
  4399. | | | |
  4400. | p4 | | p5 |
  4401. ------ ------
  4402. PART_TO_BE_ADDED
  4403. PART_IS_ADDED
  4404. PART_IS_ADDED
  4405. All unchanged partitions and the new partitions are in the partitions list
  4406. in the order they will have when the change is completed. The reorganised
  4407. partitions are placed in the temp_partitions list. PART_IS_ADDED is only a
  4408. temporary state not written in the frm file. It is used to ensure we write
  4409. the generated partition syntax in a correct manner.
  4410. */
  4411. {
  4412. List_iterator<partition_element> tab_it(tab_part_info->partitions);
  4413. uint part_count= 0;
  4414. bool found_first= FALSE;
  4415. bool found_last= FALSE;
  4416. bool is_last_partition_reorged;
  4417. uint drop_count= 0;
  4418. longlong tab_max_range= 0, alt_max_range= 0;
  4419. do
  4420. {
  4421. partition_element *part_elem= tab_it++;
  4422. is_last_partition_reorged= FALSE;
  4423. if (is_name_in_list(part_elem->partition_name,
  4424. alter_info->partition_names))
  4425. {
  4426. is_last_partition_reorged= TRUE;
  4427. drop_count++;
  4428. tab_max_range= part_elem->range_value;
  4429. if (*fast_alter_partition &&
  4430. tab_part_info->temp_partitions.push_back(part_elem))
  4431. {
  4432. mem_alloc_error(1);
  4433. DBUG_RETURN(TRUE);
  4434. }
  4435. if (*fast_alter_partition)
  4436. part_elem->part_state= PART_TO_BE_REORGED;
  4437. if (!found_first)
  4438. {
  4439. uint alt_part_count= 0;
  4440. found_first= TRUE;
  4441. List_iterator<partition_element>
  4442. alt_it(alt_part_info->partitions);
  4443. do
  4444. {
  4445. partition_element *alt_part_elem= alt_it++;
  4446. alt_max_range= alt_part_elem->range_value;
  4447. if (*fast_alter_partition)
  4448. alt_part_elem->part_state= PART_TO_BE_ADDED;
  4449. if (alt_part_count == 0)
  4450. tab_it.replace(alt_part_elem);
  4451. else
  4452. tab_it.after(alt_part_elem);
  4453. } while (++alt_part_count < no_parts_new);
  4454. }
  4455. else if (found_last)
  4456. {
  4457. my_error(ER_CONSECUTIVE_REORG_PARTITIONS, MYF(0));
  4458. DBUG_RETURN(TRUE);
  4459. }
  4460. else
  4461. tab_it.remove();
  4462. }
  4463. else
  4464. {
  4465. if (found_first)
  4466. found_last= TRUE;
  4467. }
  4468. } while (++part_count < tab_part_info->no_parts);
  4469. if (drop_count != no_parts_reorged)
  4470. {
  4471. my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "REORGANIZE");
  4472. DBUG_RETURN(TRUE);
  4473. }
  4474. if (tab_part_info->part_type == RANGE_PARTITION &&
  4475. ((is_last_partition_reorged &&
  4476. alt_max_range < tab_max_range) ||
  4477. (!is_last_partition_reorged &&
  4478. alt_max_range != tab_max_range)))
  4479. {
  4480. /*
  4481. For range partitioning the total resulting range before and
  4482. after the change must be the same except in one case. This is
  4483. when the last partition is reorganised, in this case it is
  4484. acceptable to increase the total range.
  4485. The reason is that it is not allowed to have "holes" in the
  4486. middle of the ranges and thus we should not allow to reorganise
  4487. to create "holes". Also we should not allow using REORGANIZE
  4488. to drop data.
  4489. */
  4490. my_error(ER_REORG_OUTSIDE_RANGE, MYF(0));
  4491. DBUG_RETURN(TRUE);
  4492. }
  4493. tab_part_info->no_parts= check_total_partitions;
  4494. }
  4495. }
  4496. else
  4497. {
  4498. DBUG_ASSERT(FALSE);
  4499. }
  4500. *partition_changed= TRUE;
  4501. thd->work_part_info= tab_part_info;
  4502. if (alter_info->flags & ALTER_ADD_PARTITION ||
  4503. alter_info->flags & ALTER_REORGANIZE_PARTITION)
  4504. {
  4505. if (tab_part_info->use_default_subpartitions &&
  4506. !alt_part_info->use_default_subpartitions)
  4507. {
  4508. tab_part_info->use_default_subpartitions= FALSE;
  4509. tab_part_info->use_default_no_subpartitions= FALSE;
  4510. }
  4511. if (tab_part_info->check_partition_info(thd, (handlerton**)NULL,
  4512. table->file, ULL(0), FALSE))
  4513. {
  4514. DBUG_RETURN(TRUE);
  4515. }
  4516. }
  4517. }
  4518. else
  4519. {
  4520. /*
  4521. When thd->lex->part_info has a reference to a partition_info the
  4522. ALTER TABLE contained a definition of a partitioning.
  4523. Case I:
  4524. If there was a partition before and there is a new one defined.
  4525. We use the new partitioning. The new partitioning is already
  4526. defined in the correct variable so no work is needed to
  4527. accomplish this.
  4528. We do however need to update partition_changed to ensure that not
  4529. only the frm file is changed in the ALTER TABLE command.
  4530. Case IIa:
  4531. There was a partitioning before and there is no new one defined.
  4532. Also the user has not specified to remove partitioning explicitly.
  4533. We use the old partitioning also for the new table. We do this
  4534. by assigning the partition_info from the table loaded in
  4535. open_table to the partition_info struct used by mysql_create_table
  4536. later in this method.
  4537. Case IIb:
  4538. There was a partitioning before and there is no new one defined.
  4539. The user has specified explicitly to remove partitioning
  4540. Since the user has specified explicitly to remove partitioning
  4541. we override the old partitioning info and create a new table using
  4542. the specified engine.
  4543. In this case the partition also is changed.
  4544. Case III:
  4545. There was no partitioning before altering the table, there is
  4546. partitioning defined in the altered table. Use the new partitioning.
  4547. No work needed since the partitioning info is already in the
  4548. correct variable.
  4549. In this case we discover one case where the new partitioning is using
  4550. the same partition function as the default (PARTITION BY KEY or
  4551. PARTITION BY LINEAR KEY with the list of fields equal to the primary
  4552. key fields OR PARTITION BY [LINEAR] KEY() for tables without primary
  4553. key)
  4554. Also here partition has changed and thus a new table must be
  4555. created.
  4556. Case IV:
  4557. There was no partitioning before and no partitioning defined.
  4558. Obviously no work needed.
  4559. */
  4560. if (table->part_info)
  4561. {
  4562. if (alter_info->flags & ALTER_REMOVE_PARTITIONING)
  4563. {
  4564. DBUG_PRINT("info", ("Remove partitioning"));
  4565. if (!(create_info->used_fields & HA_CREATE_USED_ENGINE))
  4566. {
  4567. DBUG_PRINT("info", ("No explicit engine used"));
  4568. create_info->db_type= table->part_info->default_engine_type;
  4569. }
  4570. DBUG_PRINT("info", ("New engine type: %s",
  4571. ha_resolve_storage_engine_name(create_info->db_type)));
  4572. thd->work_part_info= NULL;
  4573. *partition_changed= TRUE;
  4574. }
  4575. else if (!thd->work_part_info)
  4576. {
  4577. /*
  4578. Retain partitioning but possibly with a new storage engine
  4579. beneath.
  4580. */
  4581. thd->work_part_info= table->part_info;
  4582. if (create_info->used_fields & HA_CREATE_USED_ENGINE &&
  4583. create_info->db_type != table->part_info->default_engine_type)
  4584. {
  4585. /*
  4586. Make sure change of engine happens to all partitions.
  4587. */
  4588. DBUG_PRINT("info", ("partition changed"));
  4589. if (table->part_info->is_auto_partitioned)
  4590. {
  4591. /*
  4592. If the user originally didn't specify partitioning to be
  4593. used we can remove it now.
  4594. */
  4595. thd->work_part_info= NULL;
  4596. }
  4597. else
  4598. {
  4599. /*
  4600. Ensure that all partitions have the proper engine set-up
  4601. */
  4602. set_engine_all_partitions(thd->work_part_info,
  4603. create_info->db_type);
  4604. }
  4605. *partition_changed= TRUE;
  4606. }
  4607. }
  4608. }
  4609. if (thd->work_part_info)
  4610. {
  4611. partition_info *part_info= thd->work_part_info;
  4612. bool is_native_partitioned= FALSE;
  4613. /*
  4614. Need to cater for engine types that can handle partition without
  4615. using the partition handler.
  4616. */
  4617. if (thd->work_part_info != table->part_info)
  4618. {
  4619. DBUG_PRINT("info", ("partition changed"));
  4620. *partition_changed= TRUE;
  4621. }
  4622. /*
  4623. Set up partition default_engine_type either from the create_info
  4624. or from the previus table
  4625. */
  4626. if (create_info->used_fields & HA_CREATE_USED_ENGINE)
  4627. part_info->default_engine_type= create_info->db_type;
  4628. else
  4629. {
  4630. if (table->part_info)
  4631. part_info->default_engine_type= table->part_info->default_engine_type;
  4632. else
  4633. part_info->default_engine_type= create_info->db_type;
  4634. }
  4635. DBUG_ASSERT(part_info->default_engine_type &&
  4636. part_info->default_engine_type != partition_hton);
  4637. if (check_native_partitioned(create_info, &is_native_partitioned,
  4638. part_info, thd))
  4639. {
  4640. DBUG_RETURN(TRUE);
  4641. }
  4642. if (!is_native_partitioned)
  4643. {
  4644. DBUG_ASSERT(create_info->db_type);
  4645. create_info->db_type= partition_hton;
  4646. }
  4647. }
  4648. }
  4649. DBUG_RETURN(FALSE);
  4650. }
  4651. /*
  4652. Change partitions, used to implement ALTER TABLE ADD/REORGANIZE/COALESCE
  4653. partitions. This method is used to implement both single-phase and multi-
  4654. phase implementations of ADD/REORGANIZE/COALESCE partitions.
  4655. SYNOPSIS
  4656. mysql_change_partitions()
  4657. lpt Struct containing parameters
  4658. RETURN VALUES
  4659. TRUE Failure
  4660. FALSE Success
  4661. DESCRIPTION
  4662. Request handler to add partitions as set in states of the partition
  4663. Elements of the lpt parameters used:
  4664. create_info Create information used to create partitions
  4665. db Database name
  4666. table_name Table name
  4667. copied Output parameter where number of copied
  4668. records are added
  4669. deleted Output parameter where number of deleted
  4670. records are added
  4671. */
  4672. static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
  4673. {
  4674. char path[FN_REFLEN+1];
  4675. int error;
  4676. handler *file= lpt->table->file;
  4677. DBUG_ENTER("mysql_change_partitions");
  4678. build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0);
  4679. if ((error= file->ha_change_partitions(lpt->create_info, path, &lpt->copied,
  4680. &lpt->deleted, lpt->pack_frm_data,
  4681. lpt->pack_frm_len)))
  4682. {
  4683. if (error != ER_OUTOFMEMORY)
  4684. file->print_error(error, MYF(0));
  4685. else
  4686. lpt->thd->fatal_error();
  4687. DBUG_RETURN(TRUE);
  4688. }
  4689. DBUG_RETURN(FALSE);
  4690. }
  4691. /*
  4692. Rename partitions in an ALTER TABLE of partitions
  4693. SYNOPSIS
  4694. mysql_rename_partitions()
  4695. lpt Struct containing parameters
  4696. RETURN VALUES
  4697. TRUE Failure
  4698. FALSE Success
  4699. DESCRIPTION
  4700. Request handler to rename partitions as set in states of the partition
  4701. Parameters used:
  4702. db Database name
  4703. table_name Table name
  4704. */
  4705. static bool mysql_rename_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
  4706. {
  4707. char path[FN_REFLEN+1];
  4708. int error;
  4709. DBUG_ENTER("mysql_rename_partitions");
  4710. build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0);
  4711. if ((error= lpt->table->file->ha_rename_partitions(path)))
  4712. {
  4713. if (error != 1)
  4714. lpt->table->file->print_error(error, MYF(0));
  4715. DBUG_RETURN(TRUE);
  4716. }
  4717. DBUG_RETURN(FALSE);
  4718. }
  4719. /*
  4720. Drop partitions in an ALTER TABLE of partitions
  4721. SYNOPSIS
  4722. mysql_drop_partitions()
  4723. lpt Struct containing parameters
  4724. RETURN VALUES
  4725. TRUE Failure
  4726. FALSE Success
  4727. DESCRIPTION
  4728. Drop the partitions marked with PART_TO_BE_DROPPED state and remove
  4729. those partitions from the list.
  4730. Parameters used:
  4731. table Table object
  4732. db Database name
  4733. table_name Table name
  4734. */
  4735. static bool mysql_drop_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
  4736. {
  4737. char path[FN_REFLEN+1];
  4738. partition_info *part_info= lpt->table->part_info;
  4739. List_iterator<partition_element> part_it(part_info->partitions);
  4740. uint i= 0;
  4741. uint remove_count= 0;
  4742. int error;
  4743. DBUG_ENTER("mysql_drop_partitions");
  4744. build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0);
  4745. if ((error= lpt->table->file->ha_drop_partitions(path)))
  4746. {
  4747. lpt->table->file->print_error(error, MYF(0));
  4748. DBUG_RETURN(TRUE);
  4749. }
  4750. do
  4751. {
  4752. partition_element *part_elem= part_it++;
  4753. if (part_elem->part_state == PART_IS_DROPPED)
  4754. {
  4755. part_it.remove();
  4756. remove_count++;
  4757. }
  4758. } while (++i < part_info->no_parts);
  4759. part_info->no_parts-= remove_count;
  4760. DBUG_RETURN(FALSE);
  4761. }
  4762. /*
  4763. Insert log entry into list
  4764. SYNOPSIS
  4765. insert_part_info_log_entry_list()
  4766. log_entry
  4767. RETURN VALUES
  4768. NONE
  4769. */
  4770. static void insert_part_info_log_entry_list(partition_info *part_info,
  4771. DDL_LOG_MEMORY_ENTRY *log_entry)
  4772. {
  4773. log_entry->next_active_log_entry= part_info->first_log_entry;
  4774. part_info->first_log_entry= log_entry;
  4775. }
  4776. /*
  4777. Release all log entries for this partition info struct
  4778. SYNOPSIS
  4779. release_part_info_log_entries()
  4780. first_log_entry First log entry in list to release
  4781. RETURN VALUES
  4782. NONE
  4783. */
  4784. static void release_part_info_log_entries(DDL_LOG_MEMORY_ENTRY *log_entry)
  4785. {
  4786. DBUG_ENTER("release_part_info_log_entries");
  4787. while (log_entry)
  4788. {
  4789. release_ddl_log_memory_entry(log_entry);
  4790. log_entry= log_entry->next_active_log_entry;
  4791. }
  4792. DBUG_VOID_RETURN;
  4793. }
  4794. /*
  4795. Log an delete/rename frm file
  4796. SYNOPSIS
  4797. write_log_replace_delete_frm()
  4798. lpt Struct for parameters
  4799. next_entry Next reference to use in log record
  4800. from_path Name to rename from
  4801. to_path Name to rename to
  4802. replace_flag TRUE if replace, else delete
  4803. RETURN VALUES
  4804. TRUE Error
  4805. FALSE Success
  4806. DESCRIPTION
  4807. Support routine that writes a replace or delete of an frm file into the
  4808. ddl log. It also inserts an entry that keeps track of used space into
  4809. the partition info object
  4810. */
  4811. static bool write_log_replace_delete_frm(ALTER_PARTITION_PARAM_TYPE *lpt,
  4812. uint next_entry,
  4813. const char *from_path,
  4814. const char *to_path,
  4815. bool replace_flag)
  4816. {
  4817. DDL_LOG_ENTRY ddl_log_entry;
  4818. DDL_LOG_MEMORY_ENTRY *log_entry;
  4819. DBUG_ENTER("write_log_replace_delete_frm");
  4820. if (replace_flag)
  4821. ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION;
  4822. else
  4823. ddl_log_entry.action_type= DDL_LOG_DELETE_ACTION;
  4824. ddl_log_entry.next_entry= next_entry;
  4825. ddl_log_entry.handler_name= reg_ext;
  4826. ddl_log_entry.name= to_path;
  4827. if (replace_flag)
  4828. ddl_log_entry.from_name= from_path;
  4829. if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
  4830. {
  4831. DBUG_RETURN(TRUE);
  4832. }
  4833. insert_part_info_log_entry_list(lpt->part_info, log_entry);
  4834. DBUG_RETURN(FALSE);
  4835. }
  4836. /*
  4837. Log final partition changes in change partition
  4838. SYNOPSIS
  4839. write_log_changed_partitions()
  4840. lpt Struct containing parameters
  4841. RETURN VALUES
  4842. TRUE Error
  4843. FALSE Success
  4844. DESCRIPTION
  4845. This code is used to perform safe ADD PARTITION for HASH partitions
  4846. and COALESCE for HASH partitions and REORGANIZE for any type of
  4847. partitions.
  4848. We prepare entries for all partitions except the reorganised partitions
  4849. in REORGANIZE partition, those are handled by
  4850. write_log_dropped_partitions. For those partitions that are replaced
  4851. special care is needed to ensure that this is performed correctly and
  4852. this requires a two-phased approach with this log as a helper for this.
  4853. This code is closely intertwined with the code in rename_partitions in
  4854. the partition handler.
  4855. */
  4856. static bool write_log_changed_partitions(ALTER_PARTITION_PARAM_TYPE *lpt,
  4857. uint *next_entry, const char *path)
  4858. {
  4859. DDL_LOG_ENTRY ddl_log_entry;
  4860. partition_info *part_info= lpt->part_info;
  4861. DDL_LOG_MEMORY_ENTRY *log_entry;
  4862. char tmp_path[FN_REFLEN];
  4863. char normal_path[FN_REFLEN];
  4864. List_iterator<partition_element> part_it(part_info->partitions);
  4865. uint temp_partitions= part_info->temp_partitions.elements;
  4866. uint no_elements= part_info->partitions.elements;
  4867. uint i= 0;
  4868. DBUG_ENTER("write_log_changed_partitions");
  4869. do
  4870. {
  4871. partition_element *part_elem= part_it++;
  4872. if (part_elem->part_state == PART_IS_CHANGED ||
  4873. (part_elem->part_state == PART_IS_ADDED && temp_partitions))
  4874. {
  4875. if (part_info->is_sub_partitioned())
  4876. {
  4877. List_iterator<partition_element> sub_it(part_elem->subpartitions);
  4878. uint no_subparts= part_info->no_subparts;
  4879. uint j= 0;
  4880. do
  4881. {
  4882. partition_element *sub_elem= sub_it++;
  4883. ddl_log_entry.next_entry= *next_entry;
  4884. ddl_log_entry.handler_name=
  4885. ha_resolve_storage_engine_name(sub_elem->engine_type);
  4886. create_subpartition_name(tmp_path, path,
  4887. part_elem->partition_name,
  4888. sub_elem->partition_name,
  4889. TEMP_PART_NAME);
  4890. create_subpartition_name(normal_path, path,
  4891. part_elem->partition_name,
  4892. sub_elem->partition_name,
  4893. NORMAL_PART_NAME);
  4894. ddl_log_entry.name= normal_path;
  4895. ddl_log_entry.from_name= tmp_path;
  4896. if (part_elem->part_state == PART_IS_CHANGED)
  4897. ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION;
  4898. else
  4899. ddl_log_entry.action_type= DDL_LOG_RENAME_ACTION;
  4900. if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
  4901. {
  4902. DBUG_RETURN(TRUE);
  4903. }
  4904. *next_entry= log_entry->entry_pos;
  4905. sub_elem->log_entry= log_entry;
  4906. insert_part_info_log_entry_list(part_info, log_entry);
  4907. } while (++j < no_subparts);
  4908. }
  4909. else
  4910. {
  4911. ddl_log_entry.next_entry= *next_entry;
  4912. ddl_log_entry.handler_name=
  4913. ha_resolve_storage_engine_name(part_elem->engine_type);
  4914. create_partition_name(tmp_path, path,
  4915. part_elem->partition_name,
  4916. TEMP_PART_NAME, TRUE);
  4917. create_partition_name(normal_path, path,
  4918. part_elem->partition_name,
  4919. NORMAL_PART_NAME, TRUE);
  4920. ddl_log_entry.name= normal_path;
  4921. ddl_log_entry.from_name= tmp_path;
  4922. if (part_elem->part_state == PART_IS_CHANGED)
  4923. ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION;
  4924. else
  4925. ddl_log_entry.action_type= DDL_LOG_RENAME_ACTION;
  4926. if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
  4927. {
  4928. DBUG_RETURN(TRUE);
  4929. }
  4930. *next_entry= log_entry->entry_pos;
  4931. part_elem->log_entry= log_entry;
  4932. insert_part_info_log_entry_list(part_info, log_entry);
  4933. }
  4934. }
  4935. } while (++i < no_elements);
  4936. DBUG_RETURN(FALSE);
  4937. }
  4938. /*
  4939. Log dropped partitions
  4940. SYNOPSIS
  4941. write_log_dropped_partitions()
  4942. lpt Struct containing parameters
  4943. RETURN VALUES
  4944. TRUE Error
  4945. FALSE Success
  4946. */
  4947. static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt,
  4948. uint *next_entry,
  4949. const char *path,
  4950. bool temp_list)
  4951. {
  4952. DDL_LOG_ENTRY ddl_log_entry;
  4953. partition_info *part_info= lpt->part_info;
  4954. DDL_LOG_MEMORY_ENTRY *log_entry;
  4955. char tmp_path[FN_LEN];
  4956. List_iterator<partition_element> part_it(part_info->partitions);
  4957. List_iterator<partition_element> temp_it(part_info->temp_partitions);
  4958. uint no_temp_partitions= part_info->temp_partitions.elements;
  4959. uint no_elements= part_info->partitions.elements;
  4960. DBUG_ENTER("write_log_dropped_partitions");
  4961. ddl_log_entry.action_type= DDL_LOG_DELETE_ACTION;
  4962. if (temp_list)
  4963. no_elements= no_temp_partitions;
  4964. while (no_elements--)
  4965. {
  4966. partition_element *part_elem;
  4967. if (temp_list)
  4968. part_elem= temp_it++;
  4969. else
  4970. part_elem= part_it++;
  4971. if (part_elem->part_state == PART_TO_BE_DROPPED ||
  4972. part_elem->part_state == PART_TO_BE_ADDED ||
  4973. part_elem->part_state == PART_CHANGED)
  4974. {
  4975. uint name_variant;
  4976. if (part_elem->part_state == PART_CHANGED ||
  4977. (part_elem->part_state == PART_TO_BE_ADDED &&
  4978. no_temp_partitions))
  4979. name_variant= TEMP_PART_NAME;
  4980. else
  4981. name_variant= NORMAL_PART_NAME;
  4982. if (part_info->is_sub_partitioned())
  4983. {
  4984. List_iterator<partition_element> sub_it(part_elem->subpartitions);
  4985. uint no_subparts= part_info->no_subparts;
  4986. uint j= 0;
  4987. do
  4988. {
  4989. partition_element *sub_elem= sub_it++;
  4990. ddl_log_entry.next_entry= *next_entry;
  4991. ddl_log_entry.handler_name=
  4992. ha_resolve_storage_engine_name(sub_elem->engine_type);
  4993. create_subpartition_name(tmp_path, path,
  4994. part_elem->partition_name,
  4995. sub_elem->partition_name,
  4996. name_variant);
  4997. ddl_log_entry.name= tmp_path;
  4998. if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
  4999. {
  5000. DBUG_RETURN(TRUE);
  5001. }
  5002. *next_entry= log_entry->entry_pos;
  5003. sub_elem->log_entry= log_entry;
  5004. insert_part_info_log_entry_list(part_info, log_entry);
  5005. } while (++j < no_subparts);
  5006. }
  5007. else
  5008. {
  5009. ddl_log_entry.next_entry= *next_entry;
  5010. ddl_log_entry.handler_name=
  5011. ha_resolve_storage_engine_name(part_elem->engine_type);
  5012. create_partition_name(tmp_path, path,
  5013. part_elem->partition_name,
  5014. name_variant, TRUE);
  5015. ddl_log_entry.name= tmp_path;
  5016. if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
  5017. {
  5018. DBUG_RETURN(TRUE);
  5019. }
  5020. *next_entry= log_entry->entry_pos;
  5021. part_elem->log_entry= log_entry;
  5022. insert_part_info_log_entry_list(part_info, log_entry);
  5023. }
  5024. }
  5025. }
  5026. DBUG_RETURN(FALSE);
  5027. }
  5028. /*
  5029. Set execute log entry in ddl log for this partitioned table
  5030. SYNOPSIS
  5031. set_part_info_exec_log_entry()
  5032. part_info Partition info object
  5033. exec_log_entry Log entry
  5034. RETURN VALUES
  5035. NONE
  5036. */
  5037. static void set_part_info_exec_log_entry(partition_info *part_info,
  5038. DDL_LOG_MEMORY_ENTRY *exec_log_entry)
  5039. {
  5040. part_info->exec_log_entry= exec_log_entry;
  5041. exec_log_entry->next_active_log_entry= NULL;
  5042. }
  5043. /*
  5044. Write the log entry to ensure that the shadow frm file is removed at
  5045. crash.
  5046. SYNOPSIS
  5047. write_log_drop_shadow_frm()
  5048. lpt Struct containing parameters
  5049. install_frm Should we log action to install shadow frm or should
  5050. the action be to remove the shadow frm file.
  5051. RETURN VALUES
  5052. TRUE Error
  5053. FALSE Success
  5054. DESCRIPTION
  5055. Prepare an entry to the ddl log indicating a drop/install of the shadow frm
  5056. file and its corresponding handler file.
  5057. */
  5058. static bool write_log_drop_shadow_frm(ALTER_PARTITION_PARAM_TYPE *lpt)
  5059. {
  5060. partition_info *part_info= lpt->part_info;
  5061. DDL_LOG_MEMORY_ENTRY *log_entry;
  5062. DDL_LOG_MEMORY_ENTRY *exec_log_entry= NULL;
  5063. char shadow_path[FN_REFLEN + 1];
  5064. DBUG_ENTER("write_log_drop_shadow_frm");
  5065. build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt);
  5066. pthread_mutex_lock(&LOCK_gdl);
  5067. if (write_log_replace_delete_frm(lpt, 0UL, NULL,
  5068. (const char*)shadow_path, FALSE))
  5069. goto error;
  5070. log_entry= part_info->first_log_entry;
  5071. if (write_execute_ddl_log_entry(log_entry->entry_pos,
  5072. FALSE, &exec_log_entry))
  5073. goto error;
  5074. pthread_mutex_unlock(&LOCK_gdl);
  5075. set_part_info_exec_log_entry(part_info, exec_log_entry);
  5076. DBUG_RETURN(FALSE);
  5077. error:
  5078. release_part_info_log_entries(part_info->first_log_entry);
  5079. pthread_mutex_unlock(&LOCK_gdl);
  5080. part_info->first_log_entry= NULL;
  5081. my_error(ER_DDL_LOG_ERROR, MYF(0));
  5082. DBUG_RETURN(TRUE);
  5083. }
  5084. /*
  5085. Log renaming of shadow frm to real frm name and dropping of old frm
  5086. SYNOPSIS
  5087. write_log_rename_frm()
  5088. lpt Struct containing parameters
  5089. RETURN VALUES
  5090. TRUE Error
  5091. FALSE Success
  5092. DESCRIPTION
  5093. Prepare an entry to ensure that we complete the renaming of the frm
  5094. file if failure occurs in the middle of the rename process.
  5095. */
  5096. static bool write_log_rename_frm(ALTER_PARTITION_PARAM_TYPE *lpt)
  5097. {
  5098. partition_info *part_info= lpt->part_info;
  5099. DDL_LOG_MEMORY_ENTRY *log_entry;
  5100. DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry;
  5101. char path[FN_REFLEN + 1];
  5102. char shadow_path[FN_REFLEN + 1];
  5103. DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry;
  5104. DBUG_ENTER("write_log_rename_frm");
  5105. part_info->first_log_entry= NULL;
  5106. build_table_filename(path, sizeof(path) - 1, lpt->db,
  5107. lpt->table_name, "", 0);
  5108. build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt);
  5109. pthread_mutex_lock(&LOCK_gdl);
  5110. if (write_log_replace_delete_frm(lpt, 0UL, shadow_path, path, TRUE))
  5111. goto error;
  5112. log_entry= part_info->first_log_entry;
  5113. part_info->frm_log_entry= log_entry;
  5114. if (write_execute_ddl_log_entry(log_entry->entry_pos,
  5115. FALSE, &exec_log_entry))
  5116. goto error;
  5117. release_part_info_log_entries(old_first_log_entry);
  5118. pthread_mutex_unlock(&LOCK_gdl);
  5119. DBUG_RETURN(FALSE);
  5120. error:
  5121. release_part_info_log_entries(part_info->first_log_entry);
  5122. pthread_mutex_unlock(&LOCK_gdl);
  5123. part_info->first_log_entry= old_first_log_entry;
  5124. part_info->frm_log_entry= NULL;
  5125. my_error(ER_DDL_LOG_ERROR, MYF(0));
  5126. DBUG_RETURN(TRUE);
  5127. }
  5128. /*
  5129. Write the log entries to ensure that the drop partition command is completed
  5130. even in the presence of a crash.
  5131. SYNOPSIS
  5132. write_log_drop_partition()
  5133. lpt Struct containing parameters
  5134. RETURN VALUES
  5135. TRUE Error
  5136. FALSE Success
  5137. DESCRIPTION
  5138. Prepare entries to the ddl log indicating all partitions to drop and to
  5139. install the shadow frm file and remove the old frm file.
  5140. */
  5141. static bool write_log_drop_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
  5142. {
  5143. partition_info *part_info= lpt->part_info;
  5144. DDL_LOG_MEMORY_ENTRY *log_entry;
  5145. DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry;
  5146. char tmp_path[FN_REFLEN + 1];
  5147. char path[FN_REFLEN + 1];
  5148. uint next_entry= 0;
  5149. DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry;
  5150. DBUG_ENTER("write_log_drop_partition");
  5151. part_info->first_log_entry= NULL;
  5152. build_table_filename(path, sizeof(path) - 1, lpt->db,
  5153. lpt->table_name, "", 0);
  5154. build_table_shadow_filename(tmp_path, sizeof(tmp_path) - 1, lpt);
  5155. pthread_mutex_lock(&LOCK_gdl);
  5156. if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path,
  5157. FALSE))
  5158. goto error;
  5159. if (write_log_replace_delete_frm(lpt, next_entry, (const char*)tmp_path,
  5160. (const char*)path, TRUE))
  5161. goto error;
  5162. log_entry= part_info->first_log_entry;
  5163. part_info->frm_log_entry= log_entry;
  5164. if (write_execute_ddl_log_entry(log_entry->entry_pos,
  5165. FALSE, &exec_log_entry))
  5166. goto error;
  5167. release_part_info_log_entries(old_first_log_entry);
  5168. pthread_mutex_unlock(&LOCK_gdl);
  5169. DBUG_RETURN(FALSE);
  5170. error:
  5171. release_part_info_log_entries(part_info->first_log_entry);
  5172. pthread_mutex_unlock(&LOCK_gdl);
  5173. part_info->first_log_entry= old_first_log_entry;
  5174. part_info->frm_log_entry= NULL;
  5175. my_error(ER_DDL_LOG_ERROR, MYF(0));
  5176. DBUG_RETURN(TRUE);
  5177. }
  5178. /*
  5179. Write the log entries to ensure that the add partition command is not
  5180. executed at all if a crash before it has completed
  5181. SYNOPSIS
  5182. write_log_add_change_partition()
  5183. lpt Struct containing parameters
  5184. RETURN VALUES
  5185. TRUE Error
  5186. FALSE Success
  5187. DESCRIPTION
  5188. Prepare entries to the ddl log indicating all partitions to drop and to
  5189. remove the shadow frm file.
  5190. We always inject entries backwards in the list in the ddl log since we
  5191. don't know the entry position until we have written it.
  5192. */
  5193. static bool write_log_add_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
  5194. {
  5195. partition_info *part_info= lpt->part_info;
  5196. DDL_LOG_MEMORY_ENTRY *log_entry;
  5197. DDL_LOG_MEMORY_ENTRY *exec_log_entry= NULL;
  5198. char tmp_path[FN_REFLEN + 1];
  5199. char path[FN_REFLEN + 1];
  5200. uint next_entry= 0;
  5201. DBUG_ENTER("write_log_add_change_partition");
  5202. build_table_filename(path, sizeof(path) - 1, lpt->db,
  5203. lpt->table_name, "", 0);
  5204. build_table_shadow_filename(tmp_path, sizeof(tmp_path) - 1, lpt);
  5205. pthread_mutex_lock(&LOCK_gdl);
  5206. if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path,
  5207. FALSE))
  5208. goto error;
  5209. if (write_log_replace_delete_frm(lpt, next_entry, NULL, tmp_path,
  5210. FALSE))
  5211. goto error;
  5212. log_entry= part_info->first_log_entry;
  5213. if (write_execute_ddl_log_entry(log_entry->entry_pos,
  5214. FALSE, &exec_log_entry))
  5215. goto error;
  5216. pthread_mutex_unlock(&LOCK_gdl);
  5217. set_part_info_exec_log_entry(part_info, exec_log_entry);
  5218. DBUG_RETURN(FALSE);
  5219. error:
  5220. release_part_info_log_entries(part_info->first_log_entry);
  5221. pthread_mutex_unlock(&LOCK_gdl);
  5222. part_info->first_log_entry= NULL;
  5223. my_error(ER_DDL_LOG_ERROR, MYF(0));
  5224. DBUG_RETURN(TRUE);
  5225. }
  5226. /*
  5227. Write description of how to complete the operation after first phase of
  5228. change partitions.
  5229. SYNOPSIS
  5230. write_log_final_change_partition()
  5231. lpt Struct containing parameters
  5232. RETURN VALUES
  5233. TRUE Error
  5234. FALSE Success
  5235. DESCRIPTION
  5236. We will write log entries that specify to remove all partitions reorganised,
  5237. to rename others to reflect the new naming scheme and to install the shadow
  5238. frm file.
  5239. */
  5240. static bool write_log_final_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
  5241. {
  5242. partition_info *part_info= lpt->part_info;
  5243. DDL_LOG_MEMORY_ENTRY *log_entry;
  5244. DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry;
  5245. char path[FN_REFLEN + 1];
  5246. char shadow_path[FN_REFLEN + 1];
  5247. DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry;
  5248. uint next_entry= 0;
  5249. DBUG_ENTER("write_log_final_change_partition");
  5250. part_info->first_log_entry= NULL;
  5251. build_table_filename(path, sizeof(path) - 1, lpt->db,
  5252. lpt->table_name, "", 0);
  5253. build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt);
  5254. pthread_mutex_lock(&LOCK_gdl);
  5255. if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path,
  5256. lpt->alter_info->flags & ALTER_REORGANIZE_PARTITION))
  5257. goto error;
  5258. if (write_log_changed_partitions(lpt, &next_entry, (const char*)path))
  5259. goto error;
  5260. if (write_log_replace_delete_frm(lpt, 0UL, shadow_path, path, TRUE))
  5261. goto error;
  5262. log_entry= part_info->first_log_entry;
  5263. part_info->frm_log_entry= log_entry;
  5264. if (write_execute_ddl_log_entry(log_entry->entry_pos,
  5265. FALSE, &exec_log_entry))
  5266. goto error;
  5267. release_part_info_log_entries(old_first_log_entry);
  5268. pthread_mutex_unlock(&LOCK_gdl);
  5269. DBUG_RETURN(FALSE);
  5270. error:
  5271. release_part_info_log_entries(part_info->first_log_entry);
  5272. pthread_mutex_unlock(&LOCK_gdl);
  5273. part_info->first_log_entry= old_first_log_entry;
  5274. part_info->frm_log_entry= NULL;
  5275. my_error(ER_DDL_LOG_ERROR, MYF(0));
  5276. DBUG_RETURN(TRUE);
  5277. }
  5278. /*
  5279. Remove entry from ddl log and release resources for others to use
  5280. SYNOPSIS
  5281. write_log_completed()
  5282. lpt Struct containing parameters
  5283. RETURN VALUES
  5284. TRUE Error
  5285. FALSE Success
  5286. */
  5287. static void write_log_completed(ALTER_PARTITION_PARAM_TYPE *lpt,
  5288. bool dont_crash)
  5289. {
  5290. partition_info *part_info= lpt->part_info;
  5291. DDL_LOG_MEMORY_ENTRY *log_entry= part_info->exec_log_entry;
  5292. DBUG_ENTER("write_log_completed");
  5293. DBUG_ASSERT(log_entry);
  5294. pthread_mutex_lock(&LOCK_gdl);
  5295. if (write_execute_ddl_log_entry(0UL, TRUE, &log_entry))
  5296. {
  5297. /*
  5298. Failed to write, Bad...
  5299. We have completed the operation but have log records to REMOVE
  5300. stuff that shouldn't be removed. What clever things could one do
  5301. here? An error output was written to the error output by the
  5302. above method so we don't do anything here.
  5303. */
  5304. ;
  5305. }
  5306. release_part_info_log_entries(part_info->first_log_entry);
  5307. release_part_info_log_entries(part_info->exec_log_entry);
  5308. pthread_mutex_unlock(&LOCK_gdl);
  5309. part_info->exec_log_entry= NULL;
  5310. part_info->first_log_entry= NULL;
  5311. DBUG_VOID_RETURN;
  5312. }
  5313. /*
  5314. Release all log entries
  5315. SYNOPSIS
  5316. release_log_entries()
  5317. part_info Partition info struct
  5318. RETURN VALUES
  5319. NONE
  5320. */
  5321. static void release_log_entries(partition_info *part_info)
  5322. {
  5323. pthread_mutex_lock(&LOCK_gdl);
  5324. release_part_info_log_entries(part_info->first_log_entry);
  5325. release_part_info_log_entries(part_info->exec_log_entry);
  5326. pthread_mutex_unlock(&LOCK_gdl);
  5327. part_info->first_log_entry= NULL;
  5328. part_info->exec_log_entry= NULL;
  5329. }
  5330. /*
  5331. Final part of partition changes to handle things when under
  5332. LOCK TABLES.
  5333. SYNPOSIS
  5334. alter_partition_lock_handling()
  5335. lpt Struct carrying parameters
  5336. RETURN VALUES
  5337. NONE
  5338. */
  5339. static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt)
  5340. {
  5341. int err;
  5342. if (lpt->thd->locked_tables)
  5343. {
  5344. /*
  5345. Close the table if open, to remove/destroy the already altered
  5346. table->part_info object, so that it is not reused.
  5347. */
  5348. if (lpt->table->db_stat)
  5349. abort_and_upgrade_lock_and_close_table(lpt);
  5350. /*
  5351. When we have the table locked, it is necessary to reopen the table
  5352. since all table objects were closed and removed as part of the
  5353. ALTER TABLE of partitioning structure.
  5354. */
  5355. pthread_mutex_lock(&LOCK_open);
  5356. lpt->thd->in_lock_tables= 1;
  5357. err= reopen_tables(lpt->thd, 1, 1);
  5358. lpt->thd->in_lock_tables= 0;
  5359. if (err)
  5360. {
  5361. /*
  5362. Issue a warning since we weren't able to regain the lock again.
  5363. We also need to unlink table from thread's open list and from
  5364. table_cache
  5365. */
  5366. unlink_open_table(lpt->thd, lpt->table, FALSE);
  5367. sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE");
  5368. }
  5369. pthread_mutex_unlock(&LOCK_open);
  5370. }
  5371. }
  5372. /*
  5373. Handle errors for ALTER TABLE for partitioning
  5374. SYNOPSIS
  5375. handle_alter_part_error()
  5376. lpt Struct carrying parameters
  5377. not_completed Was request in complete phase when error occurred
  5378. RETURN VALUES
  5379. NONE
  5380. */
  5381. void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
  5382. bool not_completed,
  5383. bool drop_partition,
  5384. bool frm_install)
  5385. {
  5386. partition_info *part_info= lpt->part_info;
  5387. DBUG_ENTER("handle_alter_part_error");
  5388. if (part_info->first_log_entry &&
  5389. execute_ddl_log_entry(current_thd,
  5390. part_info->first_log_entry->entry_pos))
  5391. {
  5392. /*
  5393. We couldn't recover from error, most likely manual interaction
  5394. is required.
  5395. */
  5396. write_log_completed(lpt, FALSE);
  5397. release_log_entries(part_info);
  5398. if (not_completed)
  5399. {
  5400. if (drop_partition)
  5401. {
  5402. /* Table is still ok, but we left a shadow frm file behind. */
  5403. push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
  5404. "%s %s",
  5405. "Operation was unsuccessful, table is still intact,",
  5406. "but it is possible that a shadow frm file was left behind");
  5407. }
  5408. else
  5409. {
  5410. push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
  5411. "%s %s %s %s",
  5412. "Operation was unsuccessful, table is still intact,",
  5413. "but it is possible that a shadow frm file was left behind.",
  5414. "It is also possible that temporary partitions are left behind,",
  5415. "these could be empty or more or less filled with records");
  5416. }
  5417. }
  5418. else
  5419. {
  5420. if (frm_install)
  5421. {
  5422. /*
  5423. Failed during install of shadow frm file, table isn't intact
  5424. and dropped partitions are still there
  5425. */
  5426. push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
  5427. "%s %s %s",
  5428. "Failed during alter of partitions, table is no longer intact.",
  5429. "The frm file is in an unknown state, and a backup",
  5430. "is required.");
  5431. }
  5432. else if (drop_partition)
  5433. {
  5434. /*
  5435. Table is ok, we have switched to new table but left dropped
  5436. partitions still in their places. We remove the log records and
  5437. ask the user to perform the action manually. We remove the log
  5438. records and ask the user to perform the action manually.
  5439. */
  5440. push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
  5441. "%s %s",
  5442. "Failed during drop of partitions, table is intact.",
  5443. "Manual drop of remaining partitions is required");
  5444. }
  5445. else
  5446. {
  5447. /*
  5448. We failed during renaming of partitions. The table is most
  5449. certainly in a very bad state so we give user warning and disable
  5450. the table by writing an ancient frm version into it.
  5451. */
  5452. push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
  5453. "%s %s %s",
  5454. "Failed during renaming of partitions. We are now in a position",
  5455. "where table is not reusable",
  5456. "Table is disabled by writing ancient frm file version into it");
  5457. }
  5458. }
  5459. }
  5460. else
  5461. {
  5462. release_log_entries(part_info);
  5463. if (not_completed)
  5464. {
  5465. /*
  5466. We hit an error before things were completed but managed
  5467. to recover from the error. An error occurred and we have
  5468. restored things to original so no need for further action.
  5469. */
  5470. ;
  5471. }
  5472. else
  5473. {
  5474. /*
  5475. We hit an error after we had completed most of the operation
  5476. and were successful in a second attempt so the operation
  5477. actually is successful now. We need to issue a warning that
  5478. even though we reported an error the operation was successfully
  5479. completed.
  5480. */
  5481. push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,"%s %s",
  5482. "Operation was successfully completed by failure handling,",
  5483. "after failure of normal operation");
  5484. }
  5485. }
  5486. DBUG_VOID_RETURN;
  5487. }
  5488. /*
  5489. Actually perform the change requested by ALTER TABLE of partitions
  5490. previously prepared.
  5491. SYNOPSIS
  5492. fast_alter_partition_table()
  5493. thd Thread object
  5494. table Table object
  5495. alter_info ALTER TABLE info
  5496. create_info Create info for CREATE TABLE
  5497. table_list List of the table involved
  5498. db Database name of new table
  5499. table_name Table name of new table
  5500. RETURN VALUES
  5501. TRUE Error
  5502. FALSE Success
  5503. DESCRIPTION
  5504. Perform all ALTER TABLE operations for partitioned tables that can be
  5505. performed fast without a full copy of the original table.
  5506. */
  5507. uint fast_alter_partition_table(THD *thd, TABLE *table,
  5508. Alter_info *alter_info,
  5509. HA_CREATE_INFO *create_info,
  5510. TABLE_LIST *table_list,
  5511. char *db,
  5512. const char *table_name,
  5513. uint fast_alter_partition)
  5514. {
  5515. /* Set-up struct used to write frm files */
  5516. partition_info *part_info= table->part_info;
  5517. ALTER_PARTITION_PARAM_TYPE lpt_obj;
  5518. ALTER_PARTITION_PARAM_TYPE *lpt= &lpt_obj;
  5519. bool written_bin_log= TRUE;
  5520. bool not_completed= TRUE;
  5521. bool frm_install= FALSE;
  5522. DBUG_ENTER("fast_alter_partition_table");
  5523. lpt->thd= thd;
  5524. lpt->part_info= part_info;
  5525. lpt->alter_info= alter_info;
  5526. lpt->create_info= create_info;
  5527. lpt->db_options= create_info->table_options;
  5528. if (create_info->row_type == ROW_TYPE_DYNAMIC)
  5529. lpt->db_options|= HA_OPTION_PACK_RECORD;
  5530. lpt->table= table;
  5531. lpt->key_info_buffer= 0;
  5532. lpt->key_count= 0;
  5533. lpt->db= db;
  5534. lpt->table_name= table_name;
  5535. lpt->copied= 0;
  5536. lpt->deleted= 0;
  5537. lpt->pack_frm_data= NULL;
  5538. lpt->pack_frm_len= 0;
  5539. thd->work_part_info= part_info;
  5540. /* Never update timestamp columns when alter */
  5541. table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
  5542. if (fast_alter_partition & HA_PARTITION_ONE_PHASE)
  5543. {
  5544. /*
  5545. In the case where the engine supports one phase online partition
  5546. changes it is not necessary to have any exclusive locks. The
  5547. correctness is upheld instead by transactions being aborted if they
  5548. access the table after its partition definition has changed (if they
  5549. are still using the old partition definition).
  5550. The handler is in this case responsible to ensure that all users
  5551. start using the new frm file after it has changed. To implement
  5552. one phase it is necessary for the handler to have the master copy
  5553. of the frm file and use discovery mechanisms to renew it. Thus
  5554. write frm will write the frm, pack the new frm and finally
  5555. the frm is deleted and the discovery mechanisms will either restore
  5556. back to the old or installing the new after the change is activated.
  5557. Thus all open tables will be discovered that they are old, if not
  5558. earlier as soon as they try an operation using the old table. One
  5559. should ensure that this is checked already when opening a table,
  5560. even if it is found in the cache of open tables.
  5561. change_partitions will perform all operations and it is the duty of
  5562. the handler to ensure that the frm files in the system gets updated
  5563. in synch with the changes made and if an error occurs that a proper
  5564. error handling is done.
  5565. If the MySQL Server crashes at this moment but the handler succeeds
  5566. in performing the change then the binlog is not written for the
  5567. change. There is no way to solve this as long as the binlog is not
  5568. transactional and even then it is hard to solve it completely.
  5569. The first approach here was to downgrade locks. Now a different approach
  5570. is decided upon. The idea is that the handler will have access to the
  5571. Alter_info when store_lock arrives with TL_WRITE_ALLOW_READ. So if the
  5572. handler knows that this functionality can be handled with a lower lock
  5573. level it will set the lock level to TL_WRITE_ALLOW_WRITE immediately.
  5574. Thus the need to downgrade the lock disappears.
  5575. 1) Write the new frm, pack it and then delete it
  5576. 2) Perform the change within the handler
  5577. */
  5578. if (mysql_write_frm(lpt, WFRM_WRITE_SHADOW | WFRM_PACK_FRM) ||
  5579. mysql_change_partitions(lpt))
  5580. {
  5581. goto err;
  5582. }
  5583. }
  5584. else if (alter_info->flags & ALTER_DROP_PARTITION)
  5585. {
  5586. /*
  5587. Now after all checks and setting state on dropped partitions we can
  5588. start the actual dropping of the partitions.
  5589. Drop partition is actually two things happening. The first is that
  5590. a lot of records are deleted. The second is that the behaviour of
  5591. subsequent updates and writes and deletes will change. The delete
  5592. part can be handled without any particular high lock level by
  5593. transactional engines whereas non-transactional engines need to
  5594. ensure that this change is done with an exclusive lock on the table.
  5595. The second part, the change of partitioning does however require
  5596. an exclusive lock to install the new partitioning as one atomic
  5597. operation. If this is not the case, it is possible for two
  5598. transactions to see the change in a different order than their
  5599. serialisation order. Thus we need an exclusive lock for both
  5600. transactional and non-transactional engines.
  5601. For LIST partitions it could be possible to avoid the exclusive lock
  5602. (and for RANGE partitions if they didn't rearrange range definitions
  5603. after a DROP PARTITION) if one ensured that failed accesses to the
  5604. dropped partitions was aborted for sure (thus only possible for
  5605. transactional engines).
  5606. 0) Write an entry that removes the shadow frm file if crash occurs
  5607. 1) Write the new frm file as a shadow frm
  5608. 2) Write the ddl log to ensure that the operation is completed
  5609. even in the presence of a MySQL Server crash
  5610. 3) Lock the table in TL_WRITE_ONLY to ensure all other accesses to
  5611. the table have completed. This ensures that other threads can not
  5612. execute on the table in parallel.
  5613. 4) Get a name lock on the table. This ensures that we can release all
  5614. locks on the table and since no one can open the table, there can
  5615. be no new threads accessing the table. They will be hanging on the
  5616. name lock.
  5617. 5) Close all tables that have already been opened but didn't stumble on
  5618. the abort locked previously. This is done as part of the
  5619. close_data_files_and_morph_locks call.
  5620. 6) We are now ready to release all locks we got in this thread.
  5621. 7) Write the bin log
  5622. Unfortunately the writing of the binlog is not synchronised with
  5623. other logging activities. So no matter in which order the binlog
  5624. is written compared to other activities there will always be cases
  5625. where crashes make strange things occur. In this placement it can
  5626. happen that the ALTER TABLE DROP PARTITION gets performed in the
  5627. master but not in the slaves if we have a crash, after writing the
  5628. ddl log but before writing the binlog. A solution to this would
  5629. require writing the statement first in the ddl log and then
  5630. when recovering from the crash read the binlog and insert it into
  5631. the binlog if not written already.
  5632. 8) Install the previously written shadow frm file
  5633. 9) Prepare handlers for drop of partitions
  5634. 10) Drop the partitions
  5635. 11) Remove entries from ddl log
  5636. 12) Reopen table if under lock tables
  5637. 13) Complete query
  5638. We insert Error injections at all places where it could be interesting
  5639. to test if recovery is properly done.
  5640. */
  5641. if (write_log_drop_shadow_frm(lpt) ||
  5642. ERROR_INJECT_CRASH("crash_drop_partition_1") ||
  5643. mysql_write_frm(lpt, WFRM_WRITE_SHADOW) ||
  5644. ERROR_INJECT_CRASH("crash_drop_partition_2") ||
  5645. write_log_drop_partition(lpt) ||
  5646. ERROR_INJECT_CRASH("crash_drop_partition_3") ||
  5647. (not_completed= FALSE) ||
  5648. abort_and_upgrade_lock_and_close_table(lpt) ||
  5649. ERROR_INJECT_CRASH("crash_drop_partition_5") ||
  5650. ((!thd->lex->no_write_to_binlog) &&
  5651. (write_bin_log(thd, FALSE,
  5652. thd->query(), thd->query_length()), FALSE)) ||
  5653. ERROR_INJECT_CRASH("crash_drop_partition_6") ||
  5654. ((frm_install= TRUE), FALSE) ||
  5655. mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
  5656. ((frm_install= FALSE), FALSE) ||
  5657. ERROR_INJECT_CRASH("crash_drop_partition_7") ||
  5658. mysql_drop_partitions(lpt) ||
  5659. ERROR_INJECT_CRASH("crash_drop_partition_8") ||
  5660. (write_log_completed(lpt, FALSE), FALSE) ||
  5661. ERROR_INJECT_CRASH("crash_drop_partition_9") ||
  5662. (alter_partition_lock_handling(lpt), FALSE))
  5663. {
  5664. handle_alter_part_error(lpt, not_completed, TRUE, frm_install);
  5665. goto err;
  5666. }
  5667. }
  5668. else if ((alter_info->flags & ALTER_ADD_PARTITION) &&
  5669. (part_info->part_type == RANGE_PARTITION ||
  5670. part_info->part_type == LIST_PARTITION))
  5671. {
  5672. /*
  5673. ADD RANGE/LIST PARTITIONS
  5674. In this case there are no tuples removed and no tuples are added.
  5675. Thus the operation is merely adding a new partition. Thus it is
  5676. necessary to perform the change as an atomic operation. Otherwise
  5677. someone reading without seeing the new partition could potentially
  5678. miss updates made by a transaction serialised before it that are
  5679. inserted into the new partition.
  5680. 0) Write an entry that removes the shadow frm file if crash occurs
  5681. 1) Write the new frm file as a shadow frm file
  5682. 2) Log the changes to happen in ddl log
  5683. 2) Add the new partitions
  5684. 3) Lock all partitions in TL_WRITE_ONLY to ensure that no users
  5685. are still using the old partitioning scheme. Wait until all
  5686. ongoing users have completed before progressing.
  5687. 4) Get a name lock on the table. This ensures that we can release all
  5688. locks on the table and since no one can open the table, there can
  5689. be no new threads accessing the table. They will be hanging on the
  5690. name lock.
  5691. 5) Close all tables that have already been opened but didn't stumble on
  5692. the abort locked previously. This is done as part of the
  5693. close_data_files_and_morph_locks call.
  5694. 6) Close all table handlers and unlock all handlers but retain name lock
  5695. 7) Write binlog
  5696. 8) Now the change is completed except for the installation of the
  5697. new frm file. We thus write an action in the log to change to
  5698. the shadow frm file
  5699. 9) Install the new frm file of the table where the partitions are
  5700. added to the table.
  5701. 10)Wait until all accesses using the old frm file has completed
  5702. 11)Remove entries from ddl log
  5703. 12)Reopen tables if under lock tables
  5704. 13)Complete query
  5705. */
  5706. if (write_log_add_change_partition(lpt) ||
  5707. ERROR_INJECT_CRASH("crash_add_partition_1") ||
  5708. mysql_write_frm(lpt, WFRM_WRITE_SHADOW) ||
  5709. ERROR_INJECT_CRASH("crash_add_partition_2") ||
  5710. mysql_change_partitions(lpt) ||
  5711. ERROR_INJECT_CRASH("crash_add_partition_3") ||
  5712. abort_and_upgrade_lock_and_close_table(lpt) ||
  5713. ERROR_INJECT_CRASH("crash_add_partition_5") ||
  5714. ((!thd->lex->no_write_to_binlog) &&
  5715. (write_bin_log(thd, FALSE,
  5716. thd->query(), thd->query_length()), FALSE)) ||
  5717. ERROR_INJECT_CRASH("crash_add_partition_6") ||
  5718. write_log_rename_frm(lpt) ||
  5719. (not_completed= FALSE) ||
  5720. ERROR_INJECT_CRASH("crash_add_partition_7") ||
  5721. ((frm_install= TRUE), FALSE) ||
  5722. mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
  5723. ERROR_INJECT_CRASH("crash_add_partition_8") ||
  5724. (write_log_completed(lpt, FALSE), FALSE) ||
  5725. ERROR_INJECT_CRASH("crash_add_partition_9") ||
  5726. (alter_partition_lock_handling(lpt), FALSE))
  5727. {
  5728. handle_alter_part_error(lpt, not_completed, FALSE, frm_install);
  5729. goto err;
  5730. }
  5731. }
  5732. else
  5733. {
  5734. /*
  5735. ADD HASH PARTITION/
  5736. COALESCE PARTITION/
  5737. REBUILD PARTITION/
  5738. REORGANIZE PARTITION
  5739. In this case all records are still around after the change although
  5740. possibly organised into new partitions, thus by ensuring that all
  5741. updates go to both the old and the new partitioning scheme we can
  5742. actually perform this operation lock-free. The only exception to
  5743. this is when REORGANIZE PARTITION adds/drops ranges. In this case
  5744. there needs to be an exclusive lock during the time when the range
  5745. changes occur.
  5746. This is only possible if the handler can ensure double-write for a
  5747. period. The double write will ensure that it doesn't matter where the
  5748. data is read from since both places are updated for writes. If such
  5749. double writing is not performed then it is necessary to perform the
  5750. change with the usual exclusive lock. With double writes it is even
  5751. possible to perform writes in parallel with the reorganisation of
  5752. partitions.
  5753. Without double write procedure we get the following procedure.
  5754. The only difference with using double write is that we can downgrade
  5755. the lock to TL_WRITE_ALLOW_WRITE. Double write in this case only
  5756. double writes from old to new. If we had double writing in both
  5757. directions we could perform the change completely without exclusive
  5758. lock for HASH partitions.
  5759. Handlers that perform double writing during the copy phase can actually
  5760. use a lower lock level. This can be handled inside store_lock in the
  5761. respective handler.
  5762. 0) Write an entry that removes the shadow frm file if crash occurs
  5763. 1) Write the shadow frm file of new partitioning
  5764. 2) Log such that temporary partitions added in change phase are
  5765. removed in a crash situation
  5766. 3) Add the new partitions
  5767. Copy from the reorganised partitions to the new partitions
  5768. 4) Log that operation is completed and log all complete actions
  5769. needed to complete operation from here
  5770. 5) Lock all partitions in TL_WRITE_ONLY to ensure that no users
  5771. are still using the old partitioning scheme. Wait until all
  5772. ongoing users have completed before progressing.
  5773. 6) Get a name lock of the table
  5774. 7) Close all tables opened but not yet locked, after this call we are
  5775. certain that no other thread is in the lock wait queue or has
  5776. opened the table. The name lock will ensure that they are blocked
  5777. on the open call.
  5778. This is achieved also by close_data_files_and_morph_locks call.
  5779. 8) Close all partitions opened by this thread, but retain name lock.
  5780. 9) Write bin log
  5781. 10) Prepare handlers for rename and delete of partitions
  5782. 11) Rename and drop the reorged partitions such that they are no
  5783. longer used and rename those added to their real new names.
  5784. 12) Install the shadow frm file
  5785. 13) Reopen the table if under lock tables
  5786. 14) Complete query
  5787. */
  5788. if (write_log_add_change_partition(lpt) ||
  5789. ERROR_INJECT_CRASH("crash_change_partition_1") ||
  5790. mysql_write_frm(lpt, WFRM_WRITE_SHADOW) ||
  5791. ERROR_INJECT_CRASH("crash_change_partition_2") ||
  5792. mysql_change_partitions(lpt) ||
  5793. ERROR_INJECT_CRASH("crash_change_partition_3") ||
  5794. write_log_final_change_partition(lpt) ||
  5795. ERROR_INJECT_CRASH("crash_change_partition_4") ||
  5796. (not_completed= FALSE) ||
  5797. abort_and_upgrade_lock_and_close_table(lpt) ||
  5798. ERROR_INJECT_CRASH("crash_change_partition_6") ||
  5799. ((!thd->lex->no_write_to_binlog) &&
  5800. (write_bin_log(thd, FALSE,
  5801. thd->query(), thd->query_length()), FALSE)) ||
  5802. ERROR_INJECT_CRASH("crash_change_partition_7") ||
  5803. mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
  5804. ERROR_INJECT_CRASH("crash_change_partition_8") ||
  5805. mysql_drop_partitions(lpt) ||
  5806. ERROR_INJECT_CRASH("crash_change_partition_9") ||
  5807. mysql_rename_partitions(lpt) ||
  5808. ((frm_install= TRUE), FALSE) ||
  5809. ERROR_INJECT_CRASH("crash_change_partition_10") ||
  5810. (write_log_completed(lpt, FALSE), FALSE) ||
  5811. ERROR_INJECT_CRASH("crash_change_partition_11") ||
  5812. (alter_partition_lock_handling(lpt), FALSE))
  5813. {
  5814. handle_alter_part_error(lpt, not_completed, FALSE, frm_install);
  5815. goto err;
  5816. }
  5817. }
  5818. /*
  5819. A final step is to write the query to the binlog and send ok to the
  5820. user
  5821. */
  5822. DBUG_RETURN(fast_end_partition(thd, lpt->copied, lpt->deleted,
  5823. table, table_list, FALSE, NULL,
  5824. written_bin_log));
  5825. err:
  5826. if (thd->locked_tables)
  5827. {
  5828. /*
  5829. table->part_info was altered in prep_alter_part_table and must be
  5830. destroyed and recreated, since otherwise it will be reused, since
  5831. we are under LOCK TABLE.
  5832. */
  5833. alter_partition_lock_handling(lpt);
  5834. }
  5835. else
  5836. {
  5837. /* Force the table to be closed to avoid reuse of the table->part_info */
  5838. close_thread_tables(thd);
  5839. }
  5840. DBUG_RETURN(TRUE);
  5841. }
  5842. #endif
  5843. /*
  5844. Prepare for calling val_int on partition function by setting fields to
  5845. point to the record where the values of the PF-fields are stored.
  5846. SYNOPSIS
  5847. set_field_ptr()
  5848. ptr Array of fields to change ptr
  5849. new_buf New record pointer
  5850. old_buf Old record pointer
  5851. DESCRIPTION
  5852. Set ptr in field objects of field array to refer to new_buf record
  5853. instead of previously old_buf. Used before calling val_int and after
  5854. it is used to restore pointers to table->record[0].
  5855. This routine is placed outside of partition code since it can be useful
  5856. also for other programs.
  5857. */
  5858. void set_field_ptr(Field **ptr, const uchar *new_buf,
  5859. const uchar *old_buf)
  5860. {
  5861. my_ptrdiff_t diff= (new_buf - old_buf);
  5862. DBUG_ENTER("set_field_ptr");
  5863. do
  5864. {
  5865. (*ptr)->move_field_offset(diff);
  5866. } while (*(++ptr));
  5867. DBUG_VOID_RETURN;
  5868. }
  5869. /*
  5870. Prepare for calling val_int on partition function by setting fields to
  5871. point to the record where the values of the PF-fields are stored.
  5872. This variant works on a key_part reference.
  5873. It is not required that all fields are NOT NULL fields.
  5874. SYNOPSIS
  5875. set_key_field_ptr()
  5876. key_info key info with a set of fields to change ptr
  5877. new_buf New record pointer
  5878. old_buf Old record pointer
  5879. DESCRIPTION
  5880. Set ptr in field objects of field array to refer to new_buf record
  5881. instead of previously old_buf. Used before calling val_int and after
  5882. it is used to restore pointers to table->record[0].
  5883. This routine is placed outside of partition code since it can be useful
  5884. also for other programs.
  5885. */
  5886. void set_key_field_ptr(KEY *key_info, const uchar *new_buf,
  5887. const uchar *old_buf)
  5888. {
  5889. KEY_PART_INFO *key_part= key_info->key_part;
  5890. uint key_parts= key_info->key_parts;
  5891. uint i= 0;
  5892. my_ptrdiff_t diff= (new_buf - old_buf);
  5893. DBUG_ENTER("set_key_field_ptr");
  5894. do
  5895. {
  5896. key_part->field->move_field_offset(diff);
  5897. key_part++;
  5898. } while (++i < key_parts);
  5899. DBUG_VOID_RETURN;
  5900. }
  5901. /*
  5902. SYNOPSIS
  5903. mem_alloc_error()
  5904. size Size of memory attempted to allocate
  5905. None
  5906. RETURN VALUES
  5907. None
  5908. DESCRIPTION
  5909. A routine to use for all the many places in the code where memory
  5910. allocation error can happen, a tremendous amount of them, needs
  5911. simple routine that signals this error.
  5912. */
  5913. void mem_alloc_error(size_t size)
  5914. {
  5915. my_error(ER_OUTOFMEMORY, MYF(0), static_cast<int>(size));
  5916. }
  5917. #ifdef WITH_PARTITION_STORAGE_ENGINE
  5918. /*
  5919. Return comma-separated list of used partitions in the provided given string
  5920. SYNOPSIS
  5921. make_used_partitions_str()
  5922. part_info IN Partitioning info
  5923. parts_str OUT The string to fill
  5924. DESCRIPTION
  5925. Generate a list of used partitions (from bits in part_info->used_partitions
  5926. bitmap), asd store it into the provided String object.
  5927. NOTE
  5928. The produced string must not be longer then MAX_PARTITIONS * (1 + FN_LEN).
  5929. */
  5930. void make_used_partitions_str(partition_info *part_info, String *parts_str)
  5931. {
  5932. parts_str->length(0);
  5933. partition_element *pe;
  5934. uint partition_id= 0;
  5935. List_iterator<partition_element> it(part_info->partitions);
  5936. if (part_info->is_sub_partitioned())
  5937. {
  5938. partition_element *head_pe;
  5939. while ((head_pe= it++))
  5940. {
  5941. List_iterator<partition_element> it2(head_pe->subpartitions);
  5942. while ((pe= it2++))
  5943. {
  5944. if (bitmap_is_set(&part_info->used_partitions, partition_id))
  5945. {
  5946. if (parts_str->length())
  5947. parts_str->append(',');
  5948. parts_str->append(head_pe->partition_name,
  5949. strlen(head_pe->partition_name),
  5950. system_charset_info);
  5951. parts_str->append('_');
  5952. parts_str->append(pe->partition_name,
  5953. strlen(pe->partition_name),
  5954. system_charset_info);
  5955. }
  5956. partition_id++;
  5957. }
  5958. }
  5959. }
  5960. else
  5961. {
  5962. while ((pe= it++))
  5963. {
  5964. if (bitmap_is_set(&part_info->used_partitions, partition_id))
  5965. {
  5966. if (parts_str->length())
  5967. parts_str->append(',');
  5968. parts_str->append(pe->partition_name, strlen(pe->partition_name),
  5969. system_charset_info);
  5970. }
  5971. partition_id++;
  5972. }
  5973. }
  5974. }
  5975. #endif
  5976. /****************************************************************************
  5977. * Partition interval analysis support
  5978. ***************************************************************************/
  5979. /*
  5980. Setup partition_info::* members related to partitioning range analysis
  5981. SYNOPSIS
  5982. set_up_partition_func_pointers()
  5983. part_info Partitioning info structure
  5984. DESCRIPTION
  5985. Assuming that passed partition_info structure already has correct values
  5986. for members that specify [sub]partitioning type, table fields, and
  5987. functions, set up partition_info::* members that are related to
  5988. Partitioning Interval Analysis (see get_partitions_in_range_iter for its
  5989. definition)
  5990. IMPLEMENTATION
  5991. There are two available interval analyzer functions:
  5992. (1) get_part_iter_for_interval_via_mapping
  5993. (2) get_part_iter_for_interval_via_walking
  5994. They both have limited applicability:
  5995. (1) is applicable for "PARTITION BY <RANGE|LIST>(func(t.field))", where
  5996. func is a monotonic function.
  5997. (2) is applicable for
  5998. "[SUB]PARTITION BY <any-partitioning-type>(any_func(t.integer_field))"
  5999. If both are applicable, (1) is preferred over (2).
  6000. This function sets part_info::get_part_iter_for_interval according to
  6001. this criteria, and also sets some auxilary fields that the function
  6002. uses.
  6003. */
  6004. #ifdef WITH_PARTITION_STORAGE_ENGINE
  6005. static void set_up_range_analysis_info(partition_info *part_info)
  6006. {
  6007. /* Set the catch-all default */
  6008. part_info->get_part_iter_for_interval= NULL;
  6009. part_info->get_subpart_iter_for_interval= NULL;
  6010. /*
  6011. Check if get_part_iter_for_interval_via_mapping() can be used for
  6012. partitioning
  6013. */
  6014. switch (part_info->part_type) {
  6015. case RANGE_PARTITION:
  6016. case LIST_PARTITION:
  6017. if (part_info->part_expr->get_monotonicity_info() != NON_MONOTONIC)
  6018. {
  6019. part_info->get_part_iter_for_interval=
  6020. get_part_iter_for_interval_via_mapping;
  6021. goto setup_subparts;
  6022. }
  6023. default:
  6024. ;
  6025. }
  6026. /*
  6027. Check if get_part_iter_for_interval_via_walking() can be used for
  6028. partitioning
  6029. */
  6030. if (part_info->no_part_fields == 1)
  6031. {
  6032. Field *field= part_info->part_field_array[0];
  6033. switch (field->type()) {
  6034. case MYSQL_TYPE_TINY:
  6035. case MYSQL_TYPE_SHORT:
  6036. case MYSQL_TYPE_INT24:
  6037. case MYSQL_TYPE_LONG:
  6038. case MYSQL_TYPE_LONGLONG:
  6039. part_info->get_part_iter_for_interval=
  6040. get_part_iter_for_interval_via_walking;
  6041. break;
  6042. default:
  6043. ;
  6044. }
  6045. }
  6046. setup_subparts:
  6047. /*
  6048. Check if get_part_iter_for_interval_via_walking() can be used for
  6049. subpartitioning
  6050. */
  6051. if (part_info->no_subpart_fields == 1)
  6052. {
  6053. Field *field= part_info->subpart_field_array[0];
  6054. switch (field->type()) {
  6055. case MYSQL_TYPE_TINY:
  6056. case MYSQL_TYPE_SHORT:
  6057. case MYSQL_TYPE_LONG:
  6058. case MYSQL_TYPE_LONGLONG:
  6059. part_info->get_subpart_iter_for_interval=
  6060. get_part_iter_for_interval_via_walking;
  6061. break;
  6062. default:
  6063. ;
  6064. }
  6065. }
  6066. }
  6067. typedef uint32 (*get_endpoint_func)(partition_info*, bool left_endpoint,
  6068. bool include_endpoint);
  6069. /*
  6070. Partitioning Interval Analysis: Initialize the iterator for "mapping" case
  6071. SYNOPSIS
  6072. get_part_iter_for_interval_via_mapping()
  6073. part_info Partition info
  6074. is_subpart TRUE - act for subpartitioning
  6075. FALSE - act for partitioning
  6076. min_value minimum field value, in opt_range key format.
  6077. max_value minimum field value, in opt_range key format.
  6078. flags Some combination of NEAR_MIN, NEAR_MAX, NO_MIN_RANGE,
  6079. NO_MAX_RANGE.
  6080. part_iter Iterator structure to be initialized
  6081. DESCRIPTION
  6082. Initialize partition set iterator to walk over the interval in
  6083. ordered-array-of-partitions (for RANGE partitioning) or
  6084. ordered-array-of-list-constants (for LIST partitioning) space.
  6085. IMPLEMENTATION
  6086. This function is used when partitioning is done by
  6087. <RANGE|LIST>(ascending_func(t.field)), and we can map an interval in
  6088. t.field space into a sub-array of partition_info::range_int_array or
  6089. partition_info::list_array (see get_partition_id_range_for_endpoint,
  6090. get_list_array_idx_for_endpoint for details).
  6091. The function performs this interval mapping, and sets the iterator to
  6092. traverse the sub-array and return appropriate partitions.
  6093. RETURN
  6094. 0 - No matching partitions (iterator not initialized)
  6095. 1 - Ok, iterator intialized for traversal of matching partitions.
  6096. -1 - All partitions would match (iterator not initialized)
  6097. */
  6098. int get_part_iter_for_interval_via_mapping(partition_info *part_info,
  6099. bool is_subpart,
  6100. uchar *min_value, uchar *max_value,
  6101. uint flags,
  6102. PARTITION_ITERATOR *part_iter)
  6103. {
  6104. DBUG_ASSERT(!is_subpart);
  6105. Field *field= part_info->part_field_array[0];
  6106. uint32 UNINIT_VAR(max_endpoint_val);
  6107. get_endpoint_func UNINIT_VAR(get_endpoint);
  6108. bool can_match_multiple_values; /* is not '=' */
  6109. uint field_len= field->pack_length_in_rec();
  6110. part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
  6111. if (part_info->part_type == RANGE_PARTITION)
  6112. {
  6113. if (part_info->part_charset_field_array)
  6114. get_endpoint= get_partition_id_range_for_endpoint_charset;
  6115. else
  6116. get_endpoint= get_partition_id_range_for_endpoint;
  6117. max_endpoint_val= part_info->no_parts;
  6118. part_iter->get_next= get_next_partition_id_range;
  6119. }
  6120. else if (part_info->part_type == LIST_PARTITION)
  6121. {
  6122. if (part_info->part_charset_field_array)
  6123. get_endpoint= get_list_array_idx_for_endpoint_charset;
  6124. else
  6125. get_endpoint= get_list_array_idx_for_endpoint;
  6126. max_endpoint_val= part_info->no_list_values;
  6127. part_iter->get_next= get_next_partition_id_list;
  6128. part_iter->part_info= part_info;
  6129. if (max_endpoint_val == 0)
  6130. {
  6131. /*
  6132. We handle this special case without optimisations since it is
  6133. of little practical value but causes a great number of complex
  6134. checks later in the code.
  6135. */
  6136. part_iter->part_nums.start= part_iter->part_nums.end= 0;
  6137. part_iter->part_nums.cur= 0;
  6138. part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE;
  6139. return -1;
  6140. }
  6141. }
  6142. else
  6143. MY_ASSERT_UNREACHABLE();
  6144. can_match_multiple_values= (flags || !min_value || !max_value ||
  6145. memcmp(min_value, max_value, field_len));
  6146. if (can_match_multiple_values &&
  6147. (part_info->part_type == RANGE_PARTITION ||
  6148. part_info->has_null_value))
  6149. {
  6150. /* Range scan on RANGE or LIST partitioned table */
  6151. enum_monotonicity_info monotonic;
  6152. monotonic= part_info->part_expr->get_monotonicity_info();
  6153. if (monotonic == MONOTONIC_INCREASING_NOT_NULL ||
  6154. monotonic == MONOTONIC_STRICT_INCREASING_NOT_NULL)
  6155. {
  6156. /* col is NOT NULL, but F(col) can return NULL, add NULL partition */
  6157. part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE;
  6158. }
  6159. }
  6160. /*
  6161. Find minimum: Do special handling if the interval has left bound in form
  6162. " NULL <= X ":
  6163. */
  6164. if (field->real_maybe_null() && part_info->has_null_value &&
  6165. !(flags & (NO_MIN_RANGE | NEAR_MIN)) && *min_value)
  6166. {
  6167. part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE;
  6168. part_iter->part_nums.start= part_iter->part_nums.cur= 0;
  6169. if (*max_value && !(flags & NO_MAX_RANGE))
  6170. {
  6171. /* The right bound is X <= NULL, i.e. it is a "X IS NULL" interval */
  6172. part_iter->part_nums.end= 0;
  6173. return 1;
  6174. }
  6175. }
  6176. else
  6177. {
  6178. if (flags & NO_MIN_RANGE)
  6179. part_iter->part_nums.start= part_iter->part_nums.cur= 0;
  6180. else
  6181. {
  6182. /*
  6183. Store the interval edge in the record buffer, and call the
  6184. function that maps the edge in table-field space to an edge
  6185. in ordered-set-of-partitions (for RANGE partitioning) or
  6186. index-in-ordered-array-of-list-constants (for LIST) space.
  6187. */
  6188. store_key_image_to_rec(field, min_value, field_len);
  6189. bool include_endp= !test(flags & NEAR_MIN);
  6190. part_iter->part_nums.start= get_endpoint(part_info, 1, include_endp);
  6191. if (!can_match_multiple_values && part_info->part_expr->null_value)
  6192. {
  6193. /* col = x and F(x) = NULL -> only search NULL partition */
  6194. part_iter->part_nums.cur= part_iter->part_nums.start= 0;
  6195. part_iter->part_nums.end= 0;
  6196. part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE;
  6197. return 1;
  6198. }
  6199. part_iter->part_nums.cur= part_iter->part_nums.start;
  6200. if (part_iter->part_nums.start == max_endpoint_val)
  6201. return 0; /* No partitions */
  6202. }
  6203. }
  6204. /* Find maximum, do the same as above but for right interval bound */
  6205. if (flags & NO_MAX_RANGE)
  6206. part_iter->part_nums.end= max_endpoint_val;
  6207. else
  6208. {
  6209. store_key_image_to_rec(field, max_value, field_len);
  6210. bool include_endp= !test(flags & NEAR_MAX);
  6211. part_iter->part_nums.end= get_endpoint(part_info, 0, include_endp);
  6212. if (part_iter->part_nums.start >= part_iter->part_nums.end &&
  6213. !part_iter->ret_null_part)
  6214. return 0; /* No partitions */
  6215. }
  6216. return 1; /* Ok, iterator initialized */
  6217. }
  6218. /* See get_part_iter_for_interval_via_walking for definition of what this is */
  6219. #define MAX_RANGE_TO_WALK 10
  6220. /*
  6221. Partitioning Interval Analysis: Initialize iterator to walk field interval
  6222. SYNOPSIS
  6223. get_part_iter_for_interval_via_walking()
  6224. part_info Partition info
  6225. is_subpart TRUE - act for subpartitioning
  6226. FALSE - act for partitioning
  6227. min_value minimum field value, in opt_range key format.
  6228. max_value minimum field value, in opt_range key format.
  6229. flags Some combination of NEAR_MIN, NEAR_MAX, NO_MIN_RANGE,
  6230. NO_MAX_RANGE.
  6231. part_iter Iterator structure to be initialized
  6232. DESCRIPTION
  6233. Initialize partition set iterator to walk over interval in integer field
  6234. space. That is, for "const1 <=? t.field <=? const2" interval, initialize
  6235. the iterator to return a set of [sub]partitions obtained with the
  6236. following procedure:
  6237. get partition id for t.field = const1, return it
  6238. get partition id for t.field = const1+1, return it
  6239. ... t.field = const1+2, ...
  6240. ... ... ...
  6241. ... t.field = const2 ...
  6242. IMPLEMENTATION
  6243. See get_partitions_in_range_iter for general description of interval
  6244. analysis. We support walking over the following intervals:
  6245. "t.field IS NULL"
  6246. "c1 <=? t.field <=? c2", where c1 and c2 are finite.
  6247. Intervals with +inf/-inf, and [NULL, c1] interval can be processed but
  6248. that is more tricky and I don't have time to do it right now.
  6249. Additionally we have these requirements:
  6250. * number of values in the interval must be less then number of
  6251. [sub]partitions, and
  6252. * Number of values in the interval must be less then MAX_RANGE_TO_WALK.
  6253. The rationale behind these requirements is that if they are not met
  6254. we're likely to hit most of the partitions and traversing the interval
  6255. will only add overhead. So it's better return "all partitions used" in
  6256. that case.
  6257. RETURN
  6258. 0 - No matching partitions, iterator not initialized
  6259. 1 - Some partitions would match, iterator intialized for traversing them
  6260. -1 - All partitions would match, iterator not initialized
  6261. */
  6262. int get_part_iter_for_interval_via_walking(partition_info *part_info,
  6263. bool is_subpart,
  6264. uchar *min_value, uchar *max_value,
  6265. uint flags,
  6266. PARTITION_ITERATOR *part_iter)
  6267. {
  6268. Field *field;
  6269. uint total_parts;
  6270. partition_iter_func get_next_func;
  6271. part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
  6272. if (is_subpart)
  6273. {
  6274. field= part_info->subpart_field_array[0];
  6275. total_parts= part_info->no_subparts;
  6276. get_next_func= get_next_subpartition_via_walking;
  6277. }
  6278. else
  6279. {
  6280. field= part_info->part_field_array[0];
  6281. total_parts= part_info->no_parts;
  6282. get_next_func= get_next_partition_via_walking;
  6283. }
  6284. /* Handle the "t.field IS NULL" interval, it is a special case */
  6285. if (field->real_maybe_null() && !(flags & (NO_MIN_RANGE | NO_MAX_RANGE)) &&
  6286. *min_value && *max_value)
  6287. {
  6288. /*
  6289. We don't have a part_iter->get_next() function that would find which
  6290. partition "t.field IS NULL" belongs to, so find partition that contains
  6291. NULL right here, and return an iterator over singleton set.
  6292. */
  6293. uint32 part_id;
  6294. field->set_null();
  6295. if (is_subpart)
  6296. {
  6297. if (!part_info->get_subpartition_id(part_info, &part_id))
  6298. {
  6299. init_single_partition_iterator(part_id, part_iter);
  6300. return 1; /* Ok, iterator initialized */
  6301. }
  6302. }
  6303. else
  6304. {
  6305. longlong dummy;
  6306. int res= part_info->is_sub_partitioned() ?
  6307. part_info->get_part_partition_id(part_info, &part_id,
  6308. &dummy):
  6309. part_info->get_partition_id(part_info, &part_id, &dummy);
  6310. if (!res)
  6311. {
  6312. init_single_partition_iterator(part_id, part_iter);
  6313. return 1; /* Ok, iterator initialized */
  6314. }
  6315. }
  6316. return 0; /* No partitions match */
  6317. }
  6318. if ((field->real_maybe_null() &&
  6319. ((!(flags & NO_MIN_RANGE) && *min_value) || // NULL <? X
  6320. (!(flags & NO_MAX_RANGE) && *max_value))) || // X <? NULL
  6321. (flags & (NO_MIN_RANGE | NO_MAX_RANGE))) // -inf at any bound
  6322. {
  6323. return -1; /* Can't handle this interval, have to use all partitions */
  6324. }
  6325. /* Get integers for left and right interval bound */
  6326. longlong a, b;
  6327. uint len= field->pack_length_in_rec();
  6328. store_key_image_to_rec(field, min_value, len);
  6329. a= field->val_int();
  6330. store_key_image_to_rec(field, max_value, len);
  6331. b= field->val_int();
  6332. /*
  6333. Handle a special case where the distance between interval bounds is
  6334. exactly 4G-1. This interval is too big for range walking, and if it is an
  6335. (x,y]-type interval then the following "b +=..." code will convert it to
  6336. an empty interval by "wrapping around" a + 4G-1 + 1 = a.
  6337. */
  6338. if ((ulonglong)b - (ulonglong)a == ~0ULL)
  6339. return -1;
  6340. a += test(flags & NEAR_MIN);
  6341. b += test(!(flags & NEAR_MAX));
  6342. ulonglong n_values= b - a;
  6343. if (n_values > total_parts || n_values > MAX_RANGE_TO_WALK)
  6344. return -1;
  6345. part_iter->field_vals.start= part_iter->field_vals.cur= a;
  6346. part_iter->field_vals.end= b;
  6347. part_iter->part_info= part_info;
  6348. part_iter->get_next= get_next_func;
  6349. return 1;
  6350. }
  6351. /*
  6352. PARTITION_ITERATOR::get_next implementation: enumerate partitions in range
  6353. SYNOPSIS
  6354. get_next_partition_id_range()
  6355. part_iter Partition set iterator structure
  6356. DESCRIPTION
  6357. This is implementation of PARTITION_ITERATOR::get_next() that returns
  6358. [sub]partition ids in [min_partition_id, max_partition_id] range.
  6359. The function conforms to partition_iter_func type.
  6360. RETURN
  6361. partition id
  6362. NOT_A_PARTITION_ID if there are no more partitions
  6363. */
  6364. uint32 get_next_partition_id_range(PARTITION_ITERATOR* part_iter)
  6365. {
  6366. if (part_iter->part_nums.cur >= part_iter->part_nums.end)
  6367. {
  6368. if (part_iter->ret_null_part)
  6369. {
  6370. part_iter->ret_null_part= FALSE;
  6371. return 0; /* NULL always in first range partition */
  6372. }
  6373. part_iter->part_nums.cur= part_iter->part_nums.start;
  6374. part_iter->ret_null_part= part_iter->ret_null_part_orig;
  6375. return NOT_A_PARTITION_ID;
  6376. }
  6377. else
  6378. return part_iter->part_nums.cur++;
  6379. }
  6380. /*
  6381. PARTITION_ITERATOR::get_next implementation for LIST partitioning
  6382. SYNOPSIS
  6383. get_next_partition_id_list()
  6384. part_iter Partition set iterator structure
  6385. DESCRIPTION
  6386. This implementation of PARTITION_ITERATOR::get_next() is special for
  6387. LIST partitioning: it enumerates partition ids in
  6388. part_info->list_array[i] where i runs over [min_idx, max_idx] interval.
  6389. The function conforms to partition_iter_func type.
  6390. RETURN
  6391. partition id
  6392. NOT_A_PARTITION_ID if there are no more partitions
  6393. */
  6394. uint32 get_next_partition_id_list(PARTITION_ITERATOR *part_iter)
  6395. {
  6396. if (part_iter->part_nums.cur >= part_iter->part_nums.end)
  6397. {
  6398. if (part_iter->ret_null_part)
  6399. {
  6400. part_iter->ret_null_part= FALSE;
  6401. return part_iter->part_info->has_null_part_id;
  6402. }
  6403. part_iter->part_nums.cur= part_iter->part_nums.start;
  6404. part_iter->ret_null_part= part_iter->ret_null_part_orig;
  6405. return NOT_A_PARTITION_ID;
  6406. }
  6407. else
  6408. return part_iter->part_info->list_array[part_iter->
  6409. part_nums.cur++].partition_id;
  6410. }
  6411. /*
  6412. PARTITION_ITERATOR::get_next implementation: walk over field-space interval
  6413. SYNOPSIS
  6414. get_next_partition_via_walking()
  6415. part_iter Partitioning iterator
  6416. DESCRIPTION
  6417. This implementation of PARTITION_ITERATOR::get_next() returns ids of
  6418. partitions that contain records with partitioning field value within
  6419. [start_val, end_val] interval.
  6420. The function conforms to partition_iter_func type.
  6421. RETURN
  6422. partition id
  6423. NOT_A_PARTITION_ID if there are no more partitioning.
  6424. */
  6425. static uint32 get_next_partition_via_walking(PARTITION_ITERATOR *part_iter)
  6426. {
  6427. uint32 part_id;
  6428. Field *field= part_iter->part_info->part_field_array[0];
  6429. while (part_iter->field_vals.cur != part_iter->field_vals.end)
  6430. {
  6431. longlong dummy;
  6432. field->store(part_iter->field_vals.cur++,
  6433. ((Field_num*)field)->unsigned_flag);
  6434. if ((part_iter->part_info->is_sub_partitioned() &&
  6435. !part_iter->part_info->get_part_partition_id(part_iter->part_info,
  6436. &part_id, &dummy)) ||
  6437. !part_iter->part_info->get_partition_id(part_iter->part_info,
  6438. &part_id, &dummy))
  6439. return part_id;
  6440. }
  6441. part_iter->field_vals.cur= part_iter->field_vals.start;
  6442. return NOT_A_PARTITION_ID;
  6443. }
  6444. /* Same as get_next_partition_via_walking, but for subpartitions */
  6445. static uint32 get_next_subpartition_via_walking(PARTITION_ITERATOR *part_iter)
  6446. {
  6447. Field *field= part_iter->part_info->subpart_field_array[0];
  6448. uint32 res;
  6449. if (part_iter->field_vals.cur == part_iter->field_vals.end)
  6450. {
  6451. part_iter->field_vals.cur= part_iter->field_vals.start;
  6452. return NOT_A_PARTITION_ID;
  6453. }
  6454. field->store(part_iter->field_vals.cur++, FALSE);
  6455. if (part_iter->part_info->get_subpartition_id(part_iter->part_info,
  6456. &res))
  6457. return NOT_A_PARTITION_ID;
  6458. return res;
  6459. }
  6460. /*
  6461. Create partition names
  6462. SYNOPSIS
  6463. create_partition_name()
  6464. out:out Created partition name string
  6465. in1 First part
  6466. in2 Second part
  6467. name_variant Normal, temporary or renamed partition name
  6468. RETURN VALUE
  6469. NONE
  6470. DESCRIPTION
  6471. This method is used to calculate the partition name, service routine to
  6472. the del_ren_cre_table method.
  6473. */
  6474. void create_partition_name(char *out, const char *in1,
  6475. const char *in2, uint name_variant,
  6476. bool translate)
  6477. {
  6478. char transl_part_name[FN_REFLEN];
  6479. const char *transl_part;
  6480. if (translate)
  6481. {
  6482. tablename_to_filename(in2, transl_part_name, FN_REFLEN);
  6483. transl_part= transl_part_name;
  6484. }
  6485. else
  6486. transl_part= in2;
  6487. if (name_variant == NORMAL_PART_NAME)
  6488. strxmov(out, in1, "#P#", transl_part, NullS);
  6489. else if (name_variant == TEMP_PART_NAME)
  6490. strxmov(out, in1, "#P#", transl_part, "#TMP#", NullS);
  6491. else if (name_variant == RENAMED_PART_NAME)
  6492. strxmov(out, in1, "#P#", transl_part, "#REN#", NullS);
  6493. }
  6494. /*
  6495. Create subpartition name
  6496. SYNOPSIS
  6497. create_subpartition_name()
  6498. out:out Created partition name string
  6499. in1 First part
  6500. in2 Second part
  6501. in3 Third part
  6502. name_variant Normal, temporary or renamed partition name
  6503. RETURN VALUE
  6504. NONE
  6505. DESCRIPTION
  6506. This method is used to calculate the subpartition name, service routine to
  6507. the del_ren_cre_table method.
  6508. */
  6509. void create_subpartition_name(char *out, const char *in1,
  6510. const char *in2, const char *in3,
  6511. uint name_variant)
  6512. {
  6513. char transl_part_name[FN_REFLEN], transl_subpart_name[FN_REFLEN];
  6514. tablename_to_filename(in2, transl_part_name, FN_REFLEN);
  6515. tablename_to_filename(in3, transl_subpart_name, FN_REFLEN);
  6516. if (name_variant == NORMAL_PART_NAME)
  6517. strxmov(out, in1, "#P#", transl_part_name,
  6518. "#SP#", transl_subpart_name, NullS);
  6519. else if (name_variant == TEMP_PART_NAME)
  6520. strxmov(out, in1, "#P#", transl_part_name,
  6521. "#SP#", transl_subpart_name, "#TMP#", NullS);
  6522. else if (name_variant == RENAMED_PART_NAME)
  6523. strxmov(out, in1, "#P#", transl_part_name,
  6524. "#SP#", transl_subpart_name, "#REN#", NullS);
  6525. }
  6526. #endif