1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00

MDEV-20632: Recursive CTE cycle detection using CYCLE clause (nonstandard)

Added CYCLE ... RESTRICT (nonstandard) clause to recursive CTE.
This commit is contained in:
Oleksandr Byelkin
2020-01-27 21:50:16 +01:00
parent a5584b13d1
commit 50c0939166
20 changed files with 619 additions and 147 deletions

View File

@ -18023,14 +18023,27 @@ class Create_tmp_table: public Data_type_statistics
ORDER *m_group;
bool m_distinct;
bool m_save_sum_fields;
bool m_with_cycle;
ulonglong m_select_options;
ha_rows m_rows_limit;
uint m_hidden_field_count; // Remove this eventually
uint m_group_null_items;
uint m_null_count;
uint m_hidden_uneven_bit_length;
uint m_hidden_null_count;
// counter for distinct/other fields
uint m_field_count[2];
// counter for distinct/other fields which can be NULL
uint m_null_count[2];
// counter for distinct/other blob fields
uint m_blobs_count[2];
// counter for "tails" of bit fields which do not fit in a byte
uint m_uneven_bit[2];
public:
enum counter {distinct, other};
/*
shows which field we are processing: distinct/other (set in processing
cycles)
*/
counter current_counter;
Create_tmp_table(const TMP_TABLE_PARAM *param,
ORDER *group, bool distinct, bool save_sum_fields,
ulonglong select_options, ha_rows rows_limit)
@ -18040,14 +18053,21 @@ public:
m_group(group),
m_distinct(distinct),
m_save_sum_fields(save_sum_fields),
m_with_cycle(false),
m_select_options(select_options),
m_rows_limit(rows_limit),
m_hidden_field_count(param->hidden_field_count),
m_group_null_items(0),
m_null_count(0),
m_hidden_uneven_bit_length(0),
m_hidden_null_count(0)
{ }
current_counter(other)
{
m_field_count[Create_tmp_table::distinct]= 0;
m_field_count[Create_tmp_table::other]= 0;
m_null_count[Create_tmp_table::distinct]= 0;
m_null_count[Create_tmp_table::other]= 0;
m_blobs_count[Create_tmp_table::distinct]= 0;
m_blobs_count[Create_tmp_table::other]= 0;
m_uneven_bit[Create_tmp_table::distinct]= 0;
m_uneven_bit[Create_tmp_table::other]= 0;
}
void add_field(TABLE *table, Field *field, uint fieldnr, bool force_not_null_cols);
@ -18080,13 +18100,16 @@ void Create_tmp_table::add_field(TABLE *table, Field *field, uint fieldnr, bool
}
if (!(field->flags & NOT_NULL_FLAG))
m_null_count++;
m_null_count[current_counter]++;
table->s->reclength+= field->pack_length();
// Assign it here, before update_data_type_statistics() changes m_blob_count
if (field->flags & BLOB_FLAG)
{
table->s->blob_field[m_blob_count]= fieldnr;
m_blobs_count[current_counter]++;
}
table->field[fieldnr]= field;
field->field_index= fieldnr;
@ -18298,6 +18321,7 @@ bool Create_tmp_table::add_fields(THD *thd,
DBUG_ASSERT(table->s->blob_fields == 0);
const bool not_all_columns= !(m_select_options & TMP_TABLE_ALL_COLUMNS);
bool distinct_record_structure= m_distinct;
uint fieldnr= 0;
TABLE_SHARE *share= table->s;
Item **copy_func= param->items_to_copy;
@ -18308,8 +18332,29 @@ bool Create_tmp_table::add_fields(THD *thd,
List_iterator_fast<Item> li(fields);
Item *item;
Field **tmp_from_field= m_from_field;
uint uneven_delta;
while (!m_with_cycle && (item= li++))
if (item->common_flags & IS_IN_WITH_CYCLE)
{
m_with_cycle= true;
/*
Following distinct_record_structure is (m_distinct || m_with_cycle)
Note: distinct_record_structure can be true even if m_distinct is
false, for example for incr_table in recursive CTE
(see select_union_recursive::create_result_table)
*/
distinct_record_structure= true;
}
li.rewind();
while ((item=li++))
{
current_counter= (((param->hidden_field_count < (fieldnr + 1)) &&
distinct_record_structure &&
(!m_with_cycle ||
(item->common_flags & IS_IN_WITH_CYCLE)))?
distinct :
other);
Item::Type type= item->type();
if (type == Item::COPY_STR_ITEM)
{
@ -18334,7 +18379,8 @@ bool Create_tmp_table::add_fields(THD *thd,
continue;
}
}
if (item->const_item() && (int) m_hidden_field_count <= 0)
if (item->const_item() &&
param->hidden_field_count < (fieldnr + 1))
continue; // We don't have to store this
}
if (type == Item::SUM_FUNC_ITEM && !m_group && !m_save_sum_fields)
@ -18351,7 +18397,7 @@ bool Create_tmp_table::add_fields(THD *thd,
create_tmp_field(table, arg, &copy_func,
tmp_from_field, &m_default_field[fieldnr],
m_group != 0, not_all_columns,
m_distinct, false);
distinct_record_structure , false);
if (!new_field)
goto err; // Should be OOM
tmp_from_field++;
@ -18363,7 +18409,10 @@ bool Create_tmp_table::add_fields(THD *thd,
arg= sum_item->set_arg(i, thd, tmp_item);
thd->mem_root= &table->mem_root;
uneven_delta= m_uneven_bit_length;
add_field(table, new_field, fieldnr++, param->force_not_null_cols);
uneven_delta= m_uneven_bit_length - uneven_delta;
m_field_count[current_counter]++;
if (!(new_field->flags & NOT_NULL_FLAG))
{
@ -18373,6 +18422,8 @@ bool Create_tmp_table::add_fields(THD *thd,
*/
arg->maybe_null=1;
}
if (current_counter == distinct)
new_field->flags|= FIELD_PART_OF_TMP_UNIQUE;
}
}
}
@ -18440,36 +18491,23 @@ bool Create_tmp_table::add_fields(THD *thd,
}
tmp_from_field++;
uneven_delta= m_uneven_bit_length;
add_field(table, new_field, fieldnr++, param->force_not_null_cols);
uneven_delta= m_uneven_bit_length - uneven_delta;
m_field_count[current_counter]++;
if (item->marker == 4 && item->maybe_null)
{
m_group_null_items++;
new_field->flags|= GROUP_FLAG;
}
if (current_counter == distinct)
new_field->flags|= FIELD_PART_OF_TMP_UNIQUE;
}
if (!--m_hidden_field_count)
{
/*
This was the last hidden field; Remember how many hidden fields could
have null
*/
m_hidden_null_count= m_null_count;
/*
We need to update hidden_field_count as we may have stored group
functions with constant arguments
*/
param->hidden_field_count= fieldnr;
m_null_count= 0;
/*
On last hidden field we store uneven bit length in
m_hidden_uneven_bit_length and proceed calculation of
uneven bits for visible fields into m_uneven_bit_length.
*/
m_hidden_uneven_bit_length= m_uneven_bit_length;
m_uneven_bit_length= 0;
}
m_uneven_bit[current_counter]+= uneven_delta;
}
DBUG_ASSERT(fieldnr == m_field_count[other] + m_field_count[distinct]);
DBUG_ASSERT(m_blob_count == m_blobs_count[other] + m_blobs_count[distinct]);
share->fields= fieldnr;
share->blob_fields= m_blob_count;
table->field[fieldnr]= 0; // End marker
@ -18495,8 +18533,12 @@ bool Create_tmp_table::finalize(THD *thd,
DBUG_ENTER("Create_tmp_table::finalize");
DBUG_ASSERT(table);
uint hidden_null_pack_length;
uint null_pack_length;
uint null_pack_length[2];
uint null_pack_base[2];
uint null_counter[2]= {0, 0};
uint whole_null_pack_length;
bool use_packed_rows= false;
uchar *pos;
uchar *null_flags;
@ -18547,16 +18589,21 @@ bool Create_tmp_table::finalize(THD *thd,
if (share->blob_fields == 0)
{
/* We need to ensure that first byte is not 0 for the delete link */
if (param->hidden_field_count)
m_hidden_null_count++;
if (m_field_count[other])
m_null_count[other]++;
else
m_null_count++;
m_null_count[distinct]++;
}
hidden_null_pack_length= (m_hidden_null_count + 7 +
m_hidden_uneven_bit_length) / 8;
null_pack_length= (hidden_null_pack_length +
(m_null_count + m_uneven_bit_length + 7) / 8);
share->reclength+= null_pack_length;
null_pack_length[other]= (m_null_count[other] + 7 +
m_uneven_bit[other]) / 8;
null_pack_base[other]= 0;
null_pack_length[distinct]= (m_null_count[distinct] + 7 +
m_uneven_bit[distinct]) / 8;
null_pack_base[distinct]= null_pack_length[other];
whole_null_pack_length= null_pack_length[other] +
null_pack_length[distinct];
share->reclength+= whole_null_pack_length;
if (!share->reclength)
share->reclength= 1; // Dummy select
/* Use packed rows if there is blobs or a lot of space to gain */
@ -18580,43 +18627,53 @@ bool Create_tmp_table::finalize(THD *thd,
recinfo=param->start_recinfo;
null_flags=(uchar*) table->record[0];
pos=table->record[0]+ null_pack_length;
if (null_pack_length)
pos=table->record[0]+ whole_null_pack_length;
if (whole_null_pack_length)
{
bzero((uchar*) recinfo,sizeof(*recinfo));
recinfo->type=FIELD_NORMAL;
recinfo->length=null_pack_length;
recinfo->length= whole_null_pack_length;
recinfo++;
bfill(null_flags,null_pack_length,255); // Set null fields
bfill(null_flags, whole_null_pack_length, 255); // Set null fields
table->null_flags= (uchar*) table->record[0];
share->null_fields= m_null_count + m_hidden_null_count;
share->null_bytes= share->null_bytes_for_compare= null_pack_length;
share->null_fields= m_null_count[other] + m_null_count[distinct];
share->null_bytes= share->null_bytes_for_compare= whole_null_pack_length;
}
if (share->blob_fields == 0)
{
null_counter[(m_field_count[other] ? other : distinct)]++;
}
m_null_count= (share->blob_fields == 0) ? 1 : 0;
m_hidden_field_count= param->hidden_field_count;
for (uint i= 0; i < share->fields; i++, recinfo++)
{
Field *field= table->field[i];
uint length;
bzero((uchar*) recinfo,sizeof(*recinfo));
current_counter= ((field->flags & FIELD_PART_OF_TMP_UNIQUE) ?
distinct :
other);
if (!(field->flags & NOT_NULL_FLAG))
{
recinfo->null_bit= (uint8)1 << (m_null_count & 7);
recinfo->null_pos= m_null_count/8;
field->move_field(pos, null_flags + m_null_count/8,
(uint8)1 << (m_null_count & 7));
m_null_count++;
recinfo->null_bit= (uint8)1 << (null_counter[current_counter] & 7);
recinfo->null_pos= (null_pack_base[current_counter] +
null_counter[current_counter]/8);
field->move_field(pos, null_flags + recinfo->null_pos, recinfo->null_bit);
null_counter[current_counter]++;
}
else
field->move_field(pos,(uchar*) 0,0);
if (field->type() == MYSQL_TYPE_BIT)
{
/* We have to reserve place for extra bits among null bits */
((Field_bit*) field)->set_bit_ptr(null_flags + m_null_count / 8,
m_null_count & 7);
m_null_count+= (field->field_length & 7);
((Field_bit*) field)->set_bit_ptr(null_flags +
null_pack_base[current_counter] +
null_counter[current_counter]/8,
null_counter[current_counter] & 7);
null_counter[current_counter]+= (field->field_length & 7);
}
field->reset();
@ -18655,8 +18712,6 @@ bool Create_tmp_table::finalize(THD *thd,
/* Make entry for create table */
recinfo->length=length;
recinfo->type= field->tmp_engine_column_type(use_packed_rows);
if (!--m_hidden_field_count)
m_null_count= (m_null_count + 7) & ~7; // move to next byte
// fix table name in field entry
field->set_table_name(&table->alias);
@ -18777,7 +18832,8 @@ bool Create_tmp_table::finalize(THD *thd,
m_group_buff <= param->group_buff + param->group_length);
}
if (m_distinct && share->fields != param->hidden_field_count)
if (m_distinct && (share->fields != param->hidden_field_count ||
m_with_cycle))
{
uint i;
Field **reg_field;
@ -18789,7 +18845,7 @@ bool Create_tmp_table::finalize(THD *thd,
*/
DBUG_PRINT("info",("hidden_field_count: %d", param->hidden_field_count));
if (share->blob_fields)
if (m_blobs_count[distinct])
{
/*
Special mode for index creation in MyISAM used to support unique
@ -18798,10 +18854,8 @@ bool Create_tmp_table::finalize(THD *thd,
*/
share->uniques= 1;
}
null_pack_length-=hidden_null_pack_length;
keyinfo->user_defined_key_parts=
((share->fields - param->hidden_field_count)+
(share->uniques ? MY_TEST(null_pack_length) : 0));
keyinfo->user_defined_key_parts= m_field_count[distinct] +
(share->uniques ? MY_TEST(null_pack_length[distinct]) : 0);
keyinfo->ext_key_parts= keyinfo->user_defined_key_parts;
keyinfo->usable_key_parts= keyinfo->user_defined_key_parts;
table->distinct= 1;
@ -18844,11 +18898,11 @@ bool Create_tmp_table::finalize(THD *thd,
blobs can distinguish NULL from 0. This extra field is not needed
when we do not use UNIQUE indexes for blobs.
*/
if (null_pack_length && share->uniques)
if (null_pack_length[distinct] && share->uniques)
{
m_key_part_info->null_bit=0;
m_key_part_info->offset=hidden_null_pack_length;
m_key_part_info->length=null_pack_length;
m_key_part_info->offset= null_pack_base[distinct];
m_key_part_info->length= null_pack_length[distinct];
m_key_part_info->field= new Field_string(table->record[0],
(uint32) m_key_part_info->length,
(uchar*) 0,
@ -18866,8 +18920,10 @@ bool Create_tmp_table::finalize(THD *thd,
/* Create a distinct key over the columns we are going to return */
for (i= param->hidden_field_count, reg_field= table->field + i ;
i < share->fields;
i++, reg_field++, m_key_part_info++)
i++, reg_field++)
{
if (!((*reg_field)->flags & FIELD_PART_OF_TMP_UNIQUE))
continue;
m_key_part_info->field= *reg_field;
(*reg_field)->flags |= PART_KEY_FLAG;
if (m_key_part_info == keyinfo->key_part)
@ -18904,6 +18960,8 @@ bool Create_tmp_table::finalize(THD *thd,
(ha_base_keytype) m_key_part_info->type == HA_KEYTYPE_VARTEXT1 ||
(ha_base_keytype) m_key_part_info->type == HA_KEYTYPE_VARTEXT2) ?
0 : FIELDFLAG_BINARY;
m_key_part_info++;
}
}
@ -27481,7 +27539,7 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
else
str->append(',');
if (is_subquery_function() && item->is_autogenerated_name)
if (is_subquery_function() && item->is_autogenerated_name())
{
/*
Do not print auto-generated aliases in subqueries. It has no purpose