diff --git a/mysql-test/suite/compat/oracle/r/binlog_stm_ps.result b/mysql-test/suite/compat/oracle/r/binlog_stm_ps.result
index c60e349..7ed0fa6 100644
--- a/mysql-test/suite/compat/oracle/r/binlog_stm_ps.result
+++ b/mysql-test/suite/compat/oracle/r/binlog_stm_ps.result
@@ -3,7 +3,8 @@ SET sql_mode=ORACLE;
 # MDEV-10801 sql_mode: dynamic SQL placeholders
 #
 CREATE TABLE t1 (a INT, b INT);
-SET @a=10, @b=20;
+SET @a=10;
+SET @b=20;
 PREPARE stmt FROM 'INSERT INTO t1 VALUES (?,?)';
 EXECUTE stmt USING @a, @b;
 PREPARE stmt FROM 'INSERT INTO t1 VALUES (:a,:b)';
diff --git a/mysql-test/suite/compat/oracle/r/ps.result b/mysql-test/suite/compat/oracle/r/ps.result
index ed7cb4c..cfeaa84 100644
--- a/mysql-test/suite/compat/oracle/r/ps.result
+++ b/mysql-test/suite/compat/oracle/r/ps.result
@@ -2,7 +2,8 @@ SET sql_mode=ORACLE;
 #
 # MDEV-10801 sql_mode: dynamic SQL placeholders
 #
-SET @a=10, @b=20;
+SET @a=10;
+SET @b=20;
 PREPARE stmt FROM 'SELECT ?,?';
 EXECUTE stmt USING @a, @b;
 ?	?
diff --git a/mysql-test/suite/compat/oracle/r/sp-row.result b/mysql-test/suite/compat/oracle/r/sp-row.result
index 16421cd..8df410c 100644
--- a/mysql-test/suite/compat/oracle/r/sp-row.result
+++ b/mysql-test/suite/compat/oracle/r/sp-row.result
@@ -365,7 +365,11 @@ AS
 rec ROW(a INT,b DOUBLE,c VARCHAR(10));
 a INT;
 BEGIN
-SET @a= 10, rec.a=10, rec.b=20, rec.c= 'test', a= 5;
+SET @a= 10;
+SET rec.a= 10;
+SET rec.b= 20;
+SET rec.c= 'test';
+SET a= 5;
 SELECT rec.a, rec.b, rec.c, a;
 END;
 $$
diff --git a/mysql-test/suite/compat/oracle/r/sp.result b/mysql-test/suite/compat/oracle/r/sp.result
index 8f3ebd2..fcffe39 100644
--- a/mysql-test/suite/compat/oracle/r/sp.result
+++ b/mysql-test/suite/compat/oracle/r/sp.result
@@ -134,7 +134,8 @@ SET p1='p1new';
 SET p2='p2new';
 END;
 /
-SET @p1='p1', @p2='p2';
+SET @p1='p1';
+SET @p2='p2';
 CALL p1(@p1, @p2);
 SELECT @p1, @p2;
 @p1	@p2
diff --git a/mysql-test/suite/compat/oracle/t/binlog_stm_ps.test b/mysql-test/suite/compat/oracle/t/binlog_stm_ps.test
index 996ef57..fe72e18 100644
--- a/mysql-test/suite/compat/oracle/t/binlog_stm_ps.test
+++ b/mysql-test/suite/compat/oracle/t/binlog_stm_ps.test
@@ -12,7 +12,8 @@ SET sql_mode=ORACLE;
 --echo #
 
 CREATE TABLE t1 (a INT, b INT);
-SET @a=10, @b=20;
+SET @a=10;
+SET @b=20;
 PREPARE stmt FROM 'INSERT INTO t1 VALUES (?,?)';
 EXECUTE stmt USING @a, @b;
 PREPARE stmt FROM 'INSERT INTO t1 VALUES (:a,:b)';
diff --git a/mysql-test/suite/compat/oracle/t/ps.test b/mysql-test/suite/compat/oracle/t/ps.test
index 08bb957..f681841 100644
--- a/mysql-test/suite/compat/oracle/t/ps.test
+++ b/mysql-test/suite/compat/oracle/t/ps.test
@@ -4,7 +4,8 @@ SET sql_mode=ORACLE;
 --echo # MDEV-10801 sql_mode: dynamic SQL placeholders
 --echo #
 
