Fix for bug #31207: Test "join_nested" shows different strategy on IA64

CPUs / Intel's ICC compile

The bug is a combination of two problems:

1. IA64/ICC MySQL binaries use glibc's qsort(), not the one in mysys.

2. The order relation implemented by join_tab_cmp() is not transitive,
i.e. it is possible to choose such a, b and c that (a < b) && (b < c)
but (c < a). This implies that result of a sort using the relation
implemented by join_tab_cmp() depends on the order in which
elements are compared, i.e. the result is implementation-specific. Since
choose_plan() uses qsort() to pre-sort the
join tables using join_tab_cmp() as a compare function, the results of
the sorting may vary depending on qsort() implementation.

It is neither possible nor important to implement a better ordering
algorithm in join_tab_cmp(). Therefore the only way to fix it is to
force our own qsort() to be used by renaming it to my_qsort(), so we don't depend
on linker to decide that.

This patch also "fixes" bug #20530: qsort redefinition violates the
standard.
This commit is contained in:
kaa@polly.(none) 2007-10-17 20:08:58 +04:00
parent 80a2d47b22
commit 6d1f3e8de5
21 changed files with 62 additions and 46 deletions

View File

@ -709,8 +709,10 @@ extern sig_handler my_set_alarm_variable(int signo);
extern void my_string_ptr_sort(void *base,uint items,size_s size); extern void my_string_ptr_sort(void *base,uint items,size_s size);
extern void radixsort_for_str_ptr(uchar* base[], uint number_of_elements, extern void radixsort_for_str_ptr(uchar* base[], uint number_of_elements,
size_s size_of_element,uchar *buffer[]); size_s size_of_element,uchar *buffer[]);
extern qsort_t qsort2(void *base_ptr, size_t total_elems, size_t size, extern qsort_t my_qsort(void *base_ptr, size_t total_elems, size_t size,
qsort2_cmp cmp, void *cmp_argument); qsort_cmp cmp);
extern qsort_t my_qsort2(void *base_ptr, size_t total_elems, size_t size,
qsort2_cmp cmp, void *cmp_argument);
extern qsort2_cmp get_ptr_compare(uint); extern qsort2_cmp get_ptr_compare(uint);
void my_store_ptr(byte *buff, uint pack_length, my_off_t pos); void my_store_ptr(byte *buff, uint pack_length, my_off_t pos);
my_off_t my_get_ptr(byte *ptr, uint pack_length); my_off_t my_get_ptr(byte *ptr, uint pack_length);

View File

@ -73,7 +73,7 @@ sqlobjects = net.lo
sql_cmn_objects = pack.lo client.lo my_time.lo sql_cmn_objects = pack.lo client.lo my_time.lo
# Not needed in the minimum library # Not needed in the minimum library
mysysobjects2 = my_lib.lo mysysobjects2 = my_lib.lo mf_qsort.lo
mysysobjects = $(mysysobjects1) $(mysysobjects2) mysysobjects = $(mysysobjects1) $(mysysobjects2)
target_libadd = $(mysysobjects) $(mystringsobjects) $(dbugobjects) \ target_libadd = $(mysysobjects) $(mystringsobjects) $(dbugobjects) \
$(sql_cmn_objects) $(vio_objects) $(sqlobjects) $(sql_cmn_objects) $(vio_objects) $(sqlobjects)

View File

