7 Aug
2019
7 Aug
'19
4:55 p.m.
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@mariadb.org> 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: > >