-SET @a=10, @b=20;
+SET @a=10;
+SET @b=20;
 PREPARE stmt FROM 'SELECT ?,?';
 EXECUTE stmt USING @a, @b;
 PREPARE stmt FROM 'SELECT :a,:b';
diff --git a/mysql-test/suite/compat/oracle/t/sp-row.test b/mysql-test/suite/compat/oracle/t/sp-row.test
index 94cc5db..8647b46 100644
--- a/mysql-test/suite/compat/oracle/t/sp-row.test
+++ b/mysql-test/suite/compat/oracle/t/sp-row.test
@@ -472,7 +472,11 @@ AS
   rec ROW(a INT,b DOUBLE,c VARCHAR(10));
   a INT;
 BEGIN
-  SET @a= 10, rec.a=10, rec.b=20, rec.c= 'test', a= 5;
+  SET @a= 10;
+  SET rec.a= 10;
+  SET rec.b= 20;
+  SET rec.c= 'test';
+  SET a= 5;
   SELECT rec.a, rec.b, rec.c, a;
 END;
 $$
diff --git a/mysql-test/suite/compat/oracle/t/sp.test b/mysql-test/suite/compat/oracle/t/sp.test
index 7e9e76d..7f07abb 100644
--- a/mysql-test/suite/compat/oracle/t/sp.test
+++ b/mysql-test/suite/compat/oracle/t/sp.test
@@ -132,7 +132,8 @@ BEGIN
 END;
 /
 DELIMITER ;/
-SET @p1='p1', @p2='p2';
+SET @p1='p1';
+SET @p2='p2';
 CALL p1(@p1, @p2);
 SELECT @p1, @p2;
 DROP PROCEDURE p1;
diff --git a/sql/item.h b/sql/item.h
index 13f6f20..1cb5062 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -2088,6 +2088,7 @@ class Item_args
 
 class Item_spvar_args: public Item_args
 {
+protected:
   TABLE *m_table;
 public:
   Item_spvar_args():Item_args(), m_table(NULL) { }
@@ -2943,6 +2944,7 @@ class Item_field_row: public Item_field,
   bool element_index_by_name(uint *idx, const LEX_CSTRING &name) const;
   Item* element_index(uint i) { return arg_count ? args[i] : this; }
   Item** addr(uint i) { return arg_count ? args + i : NULL; }
+  TABLE * table() { return m_table; }
   bool check_cols(uint c)
   {
     if (cols() != c)
@@ -2952,6 +2954,7 @@ class Item_field_row: public Item_field,
     }
     return false;
   }
+
 };
 
 
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index cb8b083..82ce98e 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -7780,3 +7780,5 @@ ER_SP_STACK_TRACE
         eng "At line %u in %s"
 ER_UPDATED_COLUMN_ONLY_ONCE
         eng "The column %s cannot be changed more than once in a single UPDATE statement"