@ -430,8 +430,8 @@ FT_INFO * ft_init_boolean_search(MI_INFO *info, uint keynr, byte *query,
ftb->list=(FTB_WORD **)alloc_root(&ftb->mem_root, ftb->list=(FTB_WORD **)alloc_root(&ftb->mem_root,
sizeof(FTB_WORD *)*ftb->queue.elements); sizeof(FTB_WORD *)*ftb->queue.elements);
memcpy(ftb->list, ftb->queue.root+1, sizeof(FTB_WORD *)*ftb->queue.elements); memcpy(ftb->list, ftb->queue.root+1, sizeof(FTB_WORD *)*ftb->queue.elements);
qsort2(ftb->list, ftb->queue.elements, sizeof(FTB_WORD *), my_qsort2(ftb->list, ftb->queue.elements, sizeof(FTB_WORD *),
(qsort2_cmp)FTB_WORD_cmp_list, ftb->charset); (qsort2_cmp)FTB_WORD_cmp_list, ftb->charset);
if (ftb->queue.elements<2) ftb->with_scan &= ~FTB_FLAG_TRUNC; if (ftb->queue.elements<2) ftb->with_scan &= ~FTB_FLAG_TRUNC;
ftb->state=READY; ftb->state=READY;
return ftb; return ftb;

View File

@ -281,7 +281,8 @@ FT_INFO *ft_init_nlq_search(MI_INFO *info, uint keynr, byte *query,
&dptr, left_root_right); &dptr, left_root_right);
if (flags & FT_SORTED) if (flags & FT_SORTED)
qsort2(dlist->doc, dlist->ndocs, sizeof(FT_DOC), (qsort2_cmp)&FT_DOC_cmp, 0); my_qsort2(dlist->doc, dlist->ndocs, sizeof(FT_DOC), (qsort2_cmp)&FT_DOC_cmp,
0);
err: err:
delete_tree(&aio.dtree); delete_tree(&aio.dtree);

View File

@ -3148,7 +3148,7 @@ static void fakebigcodes(HUFF_COUNTS *huff_counts, HUFF_COUNTS *end_count)
cur_sort_p= sort_counts; cur_sort_p= sort_counts;
while (cur_count_p < end_count_p) while (cur_count_p < end_count_p)
*(cur_sort_p++)= cur_count_p++; *(cur_sort_p++)= cur_count_p++;
(void) qsort(sort_counts, 256, sizeof(my_off_t*), (qsort_cmp) fakecmp); (void) my_qsort(sort_counts, 256, sizeof(my_off_t*), (qsort_cmp) fakecmp);
/* /*
Assign faked counts. Assign faked counts.

View File

@ -649,8 +649,8 @@ static int NEAR_F write_keys(MI_SORT_PARAM *info, register uchar **sort_keys,
uint sort_length=info->key_length; uint sort_length=info->key_length;
DBUG_ENTER("write_keys"); DBUG_ENTER("write_keys");
qsort2((byte*) sort_keys,count,sizeof(byte*),(qsort2_cmp) info->key_cmp, my_qsort2((byte*) sort_keys,count,sizeof(byte*),(qsort2_cmp) info->key_cmp,
info); info);
if (!my_b_inited(tempfile) && if (!my_b_inited(tempfile) &&
open_cached_file(tempfile, my_tmpdir(info->tmpdir), "ST", open_cached_file(tempfile, my_tmpdir(info->tmpdir), "ST",
DISK_BUFFER_SIZE, info->sort_info->param->myf_rw)) DISK_BUFFER_SIZE, info->sort_info->param->myf_rw))
@ -692,8 +692,8 @@ static int NEAR_F write_keys_varlen(MI_SORT_PARAM *info,
int err; int err;
DBUG_ENTER("write_keys_varlen"); DBUG_ENTER("write_keys_varlen");
qsort2((byte*) sort_keys,count,sizeof(byte*),(qsort2_cmp) info->key_cmp, my_qsort2((byte*) sort_keys,count,sizeof(byte*),(qsort2_cmp) info->key_cmp,
info); info);
if (!my_b_inited(tempfile) && if (!my_b_inited(tempfile) &&
open_cached_file(tempfile, my_tmpdir(info->tmpdir), "ST", open_cached_file(tempfile, my_tmpdir(info->tmpdir), "ST",
DISK_BUFFER_SIZE, info->sort_info->param->myf_rw)) DISK_BUFFER_SIZE, info->sort_info->param->myf_rw))
@ -735,8 +735,8 @@ static int NEAR_F write_index(MI_SORT_PARAM *info, register uchar **sort_keys,
{ {
DBUG_ENTER("write_index"); DBUG_ENTER("write_index");
qsort2((gptr) sort_keys,(size_t) count,sizeof(byte*), my_qsort2((gptr) sort_keys,(size_t) count,sizeof(byte*),
(qsort2_cmp) info->key_cmp,info); (qsort2_cmp) info->key_cmp,info);
while (count--) while (count--)
{ {
if ((*info->key_write)(info,*sort_keys++)) if ((*info->key_write)(info,*sort_keys++))

View File

@ -2268,7 +2268,7 @@ static int flush_cached_blocks(KEY_CACHE *keycache,
As all blocks referred in 'cache' are marked by BLOCK_IN_FLUSH As all blocks referred in 'cache' are marked by BLOCK_IN_FLUSH
we are guarunteed no thread will change them we are guarunteed no thread will change them
*/ */
qsort((byte*) cache, count, sizeof(*cache), (qsort_cmp) cmp_sec_link); my_qsort((byte*) cache, count, sizeof(*cache), (qsort_cmp) cmp_sec_link);
keycache_pthread_mutex_lock(&keycache->cache_lock); keycache_pthread_mutex_lock(&keycache->cache_lock);
for ( ; cache != end ; cache++) for ( ; cache != end ; cache++)

