From ce2c46ddd6676960e0c002aa83c7913e85ebdc47 Mon Sep 17 00:00:00 2001
From: unknown <bell@sanja.is.com.ua>
Date: Fri, 24 Oct 2003 17:28:32 +0300
Subject: [PATCH] support 'parallel' updates and rallback of whole statement in
 case of error in evalueting value which should be assigned (BUG#1484)

mysql-test/r/variables.result:
  test of swaping variables
mysql-test/t/variables.test:
  test of swaping variables
sql/item_func.cc:
  new method of user variables to pre-fetch value
sql/item_func.h:
  new method of user variables to pre-fetch value
sql/set_var.cc:
  check() methods fetch & store value
sql/set_var.h:
  check() methods fetch & store value
---
 mysql-test/r/variables.result | 24 ++++++++++++
 mysql-test/t/variables.test   | 17 +++++++++
 sql/item_func.cc              | 71 +++++++++++++++++++++++++++++------
 sql/item_func.h               |  9 +++++
 sql/set_var.cc                | 71 ++++++++++++-----------------------
 sql/set_var.h                 |  3 +-
 6 files changed, 136 insertions(+), 59 deletions(-)

diff --git a/mysql-test/r/variables.result b/mysql-test/r/variables.result
index d65b44e784..52ea28e607 100644
--- a/mysql-test/r/variables.result
+++ b/mysql-test/r/variables.result
@@ -365,3 +365,27 @@ ERROR HY000: Unknown system variable 'xxxxxxxxxx'
 select 1;
 1
 1
+select @@global.max_user_connections,@@local.max_join_size;
+@@global.max_user_connections	@@session.max_join_size
+100	200
+set @svc=@@global.max_user_connections, @svj=@@local.max_join_size;
+select @@global.max_user_connections,@@local.max_join_size;
+@@global.max_user_connections	@@session.max_join_size
+100	200
+set @@global.max_user_connections=111,@@local.max_join_size=222;
+select @@global.max_user_connections,@@local.max_join_size;
+@@global.max_user_connections	@@session.max_join_size
+111	222
+set @@global.max_user_connections=@@local.max_join_size,@@local.max_join_size=@@global.max_user_connections;
+select @@global.max_user_connections,@@local.max_join_size;
+@@global.max_user_connections	@@session.max_join_size
+222	111
+set @@global.max_user_connections=@svc, @@local.max_join_size=@svj;
+select @@global.max_user_connections,@@local.max_join_size;
+@@global.max_user_connections	@@session.max_join_size
+100	200
+set @a=1, @b=2;
+set @a=@b, @b=@a;
+select @a, @b;
+@a	@b
+2	1
diff --git a/mysql-test/t/variables.test b/mysql-test/t/variables.test
index 396fb3ac72..c53559ed4c 100644
--- a/mysql-test/t/variables.test
+++ b/mysql-test/t/variables.test
@@ -263,3 +263,20 @@ drop table t1,t2;
 --error 1193
 select @@xxxxxxxxxx;
 select 1;
+
+#
+# swap
+#
+select @@global.max_user_connections,@@local.max_join_size;
+set @svc=@@global.max_user_connections, @svj=@@local.max_join_size;
+select @@global.max_user_connections,@@local.max_join_size;
+set @@global.max_user_connections=111,@@local.max_join_size=222;
+select @@global.max_user_connections,@@local.max_join_size;
+set @@global.max_user_connections=@@local.max_join_size,@@local.max_join_size=@@global.max_user_connections;
+select @@global.max_user_connections,@@local.max_join_size;
+set @@global.max_user_connections=@svc, @@local.max_join_size=@svj;
+select @@global.max_user_connections,@@local.max_join_size;
+set @a=1, @b=2;
+set @a=@b, @b=@a;
+select @a, @b;
+
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 929f85a112..ce0614f1e7 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -2255,6 +2255,54 @@ String *user_var_entry::val_str(my_bool *null_value, String *str,
   return(str);
 }
 
+/*
+  This functions is invoked on SET @variable or @variable:= expression.
+  Evaluete (and check expression), store results.
+
+  SYNOPSYS
+    Item_func_set_user_var::check()
+
+  NOTES
+    For now it always return OK. All problem with value evalueting
+    will be catched by thd->net.report_error check in sql_set_variables().
+
+  RETURN
+    0 - OK.
+*/
+
+bool
+Item_func_set_user_var::check()
+{
+  bool res;
+  DBUG_ENTER("Item_func_set_user_var::check");
+  LINT_INIT(res);
+
+  switch (cached_result_type) {
+  case REAL_RESULT:
+  {
+    save_result.vreal= args[0]->val();
+    break;
+  }
+  case INT_RESULT:
+  {
+    save_result.vint= args[0]->val_int();
+    break;
+  }
+  break;
+  case STRING_RESULT:
+  {
+    save_result.vstr= args[0]->val_str(&value);
+    break;
+  }
+  case ROW_RESULT:
+  default:
+    // This case should never be choosen
+    DBUG_ASSERT(0);
+    break;
+  }
+  DBUG_RETURN(0);
+}
+
 
 /*
   This functions is invoked on SET @variable or @variable:= expression.
@@ -2282,29 +2330,27 @@ Item_func_set_user_var::update()
   switch (cached_result_type) {
   case REAL_RESULT:
   {
-    double value=args[0]->val();
-    res= update_hash((void*) &value,sizeof(value), REAL_RESULT, 
-		     &my_charset_bin, DERIVATION_NONE);
+    res= update_hash((void*) &save_result.vreal,sizeof(save_result.vreal),
+		     REAL_RESULT, &my_charset_bin, DERIVATION_NONE);
     break;
   }
   case INT_RESULT:
   {
-    longlong value=args[0]->val_int();
-    res= update_hash((void*) &value, sizeof(longlong), INT_RESULT,
-		     &my_charset_bin, DERIVATION_NONE);
+    res= update_hash((void*) &save_result.vint, sizeof(save_result.vint),
+		     INT_RESULT, &my_charset_bin, DERIVATION_NONE);
     break;
   }
   break;
   case STRING_RESULT:
   {
-    String *tmp;
-    tmp=args[0]->val_str(&value);
-    if (!tmp)					// Null value
+    if (!save_result.vstr)					// Null value
       res= update_hash((void*) 0, 0, STRING_RESULT, &my_charset_bin,
 		       DERIVATION_NONE);
     else
-      res= update_hash((void*) tmp->ptr(), tmp->length(), STRING_RESULT,
-		       tmp->charset(), args[0]->collation.derivation);
+      res= update_hash((void*) save_result.vstr->ptr(),
+		       save_result.vstr->length(), STRING_RESULT,
+		       save_result.vstr->charset(),
+		       args[0]->collation.derivation);
     break;
   }
   case ROW_RESULT:
@@ -2319,18 +2365,21 @@ Item_func_set_user_var::update()
 
 double Item_func_set_user_var::val()
 {
+  check();
   update();					// Store expression
   return entry->val(&null_value);
 }
 
 longlong Item_func_set_user_var::val_int()
 {
+  check();
   update();					// Store expression
   return entry->val_int(&null_value);
 }
 
 String *Item_func_set_user_var::val_str(String *str)
 {
+  check();
   update();					// Store expression
   return entry->val_str(&null_value, str, decimals);
 }
diff --git a/sql/item_func.h b/sql/item_func.h
index 8086e65786..fe9c264521 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -900,6 +900,14 @@ class Item_func_set_user_var :public Item_func
   user_var_entry *entry;
   char buffer[MAX_FIELD_WIDTH];
   String value;
+  union
+  {
+    longlong vint;
+    double vreal;
+    String *vstr;
+  } save_result;
+  String save_buff;
+  
 
 public:
   Item_func_set_user_var(LEX_STRING a,Item *b)
@@ -910,6 +918,7 @@ public:
   String *val_str(String *str);
   bool update_hash(void *ptr, uint length, enum Item_result type, 
   		   CHARSET_INFO *cs, Derivation dv);
+  bool check();
   bool update();
   enum Item_result result_type () const { return cached_result_type; }
   bool fix_fields(THD *thd, struct st_table_list *tables, Item **ref);
diff --git a/sql/set_var.cc b/sql/set_var.cc
index d4030c9d1a..d09544697e 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -716,6 +716,12 @@ struct show_var_st init_vars[]= {
 };
 
 
+bool sys_var::check(THD *thd, set_var *var)
+{
+  var->save_result.ulonglong_value= var->value->val_int();
+  return 0;
+}
+
 /*
   Functions to check and update variables
 */
@@ -964,12 +970,9 @@ static void fix_max_connections(THD *thd, enum_var_type type)
   resize_thr_alarm(max_connections + max_insert_delayed_threads + 10);
 }
 
