maria-developers team mailing list archive
-
maria-developers team
-
Mailing list archive
-
Message #12596
Re: 2be9b69f4ff: MDEV-23001 Precreate static Item_bool() to simplify code
Hi, Monty!
On Mar 25, Michael Widenius wrote:
> revision-id: 2be9b69f4ff (mariadb-10.5.2-510-g2be9b69f4ff)
> parent(s): cb545f11169
> author: Michael Widenius <michael.widenius@xxxxxxxxx>
> committer: Michael Widenius <michael.widenius@xxxxxxxxx>
> timestamp: 2021-03-24 14:05:15 +0200
> message:
>
> MDEV-23001 Precreate static Item_bool() to simplify code
>
> The following changes where done:
> - Create global Item: Item_false and Item_true
> - Replace all creation if 'FALSE' and 'TRUE' top level items used for
> WHERE/HAVING/ON clauses to use Item_false and Item_true.
>
> The benefit are:
> - No test needed if we where able to create the new item.
> - Fixed possible errors if 'new' would have failed for the Item_bool's
agree
> - Less and faster code
I don't quite agree with that, it's not less code:
> 7 files changed, 65 insertions(+), 43 deletions(-)
and I doubt that speedup can be measured in any benchmarks whatsoever.
> diff --git a/sql/item.cc b/sql/item.cc
> index 1a86b8b3114..0f160fa257b 100644
> --- a/sql/item.cc
> +++ b/sql/item.cc
> @@ -55,7 +55,8 @@ const char *item_empty_name="";
> const char *item_used_name= "\0";
>
> static int save_field_in_field(Field *, bool *, Field *, bool);
> -
> +const Item_bool_static Item_false("FALSE", 0);
> +const Item_bool_static Item_true("TRUE", 1);
The main question - we have tons of modifiable Item members. How do you
know none of them will modified in your Item_bool_static objects?
> @@ -421,6 +421,9 @@ Item::Item(THD *thd):
> /* Initially this item is not attached to any JOIN_TAB. */
> join_tab_idx= MAX_TABLES;
>
> + /* thd is NULL in case of static items like Item_true */
> + if (!thd)
> + return;
No, please, don't.
thd can be NULL only twice, for statically initialized Item_true and
Item_false. For that you removed a useful assert and added an if() that
is completely unnecessary for billions of items created runtime.
Better to have special contructor just for these two very special items.
> /* Put item in free list so that we can free all items at end */
> next= thd->free_list;
> thd->free_list= this;
> @@ -3637,6 +3641,19 @@ void Item_int::print(String *str, enum_query_type query_type)
> }
>
>
> +/*
> + This function is needed to ensure that Item_bool_static doesn't change
> + the value of the member str_value.
> +*/
> +
> +void Item_bool::print(String *str, enum_query_type query_type)
> +{
> + // my_charset_bin is good enough for numbers
> + String tmp(value ? (char*) "1" : (char*) "0" , 1, &my_charset_bin);
> + str->append(tmp);
> +}
This is not needed anymore. Item_bool inherits from Item_int,
and Item_int::print is
void Item_int::print(String *str, enum_query_type query_type)
{
StringBuffer<LONGLONG_BUFFER_SIZE> buf;
// my_charset_bin is good enough for numbers
buf.set_int(value, unsigned_flag, &my_charset_bin);
str->append(buf);
}
You removed str_value from Item_int::print in July.
> Item *Item_bool::neg_transformer(THD *thd)
> {
> value= !value;
> diff --git a/sql/item.h b/sql/item.h
> index a1c288ab1f0..1087c08869e 100644
> --- a/sql/item.h
> +++ b/sql/item.h
> @@ -1976,7 +1976,7 @@ class Item: public Value_source,
> virtual bool limit_index_condition_pushdown_processor(void *arg) { return 0; }
> virtual bool exists2in_processor(void *arg) { return 0; }
> virtual bool find_selective_predicates_list_processor(void *arg) { return 0; }
> - bool cleanup_is_expensive_cache_processor(void *arg)
> + virtual bool cleanup_is_expensive_cache_processor(void *arg)
You've added a new virtual method? First you try so hard to remove
virtual methods, then you add a new one specifically for these two
static items. Why does it matter what
cleanup_is_expensive_cache_processor will do with them? You overwrite
is_expensive() anyway.
> {
> is_expensive_cache= (int8)(-1);
> return 0;
> @@ -3220,9 +3220,11 @@ class Item_literal: public Item_basic_constant
> Item_literal(THD *thd): Item_basic_constant(thd)
> { }
> Type type() const override { return CONST_ITEM; }
> - bool check_partition_func_processor(void *) override { return false;}
> + bool check_partition_func_processor(void *int_arg) override { return false;}
> bool const_item() const override { return true; }
> bool basic_const_item() const override { return true; }
> + bool is_expensive() override { return false; }
> + bool cleanup_is_expensive_cache_processor(void *arg) override { return 0; }
> };
>
>
> diff --git a/sql/opt_index_cond_pushdown.cc b/sql/opt_index_cond_pushdown.cc
> index 15bc2074e1f..e950ad1b7ca 100644
> --- a/sql/opt_index_cond_pushdown.cc
> +++ b/sql/opt_index_cond_pushdown.cc
> @@ -185,8 +185,8 @@ bool uses_index_fields_only(Item *item, TABLE *tbl, uint keyno,
> static Item *make_cond_for_index(THD *thd, Item *cond, TABLE *table, uint keyno,
> bool other_tbls_ok)
> {
> - if (!cond)
> - return NULL;
> + if (!cond || cond->basic_const_item())
> + return cond;
The effect of this change is that you don't set
cond->marker= ICP_COND_USES_INDEX_ONLY for basic_const_item's anymore.
This looked suspicious at first. But then I noticed (and Sergey Petrunia
confirmed) that ICP_COND_USES_INDEX_ONLY is not used at all. Since 5.3.
So here I'd suggest to revert this your change and completely remove any
ICP_COND_USES_INDEX_ONLY manipulations together with the
ICP_COND_USES_INDEX_ONLY definition.
> if (cond->type() == Item::COND_ITEM)
> {
> uint n_marked= 0;
> diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
> index 119c7360f07..e63e120a02b 100644
> --- a/sql/sql_parse.cc
> +++ b/sql/sql_parse.cc
> @@ -2395,6 +2395,7 @@ dispatch_command_return dispatch_command(enum enum_server_command command, THD *
> thd->update_server_status();
> thd->protocol->end_statement();
> query_cache_end_of_result(thd);
> + /* Check that the query didn't change const items */
what does that mean? Is it a TODO comment? Or it documents what happens
below?
> }
> if (drop_more_results)
> thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
> diff --git a/sql/sql_select.cc b/sql/sql_select.cc
> index 93f5d3591ed..894d511adf4 100644
> --- a/sql/sql_select.cc
> +++ b/sql/sql_select.cc
> @@ -2364,7 +2364,7 @@ int JOIN::optimize_stage2()
> if (!conds && outer_join)
> {
> /* Handle the case where we have an OUTER JOIN without a WHERE */
> - conds= new (thd->mem_root) Item_bool(thd, true); // Always true
> + conds= (Item*) &Item_true;
Hmm, does casting const away even work in the Intel compiler?
> }
>
> if (impossible_where)
> @@ -22743,6 +22743,8 @@ make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond,
> return new_cond;
> }
> }
> + else if (cond->basic_const_item())
> + return cond;
>
> if (is_top_and_level && used_table == rand_table_bit &&
> (cond->used_tables() & ~OUTER_REF_TABLE_BIT) != rand_table_bit)
this is rather fragile, isn't it?
You skip item->set_join_tab_idx(join_tab_idx_arg),
quite understandably.
And currently, as far as I can see, item->get_join_tab_idx()
is always used under if (!item->const_item()), so what you did is safe.
But can it change in the future? Or is join_tab_idx something that
conceptually doesn't make sense for const items?
Regards,
Sergei
VP of MariaDB Server Engineering
and security@xxxxxxxxxxx
Follow ups