summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/my_sys.h6
-rw-r--r--include/my_tree.h3
-rw-r--r--mysql-test/r/count_distinct2.result4
-rw-r--r--mysql-test/t/count_distinct2.test3
-rw-r--r--mysys/my_alloc.c41
-rw-r--r--mysys/tree.c18
-rw-r--r--sql/item_sum.cc2
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);