Skip to content

Commit 0167904

Browse files
committed
MDEV-9764: MariaDB does not limit memory used for range optimization
A partial backport of 67f21fb3a077dedfd14b9ca720e926c55e682f93, Bug#22283790: RANGE OPTIMIZER UTILIZES TOO MUCH MEMORY WITH MANY OR CONDITIONS The backported part changes SEL_TREE::keys from being an array of MAX_KEY elements (64*8=512 bytes) to a Mem_root_array<SEL_ARG*> (32 bytes + alloc'ed array of as many elements as we need). The patch doesn't fix the "not limiting memory" part, but the memory usage is much lower with it.
1 parent bc54622 commit 0167904

File tree

2 files changed

+116
-38
lines changed

2 files changed

+116
-38
lines changed

sql/mem_root_array.h

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,21 @@ template<typename Element_type, bool has_trivial_destructor>
4747
class Mem_root_array
4848
{
4949
public:
50+
/// Convenience typedef, same typedef name as std::vector
51+
typedef Element_type value_type;
52+
5053
Mem_root_array(MEM_ROOT *root)
5154
: m_root(root), m_array(NULL), m_size(0), m_capacity(0)
5255
{
5356
DBUG_ASSERT(m_root != NULL);
5457
}
5558

59+
Mem_root_array(MEM_ROOT *root, size_t n, const value_type &val= value_type())
60+
: m_root(root), m_array(NULL), m_size(0), m_capacity(0)
61+
{
62+
resize(n, val);
63+
}
64+
5665
~Mem_root_array()
5766
{
5867
clear();
@@ -70,6 +79,12 @@ class Mem_root_array
7079
return m_array[n];
7180
}
7281

82+
Element_type &operator[](size_t n) { return at(n); }
83+
const Element_type &operator[](size_t n) const { return at(n); }
84+
85+
Element_type &back() { return at(size() - 1); }
86+
const Element_type &back() const { return at(size() - 1); }
87+
7388
// Returns a pointer to the first element in the array.
7489
Element_type *begin() { return &m_array[0]; }
7590

@@ -155,6 +170,58 @@ class Mem_root_array
155170
return false;
156171
}
157172

173+
/**
174+
Removes the last element in the array, effectively reducing the
175+
container size by one. This destroys the removed element.
176+
*/
177+
void pop_back()
178+
{
179+
DBUG_ASSERT(!empty());
180+
if (!has_trivial_destructor)
181+
back().~Element_type();
182+
m_size-= 1;
183+
}
184+
185+
/**
186+
Resizes the container so that it contains n elements.
187+
188+
If n is smaller than the current container size, the content is
189+
reduced to its first n elements, removing those beyond (and
190+
destroying them).
191+
192+
If n is greater than the current container size, the content is
193+
expanded by inserting at the end as many elements as needed to
194+
reach a size of n. If val is specified, the new elements are
195+
initialized as copies of val, otherwise, they are
196+
value-initialized.
197+
198+
If n is also greater than the current container capacity, an automatic
199+
reallocation of the allocated storage space takes place.
200+
201+
Notice that this function changes the actual content of the
202+
container by inserting or erasing elements from it.
203+
*/
204+
void resize(size_t n, const value_type &val= value_type())
205+
{
206+
if (n == m_size)
207+
return;
208+
if (n > m_size)
209+
{
210+
if (!reserve(n))
211+
{
212+
while (n != m_size)
213+
push_back(val);
214+
}
215+
return;
216+
}
217+
if (!has_trivial_destructor)
218+
{
219+
while (n != m_size)
220+
pop_back();
221+
}
222+
m_size= n;
223+
}
224+
158225
size_t capacity() const { return m_capacity; }
159226
size_t element_size() const { return sizeof(Element_type); }
160227
bool empty() const { return size() == 0; }

sql/opt_range.cc

