From 64984296241873f90e39d744b59690c7b3b408bb Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 12 May 2001 17:52:54 -0600 Subject: [PATCH 1/3] fixes/optimizations for count(distinct) more extensive testing of count(distinct) sql/item_sum.cc: fixes/optimizations for count(distinct) --- mysql-test/r/count_distinct2.result | 72 +++++++++++++++++++++++++++++ mysql-test/t/count_distinct2.test | 42 +++++++++++++++++ sql/item_sum.cc | 25 ++++++---- 3 files changed, 130 insertions(+), 9 deletions(-) create mode 100644 mysql-test/r/count_distinct2.result create mode 100644 mysql-test/t/count_distinct2.test diff --git a/mysql-test/r/count_distinct2.result b/mysql-test/r/count_distinct2.result new file mode 100644 index 00000000000..b2bcf11a37c --- /dev/null +++ b/mysql-test/r/count_distinct2.result @@ -0,0 +1,72 @@ +n1 +1 +2 +NULL +count(distinct n1) +2 +n2 +11 +12 +13 +NULL +count(distinct n2) +3 +s +one +two +NULL +count(distinct s) +2 +vs +eleven +twevle +thirteen +NULL +count(distinct vs) +3 +t +eleven +twelve +foo +bar +NULL +count(distinct t) +4 +n1 n2 +1 11 +2 11 +2 12 +2 13 +NULL 13 +2 NULL +count(distinct n1,n2) +4 +n1 s +1 one +2 two +NULL two +2 NULL +count(distinct n1,s) +2 +s n1 vs +one 1 eleven +two 2 eleven +two 2 twevle +two 2 thirteen +two NULL thirteen +NULL 2 thirteen +two 2 NULL +count(distinct s,n1,vs) +4 +s t +one eleven +two eleven +two twelve +two foo +two bar +NULL bar +two NULL +count(distinct s,t) +5 +count(distinct n1) count(distinct n2) +2 3 diff --git a/mysql-test/t/count_distinct2.test b/mysql-test/t/count_distinct2.test new file mode 100644 index 00000000000..cfdce77622c --- /dev/null +++ b/mysql-test/t/count_distinct2.test @@ -0,0 +1,42 @@ +create table t1(n1 int, n2 int, s char(20), vs varchar(20), t text); +insert into t1 values (1,11, 'one','eleven', 'eleven'), + (1,11, 'one','eleven', 'eleven'), + (2,11, 'two','eleven', 'eleven'), + (2,12, 'two','twevle', 'twelve'), + (2,13, 'two','thirteen', 'foo'), + (2,13, 'two','thirteen', 'foo'), + (2,13, 'two','thirteen', 'bar'), + (NULL,13, 'two','thirteen', 'bar'), + (2,NULL, 'two','thirteen', 'bar'), + (2,13, NULL,'thirteen', 'bar'), + (2,13, 'two',NULL, 'bar'), + (2,13, 'two','thirteen', NULL); + +select distinct n1 from t1; +select count(distinct n1) from t1; + +select distinct n2 from t1; +select count(distinct n2) from t1; + +select distinct s from t1; +select count(distinct s) from t1; + +select distinct vs from t1; +select count(distinct vs) from t1; + +select distinct t from t1; +select count(distinct t) from t1; + +select distinct n1,n2 from t1; +select count(distinct n1,n2) from t1; + +select distinct n1,s from t1; +select count(distinct n1,s) from t1; + +select distinct s,n1,vs from t1; +select count(distinct s,n1,vs) from t1; + +select distinct s,t from t1; +select count(distinct s,t) from t1; + +select count(distinct n1), count(distinct n2) from t1; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index da0bc71ca1f..c0fba1dd80d 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -810,12 +810,13 @@ int composite_key_cmp(void* arg, byte* key1, byte* key2) for(; field < field_end; ++field) { int res; - int len = (*field)->field_length; + Field* f = *field; + int len = f->field_length; switch((*field)->type()) { case FIELD_TYPE_STRING: case FIELD_TYPE_VAR_STRING: - res = my_sortcmp(key1, key2, len); + res = f->key_cmp(key1, key2); break; default: res = memcmp(key1, key2, len); @@ -879,20 +880,22 @@ bool Item_sum_count_distinct::setup(THD *thd) // to use a simpler key compare method that can take advantage // of not having to worry about other fields { - switch(table->field[0]->type()) + Field* field = table->field[0]; + switch(field->type()) { // if we have a string, we must take care of charsets // and case sensitivity case FIELD_TYPE_STRING: case FIELD_TYPE_VAR_STRING: - compare_key = (qsort_cmp2)simple_str_key_cmp; + compare_key = (qsort_cmp2)(field->binary() ? simple_raw_key_cmp: + simple_str_key_cmp); break; default: // since at this point we cannot have blobs // anything else can be compared with memcmp compare_key = (qsort_cmp2)simple_raw_key_cmp; break; } - cmp_arg = (void*)(key_len = table->field[0]->field_length); + cmp_arg = (void*)(key_len = field->field_length); rec_offset = 1; } else // too bad, cannot cheat - there is more than one field @@ -908,7 +911,8 @@ bool Item_sum_count_distinct::setup(THD *thd) rec_offset = table->reclength - key_len; } - init_tree(&tree, 0, key_len, compare_key, 0, 0); + init_tree(&tree, min(max_heap_table_size, sortbuff_size/16), + key_len, compare_key, 0, 0); tree.cmp_arg = cmp_arg; use_tree = 1; } @@ -919,11 +923,14 @@ bool Item_sum_count_distinct::setup(THD *thd) void Item_sum_count_distinct::reset() { - table->file->extra(HA_EXTRA_NO_CACHE); - table->file->delete_all_rows(); - table->file->extra(HA_EXTRA_WRITE_CACHE); if(use_tree) delete_tree(&tree); + else + { + table->file->extra(HA_EXTRA_NO_CACHE); + table->file->delete_all_rows(); + table->file->extra(HA_EXTRA_WRITE_CACHE); + } (void) add(); } From b7b7eb9c05f3036da3f90b5cad03a3b47d1f7aa2 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 12 May 2001 19:50:51 -0600 Subject: [PATCH 2/3] option to free_root() to not my_free() the blocks fixed bug/updated count_distinct2 test changed reset in count distinct to avoid calls to my_free() include/my_sys.h: option to free_root() not to do my_free() include/my_tree.h: reset_tree() mysql-test/r/count_distinct2.result: added group by test mysql-test/t/count_distinct2.test: group by test + fixed bug - need to drop table mysys/my_alloc.c: mark_blocks_free() mysys/tree.c: reset_tree() sql/item_sum.cc: in count distinct reset_tree instead of delete_tree --- include/my_sys.h | 6 ++++- include/my_tree.h | 3 +++ mysql-test/r/count_distinct2.result | 4 +++ mysql-test/t/count_distinct2.test | 3 +++ mysys/my_alloc.c | 41 ++++++++++++++++++++++++++++- mysys/tree.c | 18 ++++++++++--- sql/item_sum.cc | 2 +- 7 files changed, 71 insertions(+), 6 deletions(-) diff --git a/include/my_sys.h b/include/my_sys.h index 44faddad405..b5d59d2e801 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -108,7 +108,11 @@ extern int NEAR my_errno; /* Last error in mysys */ /* root_alloc flags */ #define MY_KEEP_PREALLOC 1 - +#define MY_MARK_BLOCKS_FREE 2 /* do not my_free() blocks, + just move used into free list + and mark all blocks as fully free + */ + /* defines when allocating data */ #ifdef SAFEMALLOC diff --git a/include/my_tree.h b/include/my_tree.h index d4d28644a39..be95e3ff4d2 100644 --- a/include/my_tree.h +++ b/include/my_tree.h @@ -62,6 +62,9 @@ void init_tree(TREE *tree,uint default_alloc_size, int element_size, qsort_cmp2 compare, my_bool with_delete, void (*free_element)(void*)); void delete_tree(TREE*); +void reset_tree(TREE*); + /* similar to delete tree, except we do not my_free() blocks in mem_root + */ #define is_tree_inited(tree) ((tree)->root != 0) /* Functions on leafs */ diff --git a/mysql-test/r/count_distinct2.result b/mysql-test/r/count_distinct2.result index b2bcf11a37c..b8330835332 100644 --- a/mysql-test/r/count_distinct2.result +++ b/mysql-test/r/count_distinct2.result @@ -70,3 +70,7 @@ count(distinct s,t) 5 count(distinct n1) count(distinct n2) 2 3 +count(distinct n2) n1 +1 NULL +1 1 +3 2 diff --git a/mysql-test/t/count_distinct2.test b/mysql-test/t/count_distinct2.test index cfdce77622c..5ddd96198fe 100644 --- a/mysql-test/t/count_distinct2.test +++ b/mysql-test/t/count_distinct2.test @@ -40,3 +40,6 @@ select distinct s,t from t1; select count(distinct s,t) from t1; select count(distinct n1), count(distinct n2) from t1; + +select count(distinct n2), n1 from t1 group by n1; +drop table t1; diff --git a/mysys/my_alloc.c b/mysys/my_alloc.c index db482454e69..b82ff965dfb 100644 --- a/mysys/my_alloc.c +++ b/mysys/my_alloc.c @@ -100,7 +100,41 @@ gptr alloc_root(MEM_ROOT *mem_root,unsigned int Size) #endif } - /* deallocate everything used by alloc_root */ +static inline void mark_blocks_free(MEM_ROOT* root) +{ + reg1 USED_MEM *next,*last = 0; + + /* iterate through (partially) free blocks, mark them fully free */ + for(next = root->free; next; next = next->next ) + { + last = next; + next->left = next->size - ALIGN_SIZE(sizeof(USED_MEM)); + } + /* if free block list was not empty, point the next of the + last free block to the beginning of the used list */ + next = root->used; /* a little optimization to avoid dereferencing root + twice - we will shortly start iterating through used + list */ + if(last) + last->next = next; + else /* if free list is empty, just point it to the current used*/ + root->free = next; + + /* now go through the current used list, and mark each block + as fully free. Note that because of our optimization, we do not + need to initialize next here - see above + */ + for(;next; next = next->next) + next->left = next->size - ALIGN_SIZE(sizeof(USED_MEM)); + + /* Now everything is set - we just need to indicate that nothing is used + anymore + */ + root->used = 0; +} + + /* deallocate everything used by alloc_root or just move + used blocks to free list if called with MY_USED_TO_FREE */ void free_root(MEM_ROOT *root, myf MyFlags) { @@ -109,6 +143,11 @@ void free_root(MEM_ROOT *root, myf MyFlags) if (!root) DBUG_VOID_RETURN; /* purecov: inspected */ + if(MyFlags & MY_MARK_BLOCKS_FREE) + { + mark_blocks_free(root); + DBUG_VOID_RETURN; + } if (!(MyFlags & MY_KEEP_PREALLOC)) root->pre_alloc=0; diff --git a/mysys/tree.c b/mysys/tree.c index e46ff00adad..af64be55d2f 100644 --- a/mysys/tree.c +++ b/mysys/tree.c @@ -103,9 +103,9 @@ void init_tree(TREE *tree, uint default_alloc_size, int size, DBUG_VOID_RETURN; } -void delete_tree(TREE *tree) +static void free_tree(TREE *tree, myf free_flags) { - DBUG_ENTER("delete_tree"); + DBUG_ENTER("free_tree"); DBUG_PRINT("enter",("tree: %lx",tree)); if (tree->root) /* If initialized */ @@ -116,7 +116,7 @@ void delete_tree(TREE *tree) { if (tree->free) delete_tree_element(tree,tree->root); - free_root(&tree->mem_root,MYF(0)); + free_root(&tree->mem_root, free_flags); } } tree->root= &tree->null_element; @@ -125,6 +125,18 @@ void delete_tree(TREE *tree) DBUG_VOID_RETURN; } +void delete_tree(TREE* tree) +{ + free_tree(tree, MYF(0)); /* my_free() mem_root if applicable */ +} + +void reset_tree(TREE* tree) +{ + free_tree(tree, MYF(MY_MARK_BLOCKS_FREE)); + /* do not my_free() mem_root if applicable, just mark blocks as free */ +} + + static void delete_tree_element(TREE *tree, TREE_ELEMENT *element) { if (element != &tree->null_element) diff --git a/sql/item_sum.cc b/sql/item_sum.cc index c0fba1dd80d..ad1918e01e3 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -924,7 +924,7 @@ bool Item_sum_count_distinct::setup(THD *thd) void Item_sum_count_distinct::reset() { if(use_tree) - delete_tree(&tree); + reset_tree(&tree); else { table->file->extra(HA_EXTRA_NO_CACHE); From e3201ef24c625be3514e2d9e325cfef33055028f Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 12 May 2001 20:37:10 -0600 Subject: [PATCH 3/3] mysql-test/mysql-test-run.sh fixed typo mysql-test/mysql-test-run.sh: fixed typo --- mysql-test/mysql-test-run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/mysql-test-run.sh b/mysql-test/mysql-test-run.sh index 9216ae5a74f..33045911954 100644 --- a/mysql-test/mysql-test-run.sh +++ b/mysql-test/mysql-test-run.sh @@ -463,7 +463,7 @@ start_master() --core \ --tmpdir=$MYSQL_TMP_DIR \ --language=english \ - --innobase_data_file_path=ibdata1:50M \ + --innodb_data_file_path=ibdata1:50M \ $SMALL_SERVER \ $EXTRA_MASTER_OPT $EXTRA_MASTER_MYSQLD_OPT" fi