View File

@ -91,10 +91,10 @@ typedef struct st_stack
*****************************************************************************/ *****************************************************************************/
#ifdef QSORT_EXTRA_CMP_ARGUMENT #ifdef QSORT_EXTRA_CMP_ARGUMENT
qsort_t qsort2(void *base_ptr, size_t count, size_t size, qsort2_cmp cmp, qsort_t my_qsort2(void *base_ptr, size_t count, size_t size, qsort2_cmp cmp,
void *cmp_argument) void *cmp_argument)
#else #else
qsort_t qsort(void *base_ptr, size_t count, size_t size, qsort_cmp cmp) qsort_t my_qsort(void *base_ptr, size_t count, size_t size, qsort_cmp cmp)
#endif #endif
{ {
char *low, *high, *pivot; char *low, *high, *pivot;

View File

@ -35,7 +35,7 @@ void my_string_ptr_sort(void *base, uint items, size_s size)
if (size && items) if (size && items)
{ {
uint size_arg=size; uint size_arg=size;
qsort2(base,items,sizeof(byte*),get_ptr_compare(size),(void*) &size_arg); my_qsort2(base,items,sizeof(byte*),get_ptr_compare(size),(void*) &size_arg);
} }
} }
} }

View File

@ -187,7 +187,7 @@ MY_DIR *my_dir(const char *path, myf MyFlags)
result->number_off_files= dir_entries_storage->elements; result->number_off_files= dir_entries_storage->elements;
if (!(MyFlags & MY_DONT_SORT)) if (!(MyFlags & MY_DONT_SORT))
qsort((void *) result->dir_entry, result->number_off_files, my_qsort((void *) result->dir_entry, result->number_off_files,
sizeof(FILEINFO), (qsort_cmp) comp_names); sizeof(FILEINFO), (qsort_cmp) comp_names);
DBUG_RETURN(result); DBUG_RETURN(result);
@ -498,7 +498,7 @@ MY_DIR *my_dir(const char *path, myf MyFlags)
result->number_off_files= dir_entries_storage->elements; result->number_off_files= dir_entries_storage->elements;
if (!(MyFlags & MY_DONT_SORT)) if (!(MyFlags & MY_DONT_SORT))
qsort((void *) result->dir_entry, result->number_off_files, my_qsort((void *) result->dir_entry, result->number_off_files,
sizeof(FILEINFO), (qsort_cmp) comp_names); sizeof(FILEINFO), (qsort_cmp) comp_names);
DBUG_PRINT("exit", ("found %d files", result->number_off_files)); DBUG_PRINT("exit", ("found %d files", result->number_off_files));
DBUG_RETURN(result); DBUG_RETURN(result);
@ -605,7 +605,7 @@ MY_DIR *my_dir(const char* path, myf MyFlags)
result->number_off_files= dir_entries_storage->elements; result->number_off_files= dir_entries_storage->elements;
if (!(MyFlags & MY_DONT_SORT)) if (!(MyFlags & MY_DONT_SORT))
qsort((void *) result->dir_entry, result->number_off_files, my_qsort((void *) result->dir_entry, result->number_off_files,
sizeof(FILEINFO), (qsort_cmp) comp_names); sizeof(FILEINFO), (qsort_cmp) comp_names);
DBUG_RETURN(result); DBUG_RETURN(result);