Lines changed: 49 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -256,20 +256,28 @@ class SEL_TREE :public Sql_alloc
256256
(type == SEL_TREE::IMPOSSIBLE)
257257
*/
258258
enum Type { IMPOSSIBLE, ALWAYS, MAYBE, KEY, KEY_SMALLER } type;
259-
SEL_TREE(enum Type type_arg) :type(type_arg) {}
260-
SEL_TREE() :type(KEY)
259+
260+
SEL_TREE(enum Type type_arg, MEM_ROOT *root, size_t num_keys)
261+
: type(type_arg), keys(root, num_keys), n_ror_scans(0)
261262
{
262263
keys_map.clear_all();
263-
bzero((char*) keys,sizeof(keys));
264264
}
265+
266+
SEL_TREE(MEM_ROOT *root, size_t num_keys) :
267+
type(KEY), keys(root, num_keys), n_ror_scans(0)
268+
{
269+
keys_map.clear_all();
270+
}
271+
265272
SEL_TREE(SEL_TREE *arg, bool without_merges, RANGE_OPT_PARAM *param);
266273
/*
267274
Note: there may exist SEL_TREE objects with sel_tree->type=KEY and
268275
keys[i]=0 for all i. (SergeyP: it is not clear whether there is any
269276
merit in range analyzer functions (e.g. get_mm_parts) returning a
270277
pointer to such SEL_TREE instead of NULL)
271278
*/
272-
SEL_ARG *keys[MAX_KEY];
279+
Mem_root_array<SEL_ARG *, true> keys;
280+
273281
key_map keys_map; /* bitmask of non-NULL elements in keys */
274282

275283
/*
@@ -579,7 +587,7 @@ int SEL_IMERGE::and_sel_tree(RANGE_OPT_PARAM *param, SEL_TREE *tree,
579587
{
580588
SEL_TREE *res_or_tree= 0;
581589
SEL_TREE *and_tree= 0;
582-
if (!(res_or_tree= new SEL_TREE()) ||
590+
if (!(res_or_tree= new SEL_TREE(param->mem_root, param->keys)) ||
583591
!(and_tree= new SEL_TREE(tree, TRUE, param)))
584592
return (-1);
585593
if (!and_range_trees(param, *or_tree, and_tree, res_or_tree))
@@ -788,7 +796,10 @@ int SEL_IMERGE::or_sel_imerge_with_checks(RANGE_OPT_PARAM *param,
788796
*/
789797

790798
SEL_TREE::SEL_TREE(SEL_TREE *arg, bool without_merges,
791-
RANGE_OPT_PARAM *param): Sql_alloc()
799+
RANGE_OPT_PARAM *param)
800+
: Sql_alloc(),
801+
keys(param->mem_root, param->keys),
802+
n_ror_scans(0)
792803
{
793804
keys_map= arg->keys_map;
794805
type= arg->type;
@@ -3020,9 +3031,7 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
30203031
PARAM param;
30213032
MEM_ROOT alloc;
30223033
SEL_TREE *tree;
3023-
SEL_ARG **key, **end;
30243034
double rows;
3025-
uint idx= 0;
30263035

30273036
init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0,
30283037
MYF(MY_THREAD_SPECIFIC));
@@ -3067,22 +3076,23 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
30673076
goto free_alloc;
30683077
}
30693078

3070-
for (key= tree->keys, end= key + param.keys; key != end; key++, idx++)
3079+
for (uint idx= 0; idx < param.keys; idx++)
30713080
{
3072-
if (*key)
3081+
SEL_ARG *key= tree->keys[idx];
3082+
if (key)
30733083
{
3074-
if ((*key)->type == SEL_ARG::IMPOSSIBLE)
3084+
if (key->type == SEL_ARG::IMPOSSIBLE)
30753085
{
30763086
rows= 0;
30773087
table->reginfo.impossible_range= 1;
30783088
goto free_alloc;
30793089
}
30803090
else
30813091
{
3082-
rows= records_in_column_ranges(&param, idx, *key);
3092+
rows= records_in_column_ranges(&param, idx, key);
30833093
if (rows != HA_POS_ERROR)
3084-
(*key)->field->cond_selectivity= rows/table_records;
3085-
}
3094+
key->field->cond_selectivity= rows/table_records;
3095+
}
30863096
}
30873097
}
30883098