+ER_SP_GROUPSET_PROHIBITED
+        eng "Group assignment is not allowed.
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index a684265..191d804 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -118,7 +118,9 @@ bool sp_rcontext::init_var_table(THD *thd,
 
   DBUG_ASSERT(field_def_lst.elements == m_root_parsing_ctx->max_var_index());
 
-  if (!(m_var_table= create_virtual_tmp_table(thd, field_def_lst)))
+  if (!(m_var_table= create_virtual_tmp_table(thd, field_def_lst,
+                                              thd->variables.sql_mode &
+                                                   MODE_SET_CONSISTENCY)))
     return true;
 
   return false;
@@ -331,7 +333,7 @@ bool sp_rcontext::init_var_items(THD *thd,
 bool Item_spvar_args::row_create_items(THD *thd, List<Spvar_definition> *list)
 {
   DBUG_ASSERT(list);
-  if (!(m_table= create_virtual_tmp_table(thd, *list)))
+  if (!(m_table= create_virtual_tmp_table(thd, *list, true)))
     return true;
 
   if (alloc_arguments(thd, list->elements))
@@ -568,6 +570,40 @@ uint sp_rcontext::exit_handler(Diagnostics_area *da)
   DBUG_RETURN(continue_ip);
 }
 
+int sp_rcontext::save_set_n_switch(THD *thd, uint idx, Item **value)
+{
+  Item *dst= m_var_items[idx];
+  if (dst->cmp_type() != ROW_RESULT)
+  {
+    Field *field= m_var_table->field[idx];
+    save_or_restore_value(m_var_table, field);
+
+    // evaluate value
+    int evalret= 0;
+    if (!value)
+      field->set_null();
+    else
+      evalret= sp_eval_expr(thd, dst, field, value);
+
+    // move pointer on field in record[1]
+    if (!evalret)
+    {
+      my_ptrdiff_t rowoffset= m_var_table->record[1] - m_var_table->record[0];
+      field->move_field_offset(rowoffset);
+    }
+    return evalret;
+  }
+
+  return set_variable(thd, idx, value);
+}
+
+
+void  sp_rcontext::switch_to_new_value(uint idx)
+{
+  if (m_var_items[idx]->cmp_type() != ROW_RESULT)
+    switch_to_value_in_record(m_var_table, m_var_table->field[idx], 0);
+}
+
 
 int sp_rcontext::set_variable(THD *thd, uint idx, Item **value)
 {
@@ -630,6 +666,75 @@ void sp_rcontext::set_variable_row_field_to_null(THD *thd,
 }
 
 
+int sp_rcontext::save_set_n_switch_row_field(THD *thd, uint var_idx,
+                                             uint field_idx, Item **value)
+{
+  Item *dst= get_item(var_idx);
+  DBUG_ASSERT(dst->type() == Item::FIELD_ITEM);
+  DBUG_ASSERT(dst->cmp_type() == ROW_RESULT);
+  Item_field_row *item_field_row= (Item_field_row*) dst;
+
+  TABLE *tbl=item_field_row->table();
+  save_or_restore_value(tbl, tbl->field[field_idx]);
+
+  // evaluate value
+  int evalret= set_variable_row_field(thd, var_idx, field_idx, value);
+
+  // move pointer on field in record[1]
+  if (!evalret)
+      switch_to_value_in_record(tbl, tbl->field[field_idx], 1);
+
+  return evalret;
+}
+
+
+void sp_rcontext::save_or_restore_value(TABLE *tbl, Field *f)
+{
+  my_ptrdiff_t rowoffset= tbl->record[1] - tbl->record[0];
+
+  if (f->ptr >= tbl->record[1] && f->ptr <
+      tbl->record[1] + tbl->s->rec_buff_length)
+  {
+    // Variable is already changed. Restore value in record[0] and point on it.
+    memcpy(f->ptr - rowoffset, f->ptr, f->pack_length());
+    if (f->is_real_null())
+      f->set_null(-rowoffset);
+    else
+      f->set_notnull(-rowoffset);
+    f->move_field_offset(-rowoffset);
+  }
+  else
+  {
+    memcpy(f->ptr + rowoffset, f->ptr, f->pack_length());
+    if (f->is_real_null())
+      f->set_null(rowoffset);
+    else
+      f->set_notnull(rowoffset);
+  }
+}
+
+
+void sp_rcontext::switch_to_new_value_row_field(uint var_idx, uint field_idx)
+{
+  Item_field_row *item_field_row= (Item_field_row*) get_item(var_idx);
+  TABLE *tbl=item_field_row->table();
+  switch_to_value_in_record(tbl, tbl->field[field_idx], 0);
+}
+
+
+void sp_rcontext::switch_to_value_in_record(TABLE *tbl, Field *f,
+                                            uint record_number)
+{
+  if (!(f->ptr >= tbl->record[record_number] && f->ptr <
+      tbl->record[record_number] + tbl->s->rec_buff_length))
+  {
+    my_ptrdiff_t rowoffset= tbl->record[1] - tbl->record[0];
+    f->move_field_offset(record_number ? rowoffset : -rowoffset);
+  }
+}
+
+
+
 int sp_rcontext::set_variable_row_field(THD *thd, uint var_idx, uint field_idx,
                                         Item **value)
 {
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
index 9846451..281a00e 100644
--- a/sql/sp_rcontext.h
+++ b/sql/sp_rcontext.h
@@ -187,6 +187,15 @@ class sp_rcontext : public Sql_alloc
   /////////////////////////////////////////////////////////////////////////
 
   int set_variable(THD *thd, uint var_idx, Item **value);
+  void switch_to_value_in_record(TABLE *tbl, Field *f, uint record_number);
+  void save_or_restore_value(TABLE *tbl, Field *f);
+  int save_set_n_switch(THD *thd, uint var_idx, Item **value);
+  void switch_to_new_value(uint idx);
+
+  int save_set_n_switch_row_field(THD *thd, uint var_idx, uint field_idx,
+                             Item **value);
+  void switch_to_new_value_row_field(uint var_idx, uint field_idx);
+
   void set_variable_row_field_to_null(THD *thd, uint var_idx, uint field_idx);
   int set_variable_row_field(THD *thd, uint var_idx, uint field_idx,
                              Item **value);
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 1e69535..fc86fa0 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -3908,6 +3908,7 @@ Statement_map::~Statement_map()
   my_hash_free(&st_hash);
 }
 
+
 bool my_var_user::set(THD *thd, Item *item)
 {
   Item_func_set_user_var *suv= new (thd->mem_root) Item_func_set_user_var(thd, &name, item);
@@ -3915,14 +3916,57 @@ bool my_var_user::set(THD *thd, Item *item)
   return suv->fix_fields(thd, 0) || suv->update();
 }
 
+
+bool my_var_user::save_set_n_switch(THD *thd, Item *item)
+{
+  suv= new (thd->mem_root) Item_func_set_user_var(thd, &name, item);
+  suv->save_item_result(item);
+  return 0;
+}
+
+
+bool my_var_user::switch_to_new_value(THD *thd)
+{
+  return suv->fix_fields(thd, 0) || suv->update();
+}
+
 bool my_var_sp::set(THD *thd, Item *item)
 {
   return thd->spcont->set_variable(thd, offset, &item);
 }
 
+
+bool my_var_sp::save_set_n_switch(THD *thd, Item *item)
+{
+  return thd->spcont->save_set_n_switch(thd, offset, &item);
+}
+
+
+bool my_var_sp::switch_to_new_value(THD *thd)
+{
+  thd->spcont->switch_to_new_value(offset);
+  return 0;
+}
+
+
 bool my_var_sp_row_field::set(THD *thd, Item *item)
 {
-  return thd->spcont->set_variable_row_field(thd, offset, m_field_offset, &item);
+  return thd->spcont->set_variable_row_field(thd, offset, m_field_offset,
+                                             &item);
+}
+
+
+bool my_var_sp_row_field::save_set_n_switch(THD *thd, Item *item)
+{
+  return thd->spcont->save_set_n_switch_row_field(thd, offset, m_field_offset,
+                                                  &item);
+}
+
+
+bool my_var_sp_row_field::switch_to_new_value(THD *thd)
+{
+  thd->spcont->switch_to_new_value_row_field(offset, m_field_offset);
+  return 0;
 }
 
 
@@ -3933,6 +3977,24 @@ bool select_dumpvar::send_data_to_var_list(List<Item> &items)
   List_iterator<Item> it(items);
   Item *item;
   my_var *mv;
+
+  if (var_list.elements > 1 &&
+      (thd->variables.sql_mode & MODE_SET_CONSISTENCY))
+  {
+    while ((mv= var_li++) && (item= it++))
+    {
+      if (mv->save_set_n_switch(thd, item))
+        DBUG_RETURN(true);
+    }
+    var_li.rewind();
+    while ((mv= var_li++))
+    {
+      if (mv->switch_to_new_value(thd))
+        DBUG_RETURN(true);
+    }
+    DBUG_RETURN(false);
+  }
+
   while ((mv= var_li++) && (item= it++))
   {
     if (mv->set(thd, item))
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 09f5ad2..cf55e27 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -5637,6 +5637,8 @@ class my_var : public Sql_alloc  {
   my_var(const LEX_CSTRING *j, enum type s) : name(*j), scope(s) { }
   virtual ~my_var() {}
   virtual bool set(THD *thd, Item *val) = 0;
+  virtual bool save_set_n_switch(THD *thd, Item *val) = 0;
+  virtual bool switch_to_new_value(THD *thd) = 0;
   virtual class my_var_sp *get_my_var_sp() { return NULL; }
 };
 
@@ -5654,6 +5656,9 @@ class my_var_sp: public my_var {
     : my_var(j, LOCAL_VAR), m_type_handler(type_handler), offset(o), sp(s) { }
   ~my_var_sp() { }
   bool set(THD *thd, Item *val);
+  bool save_set_n_switch(THD *thd, Item *item);
+  bool switch_to_new_value(THD *thd);
+
   my_var_sp *get_my_var_sp() { return this; }
   const Type_handler *type_handler() const { return m_type_handler; }
 };
@@ -5672,14 +5677,19 @@ class my_var_sp_row_field: public my_var_sp
     m_field_offset(field_idx)
   { }
   bool set(THD *thd, Item *val);
+  bool save_set_n_switch(THD *thd, Item *item);
+  bool switch_to_new_value(THD *thd);
 };
 
 class my_var_user: public my_var {
 public:
+  Item_func_set_user_var *suv;
   my_var_user(const LEX_CSTRING *j)
     : my_var(j, SESSION_VAR) { }
   ~my_var_user() { }
   bool set(THD *thd, Item *val);
+  bool save_set_n_switch(THD *thd, Item *item);
+  bool switch_to_new_value(THD *thd);
 };
 
 class select_dumpvar :public select_result_interceptor {
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 3391ca3..ea5e619 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -17614,6 +17614,11 @@ void Virtual_tmp_table::setup_field_pointers()
 
 bool Virtual_tmp_table::open()
 {
+  return open(false);
+}
+
+bool Virtual_tmp_table::open(bool alloc_second_record)
+{
   // Make sure that we added all the fields we planned to:
   DBUG_ASSERT(s->fields == m_alloced_field_count);
   field[s->fields]= NULL;            // mark the end of the list
@@ -17624,12 +17629,16 @@ bool Virtual_tmp_table::open()
   s->rec_buff_length= ALIGN_SIZE(s->reclength + 1);
   if (!(record[0]= (uchar*) in_use->alloc(s->rec_buff_length)))
     return true;
+  if (alloc_second_record &&
+      !(record[1]= (uchar*) in_use->calloc(s->rec_buff_length)))
+    return true;
   if (null_pack_length)
   {
     null_flags= (uchar*) record[0];
     s->null_bytes= s->null_bytes_for_compare= null_pack_length;
   }
   setup_field_pointers();
+
   return false;
 }
 
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 9f91733..6b0ce95 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -2121,6 +2121,7 @@ class Virtual_tmp_table: public TABLE
     @return true  - on error (e.g. could not allocate the record buffer).
   */
   bool open();
+  bool open(bool alloc_second_record);
 };
 
 
@@ -2145,7 +2146,8 @@ class Virtual_tmp_table: public TABLE
 */
 
 inline TABLE *
-create_virtual_tmp_table(THD *thd, List<Spvar_definition> &field_list)
+create_virtual_tmp_table(THD *thd, List<Spvar_definition> &field_list,
+                         bool alloc_second_record)
 {
   Virtual_tmp_table *table;
   if (!(table= new(thd) Virtual_tmp_table(thd)))
@@ -2163,7 +2165,7 @@ create_virtual_tmp_table(THD *thd, List<Spvar_definition> &field_list)
 
   if (table->init(field_list.elements) ||
       table->add(field_list) ||
-      table->open())
+      table->open(alloc_second_record))
   {
     delete table;
     return NULL;
@@ -2190,7 +2192,7 @@ create_virtual_tmp_table(THD *thd, Field *field)
     return NULL;
   if (table->init(1) ||
       table->add(field) ||
-      table->open())
+      table->open(false))
   {
     delete table;
     return NULL;
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 24fee15..ed017f7 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -15071,7 +15071,12 @@ start_option_value_list_following_option_type:
 /* Remainder of the option value list after first option value. */
 option_value_list_continued:
           /* empty */
-        | ',' option_value_list
+        | ','
+          {
+            if (thd->variables.sql_mode & MODE_SET_CONSISTENCY)
+              my_yyabort_error((ER_SP_GROUPSET_PROHIBITED, MYF(0)));
+          }
+          option_value_list
         ;
 
 /* Repeating list of option values after first option value. */
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index 053c262..46f899a 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -15271,7 +15271,12 @@ start_option_value_list_following_option_type:
 /* Remainder of the option value list after first option value. */
 option_value_list_continued:
           /* empty */
-        | ',' option_value_list
+        | ','
+          {
+            if (thd->variables.sql_mode & MODE_SET_CONSISTENCY)
+              my_yyabort_error((ER_SP_GROUPSET_PROHIBITED, MYF(0)));
+          }
+          option_value_list
         ;
 
 /* Repeating list of option values after first option value. */
