maria-developers team mailing list archive
-
maria-developers team
-
Mailing list archive
-
Message #11925
Re: Coding Style Review INSERT RETURNING
Hello!
Thanks for the review. I'll start working on it and keep you updated with
my progress.
Regards,
Rucha
On Wed, Aug 7, 2019 at 7:51 PM Vicențiu Ciorbaru <vicentiu@xxxxxxxxxxx>
wrote:
> Hi Rucha!
>
> This is going to be a very thorough review looking at coding style. I'll
> nitpick on every line of code where I find something, just be patient and
> let's clean up everything.
>
> You might consider me picking on the coding style a bit pedantic, but it
> really is important. It is important because it helps in searching, it
> helps
> in understanding the code faster, it helps to look-up history faster.
> The good part is that once you get used to writing things properly, you
> won't
> even have to think about it actively. It will just come naturally.
>
> I suspect you do not have your editor configured to show whitespaces and
> show you tabs versus spaces. Please look into your editor's settings to
> highlight both tabs and spaces.
>
> There are scripts that remove trailing whitespaces for you, across whole
> files.
> I don't recommend them for MariaDB code as we have remnants from MySQL
> times of
> these and because we care more about preserving the immediate history for
> running `git blame`, we did not fully fix all files. For any other
> project, I
> highly recommend you use them.
>
> Some of these comments might slightly contradict what I said before. That
> is
> because I wanted us to get things working first and I didn't want to burden
> you with too many details initially. I'll look to explain myself now
> clearly
> so you understand why some things are bad ideas. I want you to take this
> review also as a general guideline for any other code you write, anywhere.
> With these hints, tricks, ideas, you'll produce much higher quality code in
> the long run. It will take you longer to write sometimes, but it really
> makes
> a difference. :)
>
> Also, you may have noticed some parts of our code do not agree with my
> general
> guidelines. You have to take into account that this code was written over
> the
> course of many years, by different people, with different levels of
> experience
> or available time to implement something. Try not to make use of it as an
> excuse for new code that you write. It's up to you to always write the best
> possible version *with the time allowed*. We have adequate time now so we
> can afford to look for better solutions.
>
> With all that said, this took me a while to write and I may still
> have missed a few things. Feel free to point out anything that looks
> strange.
> Now, let's get to the review.
>
> >
> > diff --git a/sql/sql_class.h b/sql/sql_class.h
> > index 18ccf992d1e..9e4bb1d4579 100644
> > --- a/sql/sql_class.h
> > +++ b/sql/sql_class.h
> > @@ -5475,7 +5476,13 @@ class select_dump :public select_to_file {
> >
> > class select_insert :public select_result_interceptor {
> > public:
> > + select_result* sel_result;
> The * should be next to the variable name. Not everybody does this, but the
> code around it does, so let's be consistent.
> > TABLE_LIST *table_list;
> > + /*
> > + Points to the table in which we're inserting records. We need to
> save the
> > + insert table before it is masked in the parsing phase.
> > + */
> > + TABLE_LIST *insert_table;
> This sort of code and comment is usually a bad idea:
> 1. We are adding an extra member, because of a hack in a different part of
> the code.
> 2. We are mentioning a specific flow for which this member is good for.
> This
> already begins to hint at a bad design decision. Sometimes there's no
> way
> around it, but this guarantees lack of re-usability. If, in the future
> we fix the "masking in the parsing phase" to no longer mask the first
> table,
> which we probably will do, because we have to to allow INSERT within
> other
> queries, the comment will become outdated. One needs to remember this
> too.
>
> Forgetting to update comments happens easily. Here is one example:
> One checks the class initially, finds the appropriate member to use,
> uses it in a different context than the original author thought of. The
> class then is no longer changed so it does not show up during review
> time,
> the reviewer doesn't think to check the class member's definition
> either.
> You now have outdated comments.
>
> To prevent this from happening, avoid as much as possible to explain
> where
> a member is used, rather explain what type of data it is supposed to
> store.
> The first sentence accomplishes that goal.
> > TABLE *table;
> > List<Item> *fields;
> > ulonglong autoinc_value_of_last_inserted_row; // autogenerated or not
> > @@ -5484,7 +5491,7 @@ class select_insert :public
> select_result_interceptor {
> > select_insert(THD *thd_arg, TABLE_LIST *table_list_par,
> > TABLE *table_par, List<Item> *fields_par,
> > List<Item> *update_fields, List<Item> *update_values,
> > - enum_duplicates duplic, bool ignore);
> > + enum_duplicates duplic, bool ignore,select_result*
> sel_ret_list,TABLE_LIST *save_first);
> This is an artifact of our long-standing code base. TABS mixed with
> spaces. :(
> Let's clean up all of this select_insert's constructor. Do not keep the
> tabs,
> we are modifying it anyway. Turn all these tabs to proper spaces. Make sure
> the function arguments match the beginning parenthesis like so:
> <function_name>(TYPE1 arg1, TYPE2 arg2,
> TYPE3 arg3, TYPE4 arg4,
> TYPE5 arg5)
>
> > ~select_insert();
> > int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
> > virtual int prepare2(JOIN *join);
> > @@ -5520,7 +5527,7 @@ class select_create: public select_insert {
> > List<Item> &select_fields,enum_duplicates duplic, bool
> ignore,
> > TABLE_LIST *select_tables_arg):
> > select_insert(thd_arg, table_arg, NULL, &select_fields, 0, 0,
> duplic,
> > - ignore),
> > + ignore, NULL, NULL),
> Sigh... again, we have a historical background of mixing NULLs with 0s.
> Ok here I guess.
> > create_table(table_arg),
> > create_info(create_info_par),
> > select_tables(select_tables_arg),
> > diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
> > index f3a548d7265..4bb19cef726 100644
> > --- a/sql/sql_insert.cc
> > +++ b/sql/sql_insert.cc
> > @@ -119,6 +119,18 @@ static bool check_view_insertability(THD *thd,
> TABLE_LIST *view);
> > @returns false if success.
> > */
> >
> > +/*
> > + Swaps the context before and after calling setup_fields() and
> setup_wild() in
> > + INSERT...SELECT when LEX::returning_list is not empty.
> One more space to indent the comment text, for both lines.
> > +*/
> > +template <typename T>
> > +void swap_context(T& cxt1, T& cxt2)
> > +{
> > + T temp = cxt1;
> > + cxt1 = cxt2;
> > + cxt2 = temp;
> > +
> > +}
> Unneded empty line before the the closing }.
> Also, you should add 2 empty lines after the function. Delete the tabs in
> the
> function, replace with 2 spaces.
> Also, this function is not even used just to swap_contexts any more. Rename
> it to swap.
>
> Can we not use std::swap instead?
> > static bool check_view_single_update(List<Item> &fields, List<Item>
> *values,
> > TABLE_LIST *view, table_map *map,
> > bool insert)
> > @@ -688,6 +700,10 @@ Field **TABLE::field_to_fill()
> >
> > /**
> > INSERT statement implementation
> > +
> Trailing whitespace.
> > + SYNOPSIS
> > + mysql_insert()
> > + result NULL if returning_list is empty
> I'd change this to NULL if the insert is not outputing results via
> 'RETURNING'
> clause.
> >
> > @note Like implementations of other DDL/DML in MySQL, this function
> > relies on the caller to close the thread tables. This is done in the
> > @@ -700,7 +716,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
> > List<Item> &update_fields,
> > List<Item> &update_values,
> > enum_duplicates duplic,
> > - bool ignore)
> > + bool ignore, select_result* result)
> Remove tabs, use spaces, align properly, like explained above.
> > {
> > bool retval= true;
> > int error, res;
> > @@ -719,6 +735,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
> > List_item *values;
> > Name_resolution_context *context;
> > Name_resolution_context_state ctx_state;
> > + SELECT_LEX* select_lex = thd->lex->first_select_lex();
> > + List<Item>& returning_list = thd->lex->returning_list;
> Generally this kind of use of references is a bad idea. Highly suggest you
> only use const references, otherwise things can turn very confusing,
> very quickly. If you read the Google C++ coding style guide, you'll see
> they
> only allow const references in any part of their code.
> > #ifndef EMBEDDED_LIBRARY
> > char *query= thd->query();
> > /*
> > @@ -775,9 +793,13 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
> >
> > if (mysql_prepare_insert(thd, table_list, table, fields, values,
> > update_fields, update_values, duplic,
> &unused_conds,
> > - FALSE))
> > - goto abort;
> > -
> > + FALSE,result?true:false))
> Missing a few spaces. One after , One before and one after ?
> One before and one after :.
> > + goto abort;
> Tabs instead of spaces.
> > +
> Trailing whitespaces.
> > + /* Prepares LEX::returing_list if it is not empty */
> > + if (result)
> > + (void)result->prepare(returning_list, NULL);
> Tabs instead of spaces.
> > +
> Tabs, spaces, trailing whitespaces. Make sure each new line that contains
> no text be just that, a newline.
> > /* mysql_prepare_insert sets table_list->table if it was not set */
> > table= table_list->table;
> >
> > @@ -947,6 +969,18 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
> > goto values_loop_end;
> > }
> > }
> > + /*
> > + If statement returns result set, we need to send the result set
> metadata
> > + to the client so that it knows that it has to expect an EOF or ERROR.
> > + At this point we have all the required information to send the result
> set metadata.
> > + */
> When adding comments like these, you need to indent the text 2 more spaces.
> You crossed the 80 character limit with the "metadata" word. That needs to
> be
> put on a new line.
> > + if (result)
> > + {
> > + if (unlikely(result->send_result_set_metadata(returning_list,
> Tabs again.
> > + Protocol::SEND_NUM_ROWS |
> > + Protocol::SEND_EOF)))
> > + goto values_loop_end;
> The goto indentation is ok, but Protocol should be aligned to the last open
> parenthesis.
> > + }
> >
> > THD_STAGE_INFO(thd, stage_update);
> > do
> > @@ -1060,7 +1094,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
> > error= 1;
> > break;
> > }
> > -
> > +
> You added an extra line here, but introduced a tab and trailing
> whitespaces,
> not good.
> > #ifndef EMBEDDED_LIBRARY
> > if (lock_type == TL_WRITE_DELAYED)
> > {
> > @@ -1072,9 +1106,25 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
> > }
> > else
> > #endif
> > - error=write_record(thd, table ,&info);
> > + error=write_record(thd, table ,&info);
> You didn't change this line logic at all. Just introduced an extra tab plus
> whitespaces. Please clean it up.
> > if (unlikely(error))
> > break;
> > + /*
> > + We send the rows after writing them to table so that the
> correct information
> > + is sent to the client. Example INSERT...ON DUPLICAT KEY UPDATE
> and values
> > + set to auto_increment. Write record handles auto_increment
> updating values
> > + if there is a duplicate key. We want to send the rows to the
> client only
> > + after these operations are carried out. Otherwise it shows 0 to
> the client
> > + if the fields that are incremented automatically are not given
> explicitly
> > + and the irreplaced values in case of ON DUPLICATE KEY UPDATE
> (even if
> > + the values are replaced or incremented while writing record.
> > + Hence it shows different result set to the client)
> > + */
> Good comment! Trailing whitespaces however are still present and you are
> breaking the 80 character rule.
> > + if (result && result->send_data(returning_list) < 0)
> > + {
> > + error = 1;
> Remove space before =.
> > + break;
> > + }
> > thd->get_stmt_da()->inc_current_row_for_warning();
> > }
> > its.rewind();
> > @@ -1233,19 +1283,30 @@ bool mysql_insert(THD *thd,TABLE_LIST
> *table_list,
> > retval= thd->lex->explain->send_explain(thd);
> > goto abort;
> > }
> > +
> Trailing whitespaces. Please fix.
> > if ((iteration * values_list.elements) == 1 &&
> (!(thd->variables.option_bits & OPTION_WARNINGS) ||
> > !thd->cuted_fields))
> > - {
> > - my_ok(thd, info.copied + info.deleted +
> > + {
> > + /*
> > + Client expects an EOF/Ok packet if result set metadata was sent.
> If
> > + LEX::returning_list is not empty and the statement returns result
> set
> > + we send EOF which is the indicator of the end of the row stream.
> > + Else we send Ok packet i.e when the statement returns only the
> status
> > + information
> > + */
> > + if (result)
> > + result->send_eof();
> > + else
> > + my_ok(thd, info.copied + info.deleted +
> > ((thd->client_capabilities & CLIENT_FOUND_ROWS) ?
> > - info.touched : info.updated),
> > - id);
> > + info.touched : info.updated),id);
> > }
> > else
> > - {
> > + {
> You didn't change the logic here, just added an extra trailing whitespace.
> > char buff[160];
> > ha_rows updated=((thd->client_capabilities & CLIENT_FOUND_ROWS) ?
> > info.touched : info.updated);
> > +
> I guess I am ok with an extra line here, but it should not have an extra
> tab.
> Please, only newlines, no extra characters.
> > if (ignore)
> > sprintf(buff, ER_THD(thd, ER_INSERT_INFO), (ulong) info.records,
> > (lock_type == TL_WRITE_DELAYED) ? (ulong) 0 :
> > @@ -1255,8 +1316,12 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
> > sprintf(buff, ER_THD(thd, ER_INSERT_INFO), (ulong) info.records,
> > (ulong) (info.deleted + updated),
> > (long)
> thd->get_stmt_da()->current_statement_warn_count());
> > - ::my_ok(thd, info.copied + info.deleted + updated, id, buff);
> > + if (result)
> > + result->send_eof();
> > + else
> Tabs, please use spaces.
> > + ::my_ok(thd, info.copied + info.deleted + updated, id, buff);
> > }
> > +
> Trailing whitespaces. :(
> > thd->abort_on_warning= 0;
> > if (thd->lex->current_select->first_cond_optimization)
> > {
> > @@ -1470,6 +1535,7 @@ static void prepare_for_positional_update(TABLE
> *table, TABLE_LIST *tables)
> > be taken from table_list->table)
> > where Where clause (for insert ... select)
> > select_insert TRUE if INSERT ... SELECT statement
> > + with_returning_list TRUE if returning_list is not empty
> Trailing whitespace. (I know the rest of the function comments also have
> trailing whitespaces. Perfect time to fix all that and remove all their
> tabs.
> >
> > TODO (in far future)
> > In cases of:
> > @@ -1490,7 +1556,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST
> *table_list,
> > TABLE *table, List<Item> &fields, List_item
> *values,
> > List<Item> &update_fields, List<Item>
> &update_values,
> > enum_duplicates duplic, COND **where,
> > - bool select_insert)
> > + bool select_insert, bool with_returning_list)
> One too many spaces after the last comma.
> > {
> > SELECT_LEX *select_lex= thd->lex->first_select_lex();
> > Name_resolution_context *context= &select_lex->context;
> > @@ -1556,8 +1622,21 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST
> *table_list,
> > */
> > table_list->next_local= 0;
> > context->resolve_in_table_list_only(table_list);
> > +
> Trailing whitespaces.
> > + /*
> > + Perform checks like all given fields exists, if exists fill
> struct with
> Trailing whitespace.
> > + current data and expand all '*' in given fields for
> LEX::returning_list.
> > + */
> Trailing whitespaces.
> > + if(with_returning_list)
> Trailing whitespaces. Put a space after if.
> > + {
> > + res= ((select_lex->with_wild && setup_wild(thd, table_list,
> Trailing whitespace.
> > +
> thd->lex->returning_list,NULL, select_lex->with_wild,
> Trailing whitespace, tabs instead of spaces.
> > +
> &select_lex->hidden_bit_fields)) ||
> > + setup_fields(thd,
> Ref_ptr_array(),
> > + thd->lex->returning_list,
> MARK_COLUMNS_READ, 0, NULL, 0));
> Tabs instead of spaces, swap them to spaces. Make sure to follow the 80
> character max line length.
> > + }
> >
> > - res= (setup_fields(thd, Ref_ptr_array(),
> > + res= (res||setup_fields(thd, Ref_ptr_array(),
> Please Add a space before and after ||.
> > *values, MARK_COLUMNS_READ, 0, NULL, 0) ||
> > check_insert_fields(thd, context->table_list, fields, *values,
> > !insert_into_view, 0, &map));
> > @@ -1724,7 +1803,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO
> *info)
> > table->file->insert_id_for_cur_row= insert_id_for_cur_row;
> > bool is_duplicate_key_error;
> > if (table->file->is_fatal_error(error, HA_CHECK_ALL))
> > - goto err;
> > + goto err;
> A good fix, not strictly necessary, but a good fix.
> > is_duplicate_key_error=
> > table->file->is_fatal_error(error, HA_CHECK_ALL &
> ~HA_CHECK_DUP);
> > if (!is_duplicate_key_error)
> > @@ -3557,29 +3636,39 @@ bool Delayed_insert::handle_inserts(void)
> > TRUE Error
> > */
> >
> > -bool mysql_insert_select_prepare(THD *thd)
> > +bool mysql_insert_select_prepare(THD *thd,select_result *sel_res)
> Put a space after last comma (,).
> This function could use a bit of tweaking, but let's fix all the code style
> issues first.
> > {
> > LEX *lex= thd->lex;
> > SELECT_LEX *select_lex= lex->first_select_lex();
> > DBUG_ENTER("mysql_insert_select_prepare");
> > -
> > + List<Item>& returning_list = thd->lex->returning_list;
> For assignment operators no space before it. Like so:
> List<Item>& returning_list= thd->lex->returning_list;
> >
> >
> > /*
> > SELECT_LEX do not belong to INSERT statement, so we can't add WHERE
> > clause if table is VIEW
> > */
> > -
> > + /*
> > + Passing with_returning_list (last argument) as false otherwise
> > + setup_field() and setup_wild() will be called twice. 1) in
> mysql_prepare_insert()
> > + and 2) time in select_insert::prepare(). We want to call it only
> once:
> > + in select_insert::prepare()
> > + */
> Ok, this is an interesting comment. It's much better if we can write code
> that
> prevents this sort of reasoning. Pragmatically, why do we end up calling it
> twice? Can we somehow refactor it such that this is not a problem in the
> first place?
> For example, here it kind of makes sense to just compute
> with_returning_list
> within the function, by looking at thd->lex->returning_list.
> > if (mysql_prepare_insert(thd, lex->query_tables,
> > lex->query_tables->table, lex->field_list, 0,
> > lex->update_list, lex->value_list,
> lex->duplicates,
> > - &select_lex->where, TRUE))
> > + &select_lex->where, TRUE,false))
> Put a space after last comma. And let's use FALSE.
> > DBUG_RETURN(TRUE);
> >
> > + /* If LEX::returning_list is not empty, we prepare the list */
> Uhm, the comment doesn't seem to match the code. You are checking sel_res,
> yet the comment says that if LEX:returning_list is not empty, prepare the
> list.
> > + if (sel_res)
> > + (void)sel_res->prepare(returning_list, NULL);
> Here we have a tab instead of spaces again.
> > +
> > DBUG_ASSERT(select_lex->leaf_tables.elements != 0);
> > List_iterator<TABLE_LIST> ti(select_lex->leaf_tables);
> > TABLE_LIST *table;
> > uint insert_tables;
> >
> > +
> This line is not necessary.
> > if (select_lex->first_cond_optimization)
> > {
> > /* Back up leaf_tables list. */
> > @@ -3607,6 +3696,7 @@ bool mysql_insert_select_prepare(THD *thd)
> > while ((table= ti++) && insert_tables--)
> > ti.remove();
> >
> > +
> This line is not necessary.
> > DBUG_RETURN(FALSE);
> > }
> >
> > @@ -3617,7 +3707,7 @@ select_insert::select_insert(THD *thd_arg,
> TABLE_LIST *table_list_par,
> > List<Item> *update_fields,
> > List<Item> *update_values,
> > enum_duplicates duplic,
> > - bool ignore_check_option_errors):
> > + bool
> ignore_check_option_errors,select_result *result, TABLE_LIST *save_first):
> This line is still way too long. I remember mentioning this in a previous
> review, please look more closely and wrap this properly.
> > select_result_interceptor(thd_arg),
> > table_list(table_list_par), table(table_par), fields(fields_par),
> > autoinc_value_of_last_inserted_row(0),
> > @@ -3630,6 +3720,8 @@ select_insert::select_insert(THD *thd_arg,
> TABLE_LIST *table_list_par,
> > info.update_values= update_values;
> > info.view= (table_list_par->view ? table_list_par : 0);
> > info.table_list= table_list_par;
> > + sel_result= result;
> > + insert_table= save_first;
> Trailing whitespace, you added a tab after =. Please delete the tab.
> > }
> >
> >
> > @@ -3637,10 +3729,11 @@ int
> > select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
> > {
> > LEX *lex= thd->lex;
> > - int res;
> > + int res=0;
> Space after =.
> > table_map map= 0;
> > SELECT_LEX *lex_current_select_save= lex->current_select;
> > DBUG_ENTER("select_insert::prepare");
> > + SELECT_LEX* select_lex = thd->lex->first_select_lex();
> Delete space before =.
> >
> > unit= u;
> >
> > @@ -3651,7 +3744,33 @@ select_insert::prepare(List<Item> &values,
> SELECT_LEX_UNIT *u)
> > */
> > lex->current_select= lex->first_select_lex();
> >
> > - res= (setup_fields(thd, Ref_ptr_array(),
> > + /*
> > + We want the returning_list to point to insert table. But the
> context is masked.
> > + So we swap it with the context saved during parsing stage.
> Tab mixed with spaces. Only use spaces. Traling whitespace, here and the
> line
> before.
> > + */
> > + if(sel_result)
> Trailing whitespace.
> > + {
> Trailing whitespace.
> > + swap_context(insert_table,select_lex->table_list.first);
> > +
> swap_context(select_lex->context.saved_table_list,select_lex->context.table_list);
> > +
> swap_context(select_lex->context.saved_name_resolution_table,select_lex->context.first_name_resolution_table);
> Line length exceeds 80 characters.
> > +
> > + /*
> > + Perform checks for LEX::returning_list like we do for other
> variant of INSERT.
> > + */
> > + res=((select_lex->with_wild && setup_wild(thd, table_list,
> > + thd->lex->returning_list, NULL,
> select_lex->with_wild,
> > + &select_lex->hidden_bit_fields)) ||
> > + setup_fields(thd, Ref_ptr_array(),
> > + thd->lex->returning_list, MARK_COLUMNS_READ, 0,
> NULL, 0));
> Tabs instead of spaces and mixed spaces. Not good. Also trailing
> whitespaces.
> Also, the parameters of setup_wild should be aligned to the first
> parenthesis
> of setup_wild. setup_fields should be aligned to the first parenthesis
> here.
> I also think there's one too many parantheses here, the outer most ones are
> not necessary..
> > +
> > + /* Swap it back to retore the previous state for the rest
> of the function */
> Tabs instead of spaces.
> > +
> > + swap_context(insert_table,select_lex->table_list.first);
> > +
> swap_context(select_lex->context.saved_table_list,select_lex->context.table_list);
> > +
> swap_context(select_lex->context.saved_name_resolution_table,
> select_lex->context.first_name_resolution_table);
> Tabs and spaces mixed here. Watch line length.
> > + }
> > +
> > + res= res || (setup_fields(thd, Ref_ptr_array(),
> > values, MARK_COLUMNS_READ, 0, NULL, 0) ||
> > check_insert_fields(thd, table_list, *fields, values,
> > !insert_into_view, 1, &map));
> The line that starts with "values" should be aligned to the first open
> parenthesis.
> > @@ -3822,13 +3941,26 @@ select_insert::prepare(List<Item> &values,
> SELECT_LEX_UNIT *u)
> >
> > int select_insert::prepare2(JOIN *)
> > {
> > +
> Trailing whitespace.
> > DBUG_ENTER("select_insert::prepare2");
> > + List<Item>& returning_list = thd->lex->returning_list;
> Delete space before =
> > + LEX* lex = thd->lex;
> This function does not use the lex local variable at all, only via
> thd->lex.
> This line should be removed.
> > if (thd->lex->current_select->options & OPTION_BUFFER_RESULT &&
> > thd->locked_tables_mode <= LTM_LOCK_TABLES &&
> > !thd->lex->describe)
> > table->file->ha_start_bulk_insert((ha_rows) 0);
> > if (table->validate_default_values_of_unset_fields(thd))
> > DBUG_RETURN(1);
> > +
> > + /* Same as the other variants of INSERT */
> > + if (sel_result)
> > + {
> > + if(unlikely(sel_result->send_result_set_metadata(returning_list,
> > + Protocol::SEND_NUM_ROWS |
> > + Protocol::SEND_EOF)))
> The Protocol should be aligned to the last open parenthesis.
> You are mixing tabs with spaces here. Convert to spaces.
> > +
> > + DBUG_RETURN(1);
> Mixing tabs with spaces again, convert to spaces.
> > + }
> > DBUG_RETURN(0);
> > }
> >
> > @@ -3842,6 +3974,8 @@ void select_insert::cleanup()
> > select_insert::~select_insert()
> > {
> > DBUG_ENTER("~select_insert");
> > + sel_result=NULL;
> Add a space after =.
> > + insert_table=NULL;
> Add a space after =.
> > if (table && table->is_created())
> > {
> > table->next_number_field=0;
> > @@ -3857,6 +3991,8 @@ select_insert::~select_insert()
> > int select_insert::send_data(List<Item> &values)
> > {
> > DBUG_ENTER("select_insert::send_data");
> > + LEX* lex = thd->lex;
> Delete space before =.
> >
> > + List<Item>& returning_list = thd->lex->returning_list;
> Delete space before =.
> > bool error=0;
> >
> > if (unit->offset_limit_cnt)
> > @@ -3889,8 +4025,18 @@ int select_insert::send_data(List<Item> &values)
> > DBUG_RETURN(1);
> > }
> > }
> > -
> > +
> Trailing whitespaces added for no good reason.
> > error= write_record(thd, table, &info);
> > +
> Trailing whitespaces.
> > + /*
> > + Sending the result set to the cliet after writing record. The
> reason is same
> > + as other variants of insert.
> > + */
> > + if (sel_result && sel_result->send_data(returning_list) < 0)
> > + {
> > + error = 1;
> Delete space before =.
> > + DBUG_RETURN(1);
> > + }
> > table->vers_write= table->versioned();
> > table->auto_increment_field_not_null= FALSE;
> >
> > @@ -4024,7 +4170,7 @@ bool select_insert::send_ok_packet() {
> > char message[160]; /* status message */
> > ulonglong row_count; /* rows affected */
> > ulonglong id; /* last insert-id */
> > -
> > + LEX *lex=thd->lex;
> This function does not use this variable at all. Please delete it if it's
> not
> used. All it does is produce a compiler warning.
> > DBUG_ENTER("select_insert::send_ok_packet");
> >
> > if (info.ignore)
> > @@ -4045,8 +4191,15 @@ bool select_insert::send_ok_packet() {
> > (thd->arg_of_last_insert_id_function ?
> > thd->first_successful_insert_id_in_prev_stmt :
> > (info.copied ? autoinc_value_of_last_inserted_row : 0));
> > -
> > - ::my_ok(thd, row_count, id, message);
> > +
> Trailing whitespace
> > + /*
> > + Client expects an EOF/Ok packet If LEX::returning_list is not empty
> and
> Trailing whitespace
> > + if result set meta was sent. See explanation for other variants of
> INSERT.
> > + */
> > + if (sel_result)
> > + sel_result->send_eof();
> > + else
> > + ::my_ok(thd, row_count, id, message);
> >
> > DBUG_RETURN(false);
> > }
> > diff --git a/sql/sql_insert.h b/sql/sql_insert.h
> > index a37ed1f31e5..314817b53d3 100644
> > --- a/sql/sql_insert.h
> > +++ b/sql/sql_insert.h
> > @@ -27,11 +27,11 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST
> *table_list, TABLE *table,
> > List<Item> &fields, List_item *values,
> > List<Item> &update_fields,
> > List<Item> &update_values, enum_duplicates
> duplic,
> > - COND **where, bool select_insert);
> > + COND **where, bool select_insert, bool
> with_returning_list);
> This line exceeds 80 characters. Please wrap it to 80 characters by moving
> the final function parameter to a new line.
> > bool mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields,
> > List<List_item> &values, List<Item> &update_fields,
> > List<Item> &update_values, enum_duplicates flag,
> > - bool ignore);
> > + bool ignore, select_result* result);
> > void upgrade_lock_type_for_insert(THD *thd, thr_lock_type *lock_type,
> > enum_duplicates duplic,
> > bool is_multi_insert);
> > diff --git a/sql/sql_lex.h b/sql/sql_lex.h
> > index 0e1d17d13f0..00cf3025efe 100644
> > --- a/sql/sql_lex.h
> > +++ b/sql/sql_lex.h
> > @@ -3091,6 +3092,8 @@ struct LEX: public Query_tables_list
> > SELECT_LEX *current_select;
> > /* list of all SELECT_LEX */
> > SELECT_LEX *all_selects_list;
> > + /* List of fields and expression for returning part of insert*/
> Add a space before closing the comment.
> > + List<Item> returning_list;
> > /* current with clause in parsing if any, otherwise 0*/
> > With_clause *curr_with_clause;
> > /* pointer to the first with clause in the current statement */
> > diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
> > index 01d0ed1c383..852078b254b 100644
> > --- a/sql/sql_parse.cc
> > +++ b/sql/sql_parse.cc
> > @@ -4489,6 +4489,7 @@ mysql_execute_command(THD *thd)
> > case SQLCOM_INSERT:
> > {
> > WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE);
> > + select_result * sel_result = NULL;
> Tabs instead of spaces. Delete the space after *. Delete the space before
> =.
> > DBUG_ASSERT(first_table == all_tables && first_table != 0);
> >
> > WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE);
> > @@ -4509,9 +4510,43 @@ mysql_execute_command(THD *thd)
> > break;
> >
> > MYSQL_INSERT_START(thd->query());
> > +
> > + Protocol* UNINIT_VAR(save_protocol);
> > + bool replaced_protocol = false;
> Tabs instead of spaces. Delete space before =
> > +
> > + if (!thd->lex->returning_list.is_empty())
> > + {
> > + /*increment the status variable. This is useful for feedback
> plugin*/
> I would remove this comment, the function name is suggestive enough.
> > + status_var_increment(thd->status_var.feature_insert_returning);
> > +
> > + /*This is INSERT ... RETURNING. It will return output to
> the client */
> Space after start of comment.
> > + if (thd->lex->analyze_stmt)
> > + {
> > + /*
> > + Actually, it is ANALYZE .. INSERT .. RETURNING.
> We need to produce
> > + output and then discard it.
> > + */
> Trailign whitespace tab.
> > + sel_result = new (thd->mem_root)
> select_send_analyze(thd);
> Delete space before =
> > + replaced_protocol = true;
> Delete space before =
> > + save_protocol = thd->protocol;
> Delete space before =
> > + thd->protocol = new Protocol_discard(thd);
> Delete space before =
> > + }
> > + else
> > + {
> > + if (!lex->result && !(sel_result = new
> (thd->mem_root) select_send(thd)))
> Delete space before =
> > + goto error;
> > + }
> > + }
> Tabs instead of spaces for this whole part.
> > +
> Trailing whitespaces.
> > res= mysql_insert(thd, all_tables, lex->field_list,
> lex->many_values,
> > lex->update_list, lex->value_list,
> > - lex->duplicates, lex->ignore);
> > + lex->duplicates, lex->ignore, lex->result ?
> lex->result : sel_result);
> This line overflows the 80 character line rule.
> Wrap the last parameter to another line. IT also makes things a lot easier
> to read.
> > + if (replaced_protocol)
> > + {
> Tabs instead of spaces for these 2 lines.
> > + delete thd->protocol;
> > + thd->protocol= save_protocol;
> > + }
> > + delete sel_result;
> Tab instead of spaces.
> > MYSQL_INSERT_DONE(res, (ulong) thd->get_row_count_func());
> > /*
> > If we have inserted into a VIEW, and the base table has
> > @@ -4547,6 +4582,8 @@ mysql_execute_command(THD *thd)
> > {
> > WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE);
> > select_insert *sel_result;
> > + TABLE_LIST *save_first= NULL;
> Tab instead of space after =.
> > + select_result* result = NULL;
> Delete space after =. Move * close to result not to select_result.
> > bool explain= MY_TEST(lex->describe);
> > DBUG_ASSERT(first_table == all_tables && first_table != 0);
> > WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE);
> > @@ -4595,6 +4632,34 @@ mysql_execute_command(THD *thd)
> > Only the INSERT table should be merged. Other will be handled by
> > select.
> > */
> > +
> > + Protocol* UNINIT_VAR(save_protocol);
> > + bool replaced_protocol = false;
> Tabs instead of spaces. Delete space before =.
> > +
> > + if (!thd->lex->returning_list.is_empty())
> > + {
> Tabs instead of spaces.
> > + /*increment the status variable. This is useful for feedback
> plugin*/
> I would remove this comment, the function name is suggestive enough.
> > + status_var_increment(thd->status_var.feature_insert_returning);
> > +
> > + /* This is INSERT ... RETURNING. It will return output
> to the client */
> > + if (thd->lex->analyze_stmt)
> > + {
> > + /*
> > + Actually, it is ANALYZE .. INSERT ..
> RETURNING. We need to produce
> > + output and then discard it.
> > + */
> > + result = new (thd->mem_root)
> select_send_analyze(thd);
> > + replaced_protocol = true;
> > + save_protocol = thd->protocol;
> > + thd->protocol = new Protocol_discard(thd);
> > + }
> > + else
> > + {
> > + if (!lex->result && !(result = new
> (thd->mem_root) select_send(thd)))
> > + goto error;
> > + }
> > + }
> > +
> > /* Skip first table, which is the table we are inserting in */
> > TABLE_LIST *second_table= first_table->next_local;
> > /*
> > @@ -4604,18 +4669,33 @@ mysql_execute_command(THD *thd)
> > TODO: fix it by removing the front element (restoring of it
> should
> > be done properly as well)
> > */
> > +
> Trailing whitespaces for this line.
> > + /*
> > + Also, if items are present in returning_list, then we need those
> items
> > + to point to INSERT table during setup_fields() and setup_wild().
> But
> > + it gets masked before that. So we save the values in saved_first,
> > + saved_table_list and saved_first_name_resolution_context before
> they are masked.
> > + We will later swap the saved values with the masked values if
> returning_list
> > + is not empty in INSERT...SELECT...RETURNING.
> This comment text needs to indented 2 more spaces. Remove trailing
> whitespaces. Also, make sure to keep within 80 characters per line.
> > + */
> > +
> > + TABLE_LIST
> *save_first=select_lex->table_list.first;
> Tabs instead of spaces. Add space after =.
> > select_lex->table_list.first= second_table;
> > +
> select_lex->context.saved_table_list=select_lex->context.table_list;
> Tabs instead of spaces. Add space after =.
> > + select_lex->context.saved_name_resolution_table=
> Tabs instead of spaces.
> > + select_lex->context.first_name_resolution_table;
> > select_lex->context.table_list=
> > select_lex->context.first_name_resolution_table= second_table;
> > - res= mysql_insert_select_prepare(thd);
> > - if (!res && (sel_result= new (thd->mem_root) select_insert(thd,
> > -
> first_table,
> > -
> first_table->table,
> > -
> &lex->field_list,
> > -
> &lex->update_list,
> > -
> &lex->value_list,
> > -
> lex->duplicates,
> > -
> lex->ignore)))
> > + res= mysql_insert_select_prepare(thd,lex->result ? lex->result :
> result);
> > + if (!res && (sel_result= new (thd->mem_root) select_insert(thd,
> first_table,
> > +
> first_table->table,
> > +
> &lex->field_list,
> > +
> &lex->update_list,
> > +
> &lex->value_list,
> > +
> lex->duplicates,
> > +
> lex->ignore,
> > +
> lex->result ? lex->result : result,
> > +
> save_first)))
> This is ugly. I suggest you keep just new (thd->mem_root) on one line and
> wrap
> everything to a new line. Something like:
> + if (!res &&
> + (sel_result= new (thd->mem_root)
> + select_insert(thd, first_table,
> + first_table->table,
> + &lex->field_list,
> + &lex->update_list,
> + &lex->value_list,
> + lex->duplicates,
> + lex->ignore,
> + lex->result ? lex->result : result,
> + save_first)))
> >
> > {
> > if (lex->analyze_stmt)
> >
> ((select_result_interceptor*)sel_result)->disable_my_ok_calls();
> > @@ -4630,6 +4710,7 @@ mysql_execute_command(THD *thd)
> > TODO: this is workaround. right way will be move invalidating
> in
> > the unlock procedure.
> > */
> > +
> Extra line added for no good reason.
> > if (!res && first_table->lock_type ==
> TL_WRITE_CONCURRENT_INSERT &&
> > thd->lock)
> > {
> > @@ -4650,8 +4731,13 @@ mysql_execute_command(THD *thd)
> > sel_result->abort_result_set();
> > }
> > delete sel_result;
> > + delete result;
> > + }
> > + if (replaced_protocol)
> > + {
> > + delete thd->protocol;
> > + thd->protocol= save_protocol;
> > }
> > -
> No need to delete this empty line.
> > if (!res && (explain || lex->analyze_stmt))
> > res= thd->lex->explain->send_explain(thd);
> >
> > diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
> > index 8088b6923f2..2f5d0f17451 100644
> > --- a/sql/sql_prepare.cc
> > +++ b/sql/sql_prepare.cc
> > @@ -1296,7 +1296,7 @@ static bool mysql_test_insert(Prepared_statement
> *stmt,
> >
> > if (mysql_prepare_insert(thd, table_list, table_list->table,
> > fields, values, update_fields,
> update_values,
> > - duplic, &unused_conds, FALSE))
> > + duplic, &unused_conds, FALSE,false))
> FALSE mixed with false. As this function call uses FALSE, let's use FALSE
> too.
> I know, the file is inconsistent, it uses FALSE and false at times. :(
> Let's do our best here and not make things stranger than they have to be.
> Space after last comma too.
> Like so: duplic, &unused_conds, FALSE, FALSE))
> > goto error;
> >
> > value_count= values->elements;
> > @@ -2154,7 +2154,7 @@ static int mysql_insert_select_prepare_tester(THD
> *thd)
> > thd->lex->first_select_lex()->context.first_name_resolution_table=
> > second_table;
> >
> > - return mysql_insert_select_prepare(thd);
> > + return mysql_insert_select_prepare(thd,NULL);
> space after , please.
> > }
> >
> >
> > diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
> > index 183a2504b70..c7706ca3c1f 100644
> > --- a/sql/sql_yacc.yy
> > +++ b/sql/sql_yacc.yy
> > @@ -1911,11 +1912,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b,
> size_t *yystacksize);
> > %type <item_basic_constant> text_literal
> >
> > %type <item_list>
> > - expr_list opt_udf_expr_list udf_expr_list when_list
> when_list_opt_else
> > + opt_insert_update
> Trailing whitespace.
> > + opt_select_expressions expr_list
> Trailing whitespace.
> > + opt_udf_expr_list udf_expr_list when_list when_list_opt_else
> > ident_list ident_list_arg opt_expr_list
> > decode_when_list_oracle
> > execute_using
> > execute_params
> > + select_item_list
> >
> > %type <sp_cursor_stmt>
> > sp_cursor_stmt_lex
> > @@ -9223,6 +9226,7 @@ query_specification_start:
> > select_item_list
> > {
> > Select->parsing_place= NO_MATTER;
> > + Select->item_list=*($5);
> Space after =.
> > }
> > ;
> >
> > @@ -9544,9 +9548,24 @@ opt_lock_wait_timeout_new:
> > }
> > ;
> >
> > + /*
> > + Here, we make select_item_list return List<Item> to prevent it
> from adding
> > + everything to SELECT_LEX::item_list. If items are already there
> in the item_list
> > + then using RETURNING with INSERT...SELECT is not possible
> because rules occuring
> > + after insert_values add everything to SELECT_LEX::item_list.
> > + */
> This comment must be wrapped at 80 lines.
> > +
> Delete this final line.
> > select_item_list:
> > select_item_list ',' select_item
> > + {
> > + $1->push_back($3,thd->mem_root);
> Space after ,
> > + $$=$1;
> Space after =
> > + }
> > | select_item
> > + {
> > + if (unlikely(!($$= List<Item>::make(thd->mem_root, $1))))
> > + MYSQL_YYABORT;
> > + }
> > | '*'
> > {
> > Item *item= new (thd->mem_root)
> > @@ -9554,24 +9573,23 @@ select_item_list:
> > NULL, NULL, &star_clex_str);
> > if (unlikely(item == NULL))
> > MYSQL_YYABORT;
> > - if (unlikely(add_item_to_list(thd, item)))
> > + if (unlikely(!($$= List<Item>::make(thd->mem_root, item))))
> > MYSQL_YYABORT;
> > (thd->lex->current_select->with_wild)++;
> > +
> > + (thd->lex->current_select->with_wild)++;
> This double's with_wild. This is a bug.
> > }
> > ;
> >
> > select_item:
> > remember_name select_sublist_qualified_asterisk remember_end
> > {
> > - if (unlikely(add_item_to_list(thd, $2)))
> > - MYSQL_YYABORT;
> > + $$=$2;
> > }
> > | remember_name expr remember_end select_alias
> > {
> > DBUG_ASSERT($1 < $3);
> > -
> > - if (unlikely(add_item_to_list(thd, $2)))
> > - MYSQL_YYABORT;
> > + $$=$2;
> > if ($4.str)
> > {
> > if (unlikely(Lex->sql_command == SQLCOM_CREATE_VIEW &&
> > @@ -13307,13 +13325,15 @@ insert:
> > Select->set_lock_for_tables($3, true);
> > Lex->current_select= Lex->first_select_lex();
> > }
> > - insert_field_spec opt_insert_update
> > - {
> > + insert_field_spec opt_insert_update opt_select_expressions
> > + {
> Trailing whitespace.
> > + if ($9)
> > + Lex->returning_list=*($9);
> > Lex->pop_select(); //main select
> > if (Lex->check_main_unit_semantics())
> > MYSQL_YYABORT;
> > }
> > - ;
> > + ;
> Trailing whitespace tab. Additionally the semicolon is not
> Why change this? The semicolon is now indented wrong. Also trailing tab
> introduced after.
> >
> > replace:
> > REPLACE
> > @@ -13331,13 +13351,15 @@ replace:
> > Select->set_lock_for_tables($3, true);
> > Lex->current_select= Lex->first_select_lex();
> > }
> > - insert_field_spec
> > + insert_field_spec opt_select_expressions
> > {
> > + if ($7)
> > + Lex->returning_list=*($7);
> Space after =.
> > Lex->pop_select(); //main select
> > if (Lex->check_main_unit_semantics())
> > MYSQL_YYABORT;
> > }
> > - ;
> > + ;
> Why change this? The semicolon is now indented wrong.
> >
> > insert_lock_option:
> > /* empty */
> > @@ -13389,8 +13411,8 @@ insert_table:
> > ;
> >
> > insert_field_spec:
> > - insert_values {}
> > - | insert_field_list insert_values {}
> > + insert_values
> Trailing whitespace.
> > + | insert_field_list insert_values
> > | SET
> > {
> > LEX *lex=Lex;
> > @@ -13732,6 +13754,8 @@ single_multi:
> > {
> > if ($3)
> > Select->order_list= *($3);
> > + if($5)
> > + Select->item_list=*($5);
> Space after if. Space after =.
> > Lex->pop_select(); //main select
> > }
> > | table_wild_list
> > @@ -13764,9 +13788,16 @@ single_multi:
> > }
> > ;
> >
> > + /*
> Trailing whitespace.
> > + Return NULL if the rule is empty else return the list of
> items
> Trailing whitespace.
> > + in the select expression
> Trailing whitespce. Put a fullstop at the end of the sentence.
> > + */
> Comment should start at the beginning of line, not indented in this case.
> > opt_select_expressions:
> > - /* empty */
> > - | RETURNING_SYM select_item_list
> > + /* empty */ {$$=NULL;}
> Space after =. Space after {. Space after ;
> > + | RETURNING_SYM select_item_list
> > + {
> > + $$=$2;
> Space after =.
> > + }
> > ;
> >
> > table_wild_list:
>
>
References