@@ -4947,8 +4957,8 @@ TABLE_READ_PLAN *merge_same_index_scans(PARAM *param, SEL_IMERGE *imerge,
49474957
{
49484958
SEL_TREE **changed_tree= imerge->trees+(*tree_idx_ptr-1);
49494959
SEL_ARG *key= (*changed_tree)->keys[key_idx];
4950-
bzero((*changed_tree)->keys,
4951-
sizeof((*changed_tree)->keys[0])*param->keys);
4960+
for (uint i= 0; i < param->keys; i++)
4961+
(*changed_tree)->keys[i]= NULL;
49524962
(*changed_tree)->keys_map.clear_all();
49534963
if (key)
49544964
key->incr_refs();
@@ -6725,8 +6735,8 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
67256735
bool update_tbl_stats,
67266736
double read_time)
67276737
{
6728-
uint idx;
6729-
SEL_ARG **key,**end, **key_to_read= NULL;
6738+
uint idx, best_idx;
6739+
SEL_ARG *key_to_read= NULL;
67306740
ha_rows UNINIT_VAR(best_records); /* protected by key_to_read */
67316741
uint UNINIT_VAR(best_mrr_flags), /* protected by key_to_read */
67326742
UNINIT_VAR(best_buf_size); /* protected by key_to_read */
@@ -6749,24 +6759,25 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
67496759
sizeof(INDEX_SCAN_INFO *) * param->keys);
67506760
}
67516761
tree->index_scans_end= tree->index_scans;
6752-
for (idx= 0,key=tree->keys, end=key+param->keys; key != end; key++,idx++)
6762+
for (idx= 0; idx < param->keys; idx++)
67536763
{
6754-
if (*key)
6764+
SEL_ARG *key= tree->keys[idx];
6765+
if (key)
67556766
{
67566767
ha_rows found_records;
67576768
Cost_estimate cost;
67586769
double found_read_time;
67596770
uint mrr_flags, buf_size;
67606771
INDEX_SCAN_INFO *index_scan;
67616772
uint keynr= param->real_keynr[idx];
6762-
if ((*key)->type == SEL_ARG::MAYBE_KEY ||
6763-
(*key)->maybe_flag)
6773+
if (key->type == SEL_ARG::MAYBE_KEY ||
6774+
key->maybe_flag)
67646775
param->needed_reg->set_bit(keynr);
67656776

67666777
bool read_index_only= index_read_must_be_used ? TRUE :
67676778
(bool) param->table->covering_keys.is_set(keynr);
67686779

6769-
found_records= check_quick_select(param, idx, read_index_only, *key,
6780+
found_records= check_quick_select(param, idx, read_index_only, key,
67706781
update_tbl_stats, &mrr_flags,
67716782
&buf_size, &cost);
67726783

@@ -6780,7 +6791,7 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
67806791
index_scan->used_key_parts= param->max_key_part+1;
67816792
index_scan->range_count= param->range_count;
67826793
index_scan->records= found_records;
6783-
index_scan->sel_arg= *key;
6794+
index_scan->sel_arg= key;
67846795
*tree->index_scans_end++= index_scan;
67856796
}
67866797
if ((found_records != HA_POS_ERROR) && param->is_ror_scan)
@@ -6794,6 +6805,7 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
67946805
read_time= found_read_time;
67956806
best_records= found_records;
67966807
key_to_read= key;
6808+
best_idx= idx;
67976809
best_mrr_flags= mrr_flags;
67986810
best_buf_size= buf_size;
67996811
}
@@ -6804,17 +6816,16 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
68046816
"ROR scans"););
68056817
if (key_to_read)
68066818
{
6807-
idx= key_to_read - tree->keys;
6808-
if ((read_plan= new (param->mem_root) TRP_RANGE(*key_to_read, idx,
6819+
if ((read_plan= new (param->mem_root) TRP_RANGE(key_to_read, best_idx,
68096820
best_mrr_flags)))
68106821
{
68116822
read_plan->records= best_records;
6812-
read_plan->is_ror= tree->ror_scans_map.is_set(idx);
6823+
read_plan->is_ror= tree->ror_scans_map.is_set(best_idx);
68136824
read_plan->read_cost= read_time;
68146825
read_plan->mrr_buf_size= best_buf_size;
68156826
DBUG_PRINT("info",
68166827
("Returning range plan for key %s, cost %g, records %lu",
6817-
param->table->key_info[param->real_keynr[idx]].name,
6828+
param->table->key_info[param->real_keynr[best_idx]].name,
68186829
read_plan->read_cost, (ulong) read_plan->records));
68196830
}
68206831
}
@@ -7442,9 +7453,11 @@ SEL_TREE *Item::get_mm_tree_for_const(RANGE_OPT_PARAM *param)
74427453
MEM_ROOT *tmp_root= param->mem_root;
74437454
param->thd->mem_root= param->old_root;
74447455
SEL_TREE *tree;
7445-
tree= val_int() ? new(tmp_root) SEL_TREE(SEL_TREE::ALWAYS) :
7446-
new(tmp_root) SEL_TREE(SEL_TREE::IMPOSSIBLE);
7456+
7457+
const SEL_TREE::Type type= val_int()? SEL_TREE::ALWAYS: SEL_TREE::IMPOSSIBLE;
74477458
param->thd->mem_root= tmp_root;
7459+
7460+
tree= new (tmp_root) SEL_TREE(type, tmp_root, param->keys);
74487461
DBUG_RETURN(tree);
74497462
}
74507463

@@ -7470,7 +7483,8 @@ SEL_TREE *Item::get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr)
74707483
if ((ref_tables & param->current_table) ||
74717484
(ref_tables & ~(param->prev_tables | param->read_tables)))
74727485
DBUG_RETURN(0);
7473-
DBUG_RETURN(new SEL_TREE(SEL_TREE::MAYBE));
7486+
DBUG_RETURN(new (param->mem_root) SEL_TREE(SEL_TREE::MAYBE, param->mem_root,
7487+
param->keys));
74747488
}
74757489