View File

@ -250,6 +250,6 @@ static int queue_fix_cmp(QUEUE *queue, void **a, void **b)
void queue_fix(QUEUE *queue) void queue_fix(QUEUE *queue)
{ {
qsort2(queue->root+1,queue->elements, sizeof(void *), my_qsort2(queue->root+1,queue->elements, sizeof(void *),
(qsort2_cmp)queue_fix_cmp, queue); (qsort2_cmp)queue_fix_cmp, queue);
} }

View File

@ -852,7 +852,7 @@ int ha_tina::rnd_end()
It also sorts so that we move the final blocks to the It also sorts so that we move the final blocks to the
beginning so that we move the smallest amount of data possible. beginning so that we move the smallest amount of data possible.
*/ */
qsort(chain, (size_t)(chain_ptr - chain), sizeof(tina_set), (qsort_cmp)sort_set); my_qsort(chain, (size_t)(chain_ptr - chain), sizeof(tina_set), (qsort_cmp)sort_set);
for (ptr= chain; ptr < chain_ptr; ptr++) for (ptr= chain; ptr < chain_ptr; ptr++)
{ {
memmove(share->mapped_file + ptr->begin, share->mapped_file + ptr->end, memmove(share->mapped_file + ptr->begin, share->mapped_file + ptr->end,

View File

@ -2775,7 +2775,7 @@ static inline int cmp_ulongs (ulonglong a_val, ulonglong b_val)
SYNOPSIS SYNOPSIS
cmp_longlong() cmp_longlong()
cmp_arg an argument passed to the calling function (qsort2) cmp_arg an argument passed to the calling function (my_qsort2)
a left argument a left argument
b right argument b right argument

View File

@ -781,7 +781,7 @@ public:
virtual byte *get_value(Item *item)=0; virtual byte *get_value(Item *item)=0;
void sort() void sort()
{ {
qsort2(base,used_count,size,compare,collation); my_qsort2(base,used_count,size,compare,collation);
} }
int find(Item *item); int find(Item *item);

View File

@ -3159,8 +3159,8 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
ROR_SCAN_INFO's. ROR_SCAN_INFO's.
Step 2: Get best ROR-intersection using an approximate algorithm. Step 2: Get best ROR-intersection using an approximate algorithm.
*/ */
qsort(tree->ror_scans, tree->n_ror_scans, sizeof(ROR_SCAN_INFO*), my_qsort(tree->ror_scans, tree->n_ror_scans, sizeof(ROR_SCAN_INFO*),
(qsort_cmp)cmp_ror_scan_info); (qsort_cmp)cmp_ror_scan_info);
DBUG_EXECUTE("info",print_ror_scans_arr(param->table, "ordered", DBUG_EXECUTE("info",print_ror_scans_arr(param->table, "ordered",
tree->ror_scans, tree->ror_scans,
tree->ror_scans_end);); tree->ror_scans_end););
@ -3349,8 +3349,8 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param,
bitmap_get_first(&(*scan)->covered_fields); bitmap_get_first(&(*scan)->covered_fields);
} }
qsort(ror_scan_mark, ror_scans_end-ror_scan_mark, sizeof(ROR_SCAN_INFO*), my_qsort(ror_scan_mark, ror_scans_end-ror_scan_mark, sizeof(ROR_SCAN_INFO*),
(qsort_cmp)cmp_ror_scan_info_covering); (qsort_cmp)cmp_ror_scan_info_covering);
DBUG_EXECUTE("info", print_ror_scans_arr(param->table, DBUG_EXECUTE("info", print_ror_scans_arr(param->table,
"remaining scans", "remaining scans",

View File

@ -497,7 +497,8 @@ static int rr_from_cache(READ_RECORD *info)
int3store(ref_position,(long) i); int3store(ref_position,(long) i);
ref_position+=3; ref_position+=3;
} }
qsort(info->read_positions,length,info->struct_length,(qsort_cmp) rr_cmp); my_qsort(info->read_positions, length, info->struct_length,
(qsort_cmp) rr_cmp);
position=info->read_positions; position=info->read_positions;
for (i=0 ; i < length ; i++) for (i=0 ; i < length ; i++)

View File

@ -248,8 +248,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
#endif #endif
VOID(push_dynamic(&acl_hosts,(gptr) &host)); VOID(push_dynamic(&acl_hosts,(gptr) &host));
} }
qsort((gptr) dynamic_element(&acl_hosts,0,ACL_HOST*),acl_hosts.elements, my_qsort((gptr) dynamic_element(&acl_hosts,0,ACL_HOST*),acl_hosts.elements,
sizeof(ACL_HOST),(qsort_cmp) acl_compare); sizeof(ACL_HOST),(qsort_cmp) acl_compare);
end_read_record(&read_record_info); end_read_record(&read_record_info);
freeze_size(&acl_hosts); freeze_size(&acl_hosts);
@ -421,8 +421,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
allow_all_hosts=1; // Anyone can connect allow_all_hosts=1; // Anyone can connect
} }
} }
qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements, my_qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
sizeof(ACL_USER),(qsort_cmp) acl_compare); sizeof(ACL_USER),(qsort_cmp) acl_compare);
end_read_record(&read_record_info); end_read_record(&read_record_info);
freeze_size(&acl_users); freeze_size(&acl_users);
@ -479,8 +479,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
#endif #endif
VOID(push_dynamic(&acl_dbs,(gptr) &db)); VOID(push_dynamic(&acl_dbs,(gptr) &db));
} }
qsort((gptr) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements, my_qsort((gptr) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
sizeof(ACL_DB),(qsort_cmp) acl_compare); sizeof(ACL_DB),(qsort_cmp) acl_compare);
end_read_record(&read_record_info); end_read_record(&read_record_info);
freeze_size(&acl_dbs); freeze_size(&acl_dbs);
init_check_host(); init_check_host();
@ -1110,8 +1110,8 @@ static void acl_insert_user(const char *user, const char *host,
if (!acl_user.host.hostname || if (!acl_user.host.hostname ||
(acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1])) (acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1]))
allow_all_hosts=1; // Anyone can connect /* purecov: tested */ allow_all_hosts=1; // Anyone can connect /* purecov: tested */
qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements, my_qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
sizeof(ACL_USER),(qsort_cmp) acl_compare); sizeof(ACL_USER),(qsort_cmp) acl_compare);
/* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */ /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
rebuild_check_host(); rebuild_check_host();
@ -1173,8 +1173,8 @@ static void acl_insert_db(const char *user, const char *host, const char *db,
acl_db.access=privileges; acl_db.access=privileges;
acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user); acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user);
VOID(push_dynamic(&acl_dbs,(gptr) &acl_db)); VOID(push_dynamic(&acl_dbs,(gptr) &acl_db));
qsort((gptr) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements, my_qsort((gptr) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
sizeof(ACL_DB),(qsort_cmp) acl_compare); sizeof(ACL_DB),(qsort_cmp) acl_compare);
} }

