1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-27 18:02:13 +03:00

MDEV-16309 Split ::create_tmp_field() into virtual methods in Item

Detailed: changes:
1. Moving Field specific code into new methods on Field:
   - Field *Field::create_tmp_field(...)
   - virtual void init_for_tmp_table(...)

2. Removing virtual Item::create_tmp_field().
   Adding instead a new virtual method Item::create_tmp_field_ex().

   Note, a virtual create_tmp_field() still exists, but only for Item_sum.
   This resembles 10.0 code structure. Perhaps create_tmp_field() should
   be removed from Item_sum and Item_sum descendants should override
   create_tmp_field_ex() directly. This can be done in a separate commit.

3. Adding helper classes Tmp_field_src and Tmp_field_param,
   to make the API for Item::create_tmp_field_ex() smaller
   and easier to extend in the future.

4. Decomposing the public function create_tmp_field() into
   virtual implementations for Item and a number of its descendants:
   - Item_basic_value
   - Item_sp_variable
   - Item_name_const
   - Item_result_field
   - Item_field
   - Item_ref
   - Item_type_holder
   - Item_row
   - Item_func_sp
   - Item_func_user_var
   - Item_sum
   - Item_sum_field
   - Item_proc

5. Adding DBUG_ASSERT-only virtual implementations for
   Item types that should not appear in create_tmp_table_ex(),
   for easier debugging:
   - Item_nodeset_func
   - Item_nodeset_to_const_comparator
   - Item_null_result
   - Item_copy
   - Item_ident_for_show
   - Item_user_var_as_out_param

6. Moving public function create_tmp_field_from_field()
   as a method to Item_field.

7. Removing Item::set_result_field(). It's not needed any more.

8. Cleanup: Removing the enum value "EXPR_CACHE_ITEM",
   as it's not used for a very long time.
This commit is contained in:
Alexander Barkov
2018-05-28 16:57:59 +04:00
parent 13f7ac2269
commit 637af78383
16 changed files with 611 additions and 310 deletions

View File