-
 bool sys_var_long_ptr::update(THD *thd, set_var *var)
 {
-  ulonglong tmp= var->value->val_int();
-  if (thd->net.report_error)
-    return 1;
+  ulonglong tmp= var->save_result.ulonglong_value;
   pthread_mutex_lock(&LOCK_global_system_variables);
   if (option_limits)
     *value= (ulong) getopt_ull_limit_value(tmp, option_limits);
@@ -988,9 +991,7 @@ void sys_var_long_ptr::set_default(THD *thd, enum_var_type type)
 
 bool sys_var_ulonglong_ptr::update(THD *thd, set_var *var)
 {
-  ulonglong tmp= var->value->val_int();
-  if (thd->net.report_error)
-    return 1;
+  ulonglong tmp= var->save_result.ulonglong_value;
   pthread_mutex_lock(&LOCK_global_system_variables);
   if (option_limits)
     *value= (ulonglong) getopt_ull_limit_value(tmp, option_limits);
@@ -1037,9 +1038,7 @@ byte *sys_var_enum::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
 
 bool sys_var_thd_ulong::update(THD *thd, set_var *var)
 {
-  ulonglong tmp= var->value->val_int();
-  if (thd->net.report_error)
-    return 1;
+  ulonglong tmp= var->save_result.ulonglong_value;
 
   /* Don't use bigger value than given with --maximum-variable-name=.. */
   if ((ulong) tmp > max_system_variables.*offset)
@@ -1078,9 +1077,7 @@ byte *sys_var_thd_ulong::value_ptr(THD *thd, enum_var_type type,
 
 bool sys_var_thd_ha_rows::update(THD *thd, set_var *var)
 {
-  ulonglong tmp= var->value->val_int();
-  if (thd->net.report_error)
-    return 1;
+  ulonglong tmp= var->save_result.ulonglong_value;
 
   /* Don't use bigger value than given with --maximum-variable-name=.. */
   if ((ha_rows) tmp > max_system_variables.*offset)
@@ -1123,12 +1120,9 @@ byte *sys_var_thd_ha_rows::value_ptr(THD *thd, enum_var_type type,
   return (byte*) &(thd->variables.*offset);
 }
 
-
 bool sys_var_thd_ulonglong::update(THD *thd,  set_var *var)
 {
-  ulonglong tmp= var->value->val_int();
-  if (thd->net.report_error)
-    return 1;
+  ulonglong tmp= var->save_result.ulonglong_value;
 
   if ((ulonglong) tmp > max_system_variables.*offset)
     tmp= max_system_variables.*offset;
@@ -1700,9 +1694,7 @@ void sys_var_collation_server::set_default(THD *thd, enum_var_type type)
 
 bool sys_var_key_buffer_size::update(THD *thd, set_var *var)
 {
-  ulonglong tmp= var->value->val_int();
-  if (thd->net.report_error)
-    return 1;
+  ulonglong tmp= var->save_result.ulonglong_value;
 
   NAMED_LIST *list;
   LEX_STRING *base_name= &var->base;
@@ -1799,11 +1791,7 @@ int set_var_collation_client::update(THD *thd)
 
 bool sys_var_timestamp::update(THD *thd,  set_var *var)
 {
-  time_t tmp= (time_t) var->value->val_int();
-  if (thd->net.report_error)
-    return 1;
-
-  thd->set_time(tmp);
+  thd->set_time((time_t) var->save_result.ulonglong_value);
   return 0;
 }
 
@@ -1824,11 +1812,7 @@ byte *sys_var_timestamp::value_ptr(THD *thd, enum_var_type type,
 
 bool sys_var_last_insert_id::update(THD *thd, set_var *var)
 {
-  ulonglong tmp= var->value->val_int();
-  if (thd->net.report_error)
-    return 1;
-
-  thd->insert_id(tmp);
+  thd->insert_id(var->save_result.ulonglong_value);
   return 0;
 }
 
@@ -1843,11 +1827,7 @@ byte *sys_var_last_insert_id::value_ptr(THD *thd, enum_var_type type,
 
 bool sys_var_insert_id::update(THD *thd, set_var *var)
 {
-  ulonglong tmp= var->value->val_int();
-  if (thd->net.report_error)
-    return 1;
-
-  thd->next_insert_id= tmp;
+  thd->next_insert_id= var->save_result.ulonglong_value;
   return 0;
 }
 
@@ -1860,6 +1840,7 @@ byte *sys_var_insert_id::value_ptr(THD *thd, enum_var_type type,
 
 bool sys_var_pseudo_thread_id::check(THD *thd, set_var *var)
 {
+  var->save_result.ulonglong_value= var->value->val_int();
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
   if (thd->master_access & SUPER_ACL)
     return 0;
@@ -1887,6 +1868,7 @@ bool sys_var_slave_skip_counter::check(THD *thd, set_var *var)
   }
   pthread_mutex_unlock(&active_mi->rli.run_lock);
   UNLOCK_ACTIVE_MI;
+  var->save_result.ulong_value= (ulong) var->value->val_int();
   return result;
 }
 
@@ -1903,7 +1885,7 @@ bool sys_var_slave_skip_counter::update(THD *thd, set_var *var)
   if (!active_mi->rli.slave_running)
   {
     pthread_mutex_lock(&active_mi->rli.data_lock);
-    active_mi->rli.slave_skip_counter= (ulong) var->value->val_int();
+    active_mi->rli.slave_skip_counter= var->save_result.ulong_value;
     pthread_mutex_unlock(&active_mi->rli.data_lock);
   }
   pthread_mutex_unlock(&active_mi->rli.run_lock);
@@ -1914,21 +1896,13 @@ bool sys_var_slave_skip_counter::update(THD *thd, set_var *var)
 
 bool sys_var_rand_seed1::update(THD *thd, set_var *var)
 {
-  ulong tmp= (ulong) var->value->val_int();
-  if (thd->net.report_error)
-    return 1;
-
-  thd->rand.seed1= tmp;
+  thd->rand.seed1= (ulong) var->save_result.ulonglong_value;
   return 0;
 }
 
 bool sys_var_rand_seed2::update(THD *thd, set_var *var)
 {
-  ulong tmp= (ulong) var->value->val_int();
-  if (thd->net.report_error)
-    return 1;
-
-  thd->rand.seed2= tmp;
+  thd->rand.seed2= (ulong) var->save_result.ulonglong_value;
   return 0;
 }
 
@@ -2153,6 +2127,8 @@ int sql_set_variables(THD *thd, List<set_var_base> *var_list)
     if ((error=var->check(thd)))
       DBUG_RETURN(error);
   }
+  if (thd->net.report_error)
+    DBUG_RETURN(1);
   it.rewind();
   while ((var=it++))
     error|= var->update(thd);			// Returns 0, -1 or 1
@@ -2215,7 +2191,8 @@ int set_var::update(THD *thd)
 
 int set_var_user::check(THD *thd)
 {
-  return user_var_item->fix_fields(thd,0, (Item**) 0) ? -1 : 0;
+  return (user_var_item->fix_fields(thd,0, (Item**) 0) ||
+	  user_var_item->check()) ? -1 : 0;
 }
 
 
diff --git a/sql/set_var.h b/sql/set_var.h
index 4c67c5ccc5..752f275c9f 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -57,7 +57,7 @@ public:
     :name(name_arg),after_update(func)
   {}
   virtual ~sys_var() {}
-  virtual bool check(THD *thd, set_var *var) { return 0; }
+  virtual bool check(THD *thd, set_var *var);
   bool check_enum(THD *thd, set_var *var, TYPELIB *enum_names);
   bool check_set(THD *thd, set_var *var, TYPELIB *enum_names);
   virtual bool update(THD *thd, set_var *var)=0;
@@ -656,6 +656,7 @@ public:
   {
     CHARSET_INFO *charset;
     ulong ulong_value;
+    ulonglong ulonglong_value;
   } save_result;
   LEX_STRING base;			/* for structs */
 
-- 
2.30.9