74767490

@@ -7588,7 +7602,8 @@ Item_bool_func::get_mm_parts(RANGE_OPT_PARAM *param, Field *field,
75887602
if (field->eq(key_part->field))
75897603
{
75907604
SEL_ARG *sel_arg=0;
7591-
if (!tree && !(tree=new (param->thd->mem_root) SEL_TREE()))
7605+
if (!tree && !(tree=new (param->thd->mem_root) SEL_TREE(param->mem_root,
7606+
param->keys)))
75927607
DBUG_RETURN(0); // OOM
75937608
if (!value || !(value_used_tables & ~param->read_tables))
75947609
{
@@ -8551,7 +8566,7 @@ tree_or(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
85518566
imerge[0]= new SEL_IMERGE(tree1->merges.head(), 0, param);
85528567
}
85538568
bool no_imerge_from_ranges= FALSE;
8554-
if (!(result= new SEL_TREE()))
8569+
if (!(result= new (param->mem_root) SEL_TREE(param->mem_root, param->keys)))
85558570
DBUG_RETURN(result);
85568571

85578572
/* Build the range part of the tree for the formula (1) */
@@ -14382,16 +14397,12 @@ void QUICK_GROUP_MIN_MAX_SELECT::add_keys_and_lengths(String *key_names,
1438214397
static void print_sel_tree(PARAM *param, SEL_TREE *tree, key_map *tree_map,
1438314398
const char *msg)
1438414399
{
14385-
SEL_ARG **key,**end;
14386-
int idx;
1438714400
char buff[1024];
1438814401
DBUG_ENTER("print_sel_tree");
1438914402

1439014403
String tmp(buff,sizeof(buff),&my_charset_bin);
1439114404
tmp.length(0);
14392-
for (idx= 0,key=tree->keys, end=key+param->keys ;
14393-
key != end ;
14394-
key++,idx++)
14405+
for (uint idx= 0; idx < param->keys; idx++)
1439514406
{
1439614407
if (tree_map->is_set(idx))
1439714408
{

0 commit comments

Comments
 (0)