@ -16525,60 +16525,6 @@ const_expression_in_where(COND *cond, Item *comp_item, Field *comp_field,
Create internal temporary table
****************************************************************************/
/**
Create field for temporary table from given field.
@param thd Thread handler
@param org_field field from which new field will be created
@param name New field name
@param table Temporary table
@param item !=NULL if item->result_field should point to new field.
This is relevant for how fill_record() is going to work:
If item != NULL then fill_record() will update
the record in the original table.
If item == NULL then fill_record() will update
the temporary table
@retval
NULL on error
@retval
new_created field
*/
Field *create_tmp_field_from_field(THD *thd, Field *org_field,
LEX_CSTRING *name, TABLE *table,
Item_field *item)
{
Field *new_field;
new_field= org_field->make_new_field(thd->mem_root, table,
table == org_field->table);
if (new_field)
{
new_field->init(table);
new_field->orig_table= org_field->orig_table;
if (item)
item->result_field= new_field;
else
new_field->field_name= *name;
new_field->flags|= org_field->flags & NO_DEFAULT_VALUE_FLAG;
if (org_field->maybe_null() || (item && item->maybe_null))
new_field->flags&= ~NOT_NULL_FLAG; // Because of outer join
if (org_field->type() == MYSQL_TYPE_VAR_STRING ||
org_field->type() == MYSQL_TYPE_VARCHAR)
table->s->db_create_options|= HA_OPTION_PACK_RECORD;
else if (org_field->type() == FIELD_TYPE_DOUBLE)
((Field_double *) new_field)->not_fixed= TRUE;
new_field->vcol_info= 0;
new_field->cond_selectivity= 1.0;
new_field->next_equal_field= NULL;
new_field->option_list= NULL;
new_field->option_struct= NULL;
}
return new_field;
}
Field *Item::create_tmp_field_int(TABLE *table, uint convert_int_length)
{
const Type_handler *h= &type_handler_long;
@ -16619,59 +16565,6 @@ Field *Item_sum::create_tmp_field(bool group, TABLE *table)
}
static void create_tmp_field_from_item_finalize(THD *thd,
Field *new_field,
Item *item,
Item ***copy_func,
bool modify_item)
{
if (copy_func &&
(item->is_result_field() ||
(item->real_item()->is_result_field())))
*((*copy_func)++) = item; // Save for copy_funcs
if (modify_item)
item->set_result_field(new_field);
if (item->type() == Item::NULL_ITEM)
new_field->is_created_from_null_item= TRUE;
}
/**
Create field for temporary table using type of given item.
@param thd Thread handler
@param item Item to create a field for
@param table Temporary table
@param copy_func If set and item is a function, store copy of
item in this array
@param modify_item 1 if item->result_field should point to new
item. This is relevent for how fill_record()
is going to work:
If modify_item is 1 then fill_record() will
update the record in the original table.
If modify_item is 0 then fill_record() will
update the temporary table
@param convert_blob_length If >0 create a varstring(convert_blob_length)
field instead of blob.
@retval
0 on error
@retval
new_created field
*/
static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table,
Item ***copy_func, bool modify_item)
{
Field *UNINIT_VAR(new_field);
DBUG_ASSERT(thd == table->in_use);
if ((new_field= item->create_tmp_field(false, table)))
create_tmp_field_from_item_finalize(thd, new_field, item,
copy_func, modify_item);
return new_field;
}
/**
Create field for information schema table.
@ -16708,20 +16601,207 @@ Field *Item::create_field_for_schema(THD *thd, TABLE *table)
}
/**
Create a temporary field for Item_field (or its descendant),
either direct or referenced by an Item_ref.
*/
Field *
Item_field::create_tmp_field_from_item_field(TABLE *new_table,
Item_ref *orig_item,
const Tmp_field_param *param)
{
DBUG_ASSERT(!is_result_field());
Field *result;
/*
If item have to be able to store NULLs but underlaid field can't do it,
create_tmp_field_from_field() can't be used for tmp field creation.
*/
if (((maybe_null && in_rollup) ||
(new_table->in_use->create_tmp_table_for_derived && /* for mat. view/dt */
orig_item && orig_item->maybe_null)) &&
!field->maybe_null())
{
/*
The item the ref points to may have maybe_null flag set while
the ref doesn't have it. This may happen for outer fields
when the outer query decided at some point after name resolution phase
that this field might be null. Take this into account here.
*/
Record_addr rec(orig_item ? orig_item->maybe_null : maybe_null);
const Type_handler *handler= type_handler()->
type_handler_for_tmp_table(this);
result= handler->make_and_init_table_field(&name, rec, *this, new_table);
}
else if (param->table_cant_handle_bit_fields() &&
field->type() == MYSQL_TYPE_BIT)
{
const Type_handler *handler= type_handler_long_or_longlong();
result= handler->make_and_init_table_field(&name,
Record_addr(maybe_null),
*this, new_table);
}
else
{
LEX_CSTRING *tmp= orig_item ? &orig_item->name : &name;
bool tmp_maybe_null= param->modify_item() ? maybe_null :
field->maybe_null();
result= field->create_tmp_field(new_table->in_use->mem_root, new_table,
tmp_maybe_null);
if (result)
result->field_name= *tmp;
}
if (result && param->modify_item())
result_field= result;
return result;
}
Field *Item_field::create_tmp_field_ex(TABLE *table,
Tmp_field_src *src,
const Tmp_field_param *param)
{
DBUG_ASSERT(!is_result_field());
Field *result;
src->set_field(field);
if (!(result= create_tmp_field_from_item_field(table, NULL, param)))
return NULL;
/*
Fields that are used as arguments to the DEFAULT() function already have
their data pointers set to the default value during name resolution. See
Item_default_value::fix_fields.
*/
if (type() != Item::DEFAULT_VALUE_ITEM && field->eq_def(result))
src->set_default_field(field);
return result;
}
Field *Item_ref::create_tmp_field_ex(TABLE *table,
Tmp_field_src *src,
const Tmp_field_param *param)
{
Item *item= real_item();
DBUG_ASSERT(is_result_field());
if (item->type() == Item::FIELD_ITEM)
{
Field *result;
Item_field *field= (Item_field*) item;
Tmp_field_param prm2(*param);
prm2.set_modify_item(false);
src->set_field(field->field);
if (!(result= field->create_tmp_field_from_item_field(table, this, &prm2)))
return NULL;
if (param->modify_item())
result_field= result;
return result;
}
return Item_result_field::create_tmp_field_ex(table, src, param);
}
void Item_result_field::get_tmp_field_src(Tmp_field_src *src,
const Tmp_field_param *param)
{
if (param->make_copy_field())
{
DBUG_ASSERT(result_field);
src->set_field(result_field);
}
else
{
src->set_item_result_field(this); // Save for copy_funcs
}
}
Field *Item_result_field::create_tmp_field_ex(TABLE *table,
Tmp_field_src *src,
const Tmp_field_param *param)
{
/*
Possible Item types:
- Item_cache_wrapper (only for CREATE..SELECT ?)
- Item_func
- Item_subselect
*/
DBUG_ASSERT(is_result_field());
DBUG_ASSERT(type() != NULL_ITEM);
get_tmp_field_src(src, param);
Field *result;
if ((result= tmp_table_field_from_field_type(table)) && param->modify_item())
result_field= result;
return result;
}
Field *Item_func_user_var::create_tmp_field_ex(TABLE *table,
Tmp_field_src *src,
const Tmp_field_param *param)
{
DBUG_ASSERT(is_result_field());
DBUG_ASSERT(type() != NULL_ITEM);
get_tmp_field_src(src, param);
Field *result;
if ((result= create_table_field_from_handler(table)) && param->modify_item())
result_field= result;
return result;
}
Field *Item_func_sp::create_tmp_field_ex(TABLE *table,
Tmp_field_src *src,
const Tmp_field_param *param)
{
Field *result;
get_tmp_field_src(src, param);
if ((result= sp_result_field->create_tmp_field(table->in_use->mem_root,
table)))
{
result->field_name= name;
if (param->modify_item())
result_field= result;
}
return result;
}
Field *Item_basic_value::create_tmp_field_ex(TABLE *table,
Tmp_field_src *src,
const Tmp_field_param *param)
{
/*
create_tmp_field_ex() for this type of Items is called for:
- CREATE TABLE ... SELECT
- In ORDER BY: SELECT max(a) FROM t1 GROUP BY a ORDER BY 'const';
- In CURSORS:
DECLARE c CURSOR FOR SELECT 'test';
OPEN c;
*/
DBUG_ASSERT(!param->make_copy_field());
DBUG_ASSERT(!is_result_field());
Field *result;
if ((result= tmp_table_field_from_field_type(table)))
{
if (type() == Item::NULL_ITEM) // Item_null or Item_param
result->is_created_from_null_item= true;
}
return result;
}
/**
Create field for temporary table.
@param thd Thread handler
@param table Temporary table
@param item Item to create a field for
@param type Type of item (normally item->type)
@param copy_func If set and item is a function, store copy of item
@param table Temporary table
@param item Item to create a field for
@param type Type of item (normally item->type)
@param copy_func If set and item is a function, store copy of item
in this array
@param from_field if field will be created using other field as example,
pointer example field will be written here
@param default_field If field has a default value field, store it here
@param group 1 if we are going to do a relative group by on result
@param modify_item 1 if item->result_field should point to new item.
@param default_field If field has a default value field, store it here
@param group 1 if we are going to do a relative group by on result
@param modify_item 1 if item->result_field should point to new item.
This is relevent for how fill_record() is going to
work:
If modify_item is 1 then fill_record() will update
@ -16730,172 +16810,28 @@ Field *Item::create_field_for_schema(THD *thd, TABLE *table)
the temporary table
@retval
0 on error
0 on error
@retval
new_created field
Create a temporary field for Item_field (or its descendant),
either direct or referenced by an Item_ref.
*/
Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
Field *create_tmp_field(TABLE *table, Item *item,
Item ***copy_func, Field **from_field,
Field **default_field,
bool group, bool modify_item,
bool table_cant_handle_bit_fields,
bool make_copy_field)
{
Field *result;
Item::Type orig_type= type;
Item *orig_item= 0;
DBUG_ASSERT(thd == table->in_use);
if (type != Item::FIELD_ITEM &&
item->real_item()->type() == Item::FIELD_ITEM)
{
orig_item= item;
item= item->real_item();
type= Item::FIELD_ITEM;
}
switch (type) {
case Item::TYPE_HOLDER:
case Item::SUM_FUNC_ITEM:
{
result= item->create_tmp_field(group, table);
if (!result)
my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
return result;
}
case Item::FIELD_ITEM:
case Item::DEFAULT_VALUE_ITEM:
case Item::INSERT_VALUE_ITEM:
case Item::TRIGGER_FIELD_ITEM:
{
Item_field *field= (Item_field*) item;
bool orig_modify= modify_item;
if (orig_type == Item::REF_ITEM)
modify_item= 0;
/*
If item have to be able to store NULLs but underlaid field can't do it,
create_tmp_field_from_field() can't be used for tmp field creation.
*/
if (((field->maybe_null && field->in_rollup) ||
(thd->create_tmp_table_for_derived && /* for mat. view/dt */
orig_item && orig_item->maybe_null)) &&
!field->field->maybe_null())
{
bool save_maybe_null= FALSE;
/*
The item the ref points to may have maybe_null flag set while
the ref doesn't have it. This may happen for outer fields
when the outer query decided at some point after name resolution phase
that this field might be null. Take this into account here.
*/
if (orig_item)
{
save_maybe_null= item->maybe_null;
item->maybe_null= orig_item->maybe_null;
}
result= create_tmp_field_from_item(thd, item, table, NULL,
modify_item);
*from_field= field->field;
if (result && modify_item)
field->result_field= result;
if (orig_item)
item->maybe_null= save_maybe_null;
}
else if (table_cant_handle_bit_fields && field->field->type() ==
MYSQL_TYPE_BIT)
{
const Type_handler *handler= item->type_handler_long_or_longlong();
*from_field= field->field;
if ((result=
handler->make_and_init_table_field(&item->name,
Record_addr(item->maybe_null),
*item, table)))
create_tmp_field_from_item_finalize(thd, result, item,
copy_func, modify_item);
if (result && modify_item)
field->result_field= result;
}
else
{
LEX_CSTRING *tmp= orig_item ? &orig_item->name : &item->name;
result= create_tmp_field_from_field(thd, (*from_field= field->field),
tmp, table,
modify_item ? field :
NULL);
}
if (orig_type == Item::REF_ITEM && orig_modify)
((Item_ref*)orig_item)->set_result_field(result);
/*
Fields that are used as arguments to the DEFAULT() function already have
their data pointers set to the default value during name resolution. See
Item_default_value::fix_fields.
*/
if (orig_type != Item::DEFAULT_VALUE_ITEM && field->field->eq_def(result))
*default_field= field->field;
return result;
}
/* Fall through */
case Item::FUNC_ITEM:
if (((Item_func *) item)->functype() == Item_func::FUNC_SP)
{
Item_func_sp *item_func_sp= (Item_func_sp *) item;
Field *sp_result_field= item_func_sp->get_sp_result_field();
if (make_copy_field)
{
DBUG_ASSERT(item_func_sp->result_field);
*from_field= item_func_sp->result_field;
}
else
{
*((*copy_func)++)= item;
}
Field *result_field=
create_tmp_field_from_field(thd,
sp_result_field,
&item_func_sp->name,
table,
NULL);
if (modify_item)
item->set_result_field(result_field);
return result_field;
}
/* Fall through */
case Item::COND_ITEM:
case Item::FIELD_AVG_ITEM:
case Item::FIELD_STD_ITEM:
case Item::SUBSELECT_ITEM:
/* The following can only happen with 'CREATE TABLE ... SELECT' */
case Item::PROC_ITEM:
case Item::INT_ITEM:
case Item::REAL_ITEM:
case Item::DECIMAL_ITEM:
case Item::STRING_ITEM:
case Item::DATE_ITEM:
case Item::REF_ITEM:
case Item::NULL_ITEM:
case Item::VARBIN_ITEM:
case Item::CACHE_ITEM:
case Item::WINDOW_FUNC_ITEM: // psergey-winfunc:
case Item::EXPR_CACHE_ITEM:
case Item::PARAM_ITEM:
if (make_copy_field)
{
DBUG_ASSERT(((Item_result_field*)item)->result_field);
*from_field= ((Item_result_field*)item)->result_field;
}
return create_tmp_field_from_item(thd, item, table,
(make_copy_field ? 0 : copy_func),
modify_item);
default: // Dosen't have to be stored
return 0;
}
Tmp_field_src src;
Tmp_field_param prm(group, modify_item, table_cant_handle_bit_fields,
make_copy_field);
Field *result= item->create_tmp_field_ex(table, &src, &prm);
*from_field= src.field();
*default_field= src.default_field();
if (src.item_result_field())
*((*copy_func)++)= src.item_result_field();
return result;
}
/*
@ -17202,7 +17138,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
{
Item *tmp_item;
Field *new_field=
create_tmp_field(thd, table, arg, arg->type(), &copy_func,
create_tmp_field(table, arg, &copy_func,
tmp_from_field, &default_field[fieldnr],
group != 0,not_all_columns,
distinct, false);
@ -17252,7 +17188,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
else
{
/*
The last parameter to create_tmp_field() is a bit tricky:
The last parameter to create_tmp_field_ex() is a bit tricky:
We need to set it to 0 in union, to get fill_record() to modify the
temporary table.
@ -17266,7 +17202,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
*/
Field *new_field= (param->schema_table) ?
item->create_field_for_schema(thd, table) :
create_tmp_field(thd, table, item, type, &copy_func,
create_tmp_field(table, item, &copy_func,
tmp_from_field, &default_field[fieldnr],
group != 0,
!force_copy_fields &&
@ -17280,7 +17216,6 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
*/
item->marker == 4 || param->bit_fields_as_long,
force_copy_fields);
if (!new_field)
{
if (thd->is_fatal_error)
@ -23466,7 +23401,7 @@ calc_group_buffer(JOIN *join,ORDER *group)
{
/*
Group strings are taken as varstrings and require an length field.
A field is not yet created by create_tmp_field()
A field is not yet created by create_tmp_field_ex()
and the sizes should match up.
*/
key_length+= group_item->max_length + HA_KEY_BLOB_LENGTH;