← Back to team overview

maria-developers team mailing list archive

Re: 4c263b6d30b: MDEV-20632: Recursive CTE cycle detection using CYCLE clause

 

Hi, Sanja!

_some_ comments are below.

The main thing, I still don't understand your changes in sql_select.cc.

Why did you create separate counters and code paths with if() for CYCLE?
I'd think it could be just a generalization of DISTINCT

On Feb 20, Oleksandr Byelkin wrote:
> revision-id: 4c263b6d30b (mariadb-10.5.0-92-g4c263b6d30b)
> parent(s): 6f2e2285291
> author: Oleksandr Byelkin <sanja@xxxxxxxxxxx>
> committer: Oleksandr Byelkin <sanja@xxxxxxxxxxx>
> timestamp: 2020-01-27 21:56:02 +0100
> message:
> 
> MDEV-20632: Recursive CTE cycle detection using CYCLE clause
> 
> Added CYCLE clause to recursive CTE.
>
> diff --git a/sql/item.h b/sql/item.h
> index c1f68a4f942..2cbaf880e00 100644
> --- a/sql/item.h
> +++ b/sql/item.h
> @@ -622,6 +622,13 @@ class st_select_lex_unit;
>  class Item_func_not;
>  class Item_splocal;
>  
> +/* Item::common_flags */
> +/* Indicates that name of this Item autogenerated or set by user */
> +#define IS_AUTO_GENETATED_NAME 1

typo ^^^

> +/* Inticates that this item is in CYCLE clause of WITH */

typo ^^^

> +#define IS_IN_WITH_CYCLE       2
> +
> +
>  /**
>    String_copier that sends Item specific warnings.
>  */
> diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc
> index 5b3d3108da5..971844120bc 100644
> --- a/sql/sql_cte.cc
> +++ b/sql/sql_cte.cc
> @@ -982,6 +982,38 @@ With_element::rename_columns_of_derived_unit(THD *thd,

are you sure what you're doing below can still be called
"rename_columns_of_derived_unit" ?

>    else
>      make_valid_column_names(thd, select->item_list);
>  
> +  if (cycle_list)
> +  {
> +    List_iterator_fast<Item> it(select->item_list);
> +    List_iterator_fast<LEX_CSTRING> nm(*cycle_list);
> +    List_iterator_fast<LEX_CSTRING> nm_check(*cycle_list);
> +    DBUG_ASSERT(cycle_list->elements != 0);
> +    while (LEX_CSTRING *name= nm++)
> +    {
> +      Item *item;
> +      // unique check
> +      LEX_CSTRING *check;
> +      nm_check.rewind();
> +      while ((check= nm_check++) && check != name)
> +      {
> +        if (check->length == name->length &&
> +            strncmp(check->str, name->str, name->length) == 0)
> +        {
> +          my_error(ER_DUP_FIELDNAME, MYF(0), check->str);
> +          return true;
> +        }
> +      }
> +      while ((item= it++) &&
> +             (item->name.length != name->length ||
> +              strncmp(item->name.str, name->str, name->length) != 0));
> +      if (item == NULL)
> +      {
> +        my_error(ER_BAD_FIELD_ERROR, MYF(0), name->str, "CYCLE clause");
> +        return true;
> +      }

this pattern is used so often that we might want to have
a helper, like, List_iterator_fast<>::find()

not in this commit, though.

> +      item->common_flags|= IS_IN_WITH_CYCLE;
> +    }
> +  }
>    unit->columns_are_renamed= true;
>  
>    return false;
> @@ -1425,6 +1457,21 @@ void With_clause::print(String *str, enum_query_type query_type)
>  }
>  
>  
> +static void list_strlex_print(String *str, List<LEX_CSTRING> *list)
> +{
> +  List_iterator_fast<LEX_CSTRING> li(*list);
> +  bool first= TRUE;
> +  while(LEX_CSTRING *col_name= li++)
> +  {
> +    if (first)
> +      first= FALSE;
> +    else
> +      str->append(',');
> +    str->append(col_name);

should be "append_identifier", shouldn't it?
add a test where a column name is a reserved word.

> +  }
> +}
> +
> +
>  /**
>     @brief
>       Print this with element
> @@ -1444,22 +1491,20 @@ void With_element::print(String *str, enum_query_type query_type)
>    {
>      List_iterator_fast<LEX_CSTRING> li(column_list);
>      str->append('(');
> -    for (LEX_CSTRING *col_name= li++; ; )
> -    {
> -      str->append(col_name);
> -      col_name= li++;
> -      if (!col_name)
> -      {
> -        str->append(')');
> -        break;
> -      }
> -      str->append(',');
> -    }
> +    list_strlex_print(str, &column_list);

any other code that could make use of your list_strlex_print ?

> +    str->append(')');
>    }
> -  str->append(STRING_WITH_LEN(" as "));
> -  str->append('(');
> +  str->append(STRING_WITH_LEN(" as ("));
>    spec->print(str, query_type);
>    str->append(')');
> +
> +  if (cycle_list)
> +  {
> +    DBUG_ASSERT(cycle_list->elements != 0);
> +    str->append(STRING_WITH_LEN(" CYCLE ("));
> +    list_strlex_print(str, cycle_list);
> +    str->append(") ");
> +  }
>  }
>  
>  
> diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
> index c115d9352aa..0d60ab8579f 100644
> --- a/sql/sql_yacc.yy
> +++ b/sql/sql_yacc.yy
> @@ -14704,9 +14704,30 @@ with_list_element:
>              if (elem->set_unparsed_spec(thd, spec_start, $6.pos(),
>                                          spec_start - query_start))
>                MYSQL_YYABORT;
> +            if ($7)
> +            {
> +              elem->set_cycle_list($7);
> +            }
>  	  }
>  	;
>  
> +opt_cycle:
> +         /* empty */
> +         { $$= NULL; }
> +         |
> +         CYCLE_SYM
> +         {
> +           if (!Lex->curr_with_clause->with_recursive)
> +           {
> +             thd->parse_error(ER_SYNTAX_ERROR, $1.pos());
> +           }
> +         }
> +         '(' with_column_list ')'

Where did you see that the standard requires parentheses here?

> +         {
> +           $$= $4;
> +         }
> +         ;
> +
>  
>  opt_with_column_list:
>            /* empty */

Regards,
Sergei
VP of MariaDB Server Engineering
and security@xxxxxxxxxxx