View File

@ -62,7 +62,7 @@ public:
void sort(CMP_FUNC cmp_func) void sort(CMP_FUNC cmp_func)
{ {
qsort(array.buffer, array.elements, sizeof(Elem), (qsort_cmp)cmp_func); my_qsort(array.buffer, array.elements, sizeof(Elem), (qsort_cmp)cmp_func);
} }
}; };

View File

@ -524,7 +524,7 @@ int send_variant_2_list(MEM_ROOT *mem_root, Protocol *protocol,
List_iterator<String> it(*names); List_iterator<String> it(*names);
for (pos= pointers; pos!=end; (*pos++= it++)); for (pos= pointers; pos!=end; (*pos++= it++));
qsort(pointers,names->elements,sizeof(String*),string_ptr_cmp); my_qsort(pointers,names->elements,sizeof(String*),string_ptr_cmp);
for (pos= pointers; pos!=end; pos++) for (pos= pointers; pos!=end; pos++)
{ {

View File

@ -3658,7 +3658,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
{ {
KEYUSE key_end,*prev,*save_pos,*use; KEYUSE key_end,*prev,*save_pos,*use;
qsort(keyuse->buffer,keyuse->elements,sizeof(KEYUSE), my_qsort(keyuse->buffer,keyuse->elements,sizeof(KEYUSE),
(qsort_cmp) sort_keyuse); (qsort_cmp) sort_keyuse);
bzero((char*) &key_end,sizeof(key_end)); /* Add for easy testing */ bzero((char*) &key_end,sizeof(key_end)); /* Add for easy testing */
@ -4371,8 +4371,9 @@ choose_plan(JOIN *join, table_map join_tables)
Apply heuristic: pre-sort all access plans with respect to the number of Apply heuristic: pre-sort all access plans with respect to the number of
records accessed. records accessed.
*/ */
qsort(join->best_ref + join->const_tables, join->tables - join->const_tables, my_qsort(join->best_ref + join->const_tables,
sizeof(JOIN_TAB*), straight_join?join_tab_cmp_straight:join_tab_cmp); join->tables - join->const_tables, sizeof(JOIN_TAB*),
straight_join ? join_tab_cmp_straight : join_tab_cmp);
if (straight_join) if (straight_join)
{ {
@ -4421,6 +4422,17 @@ choose_plan(JOIN *join, table_map join_tables)
ptr1 pointer to first JOIN_TAB object ptr1 pointer to first JOIN_TAB object
ptr2 pointer to second JOIN_TAB object ptr2 pointer to second JOIN_TAB object
NOTES
The order relation implemented by join_tab_cmp() is not transitive,
i.e. it is possible to choose such a, b and c that (a < b) && (b < c)
but (c < a). This implies that result of a sort using the relation
implemented by join_tab_cmp() depends on the order in which
elements are compared, i.e. the result is implementation-specific.
Example:
a: dependent = 0x0 table->map = 0x1 found_records = 3 ptr = 0x907e6b0
b: dependent = 0x0 table->map = 0x2 found_records = 3 ptr = 0x907e838
c: dependent = 0x6 table->map = 0x10 found_records = 2 ptr = 0x907ecd0
RETURN RETURN
1 if first is bigger 1 if first is bigger
-1 if second is bigger -1 if second is bigger

View File

@ -1476,7 +1476,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
/* Sort keys in optimized order */ /* Sort keys in optimized order */
qsort((gptr) *key_info_buffer, *key_count, sizeof(KEY), my_qsort((gptr) *key_info_buffer, *key_count, sizeof(KEY),
(qsort_cmp) sort_keys); (qsort_cmp) sort_keys);
create_info->null_bits= null_fields; create_info->null_bits= null_fields;