diff --git a/mysql-test/r/gis-debug.result b/mysql-test/r/gis-debug.result
index 0f63509..889ee5c 100644
--- a/mysql-test/r/gis-debug.result
+++ b/mysql-test/r/gis-debug.result
@@ -352,3 +352,56 @@ Note	1105	DBUG: types_compatible=yes bisect=yes
 DROP TABLE t1;
 SET SESSION debug_dbug="-d,Predicant_to_list_comparator";
 SET SESSION debug_dbug="-d,Item_func_in";
+#
+# MDEV-12238 Add Type_handler::Item_func_{plus|minus|mul|div|mod}_fix_length_and_dec()
+#
+SET debug_dbug='+d,num_op';
+CREATE TABLE t1 AS SELECT
+POINT(0,0)+POINT(0,0),
+POINT(0,0)-POINT(0,0),
+POINT(0,0)*POINT(0,0),
+POINT(0,0)/POINT(0,0),
+POINT(0,0) MOD POINT(0,0) LIMIT 0;
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `POINT(0,0)+POINT(0,0)` geometry DEFAULT NULL,
+  `POINT(0,0)-POINT(0,0)` geometry DEFAULT NULL,
+  `POINT(0,0)*POINT(0,0)` geometry DEFAULT NULL,
+  `POINT(0,0)/POINT(0,0)` geometry DEFAULT NULL,
+  `POINT(0,0) MOD POINT(0,0)` geometry DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE TABLE t1 AS SELECT
+POINT(0,0)+'0',
+POINT(0,0)-'0',
+POINT(0,0)*'0',
+POINT(0,0)/'0',
+POINT(0,0) MOD '0' LIMIT 0;
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `POINT(0,0)+'0'` longtext DEFAULT NULL,
+  `POINT(0,0)-'0'` longtext DEFAULT NULL,
+  `POINT(0,0)*'0'` longtext DEFAULT NULL,
+  `POINT(0,0)/'0'` longtext DEFAULT NULL,
+  `POINT(0,0) MOD '0'` longtext DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE TABLE t1 AS SELECT
+'0'+POINT(0,0),
+'0'*POINT(0,0) LIMIT 0;
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `'0'+POINT(0,0)` longtext DEFAULT NULL,
+  `'0'*POINT(0,0)` longtext DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE TABLE t1 AS SELECT '0'-POINT(0,0) LIMIT 0;
+ERROR HY000: Illegal parameter data types varchar and geometry for operation '-'
+CREATE TABLE t1 AS SELECT '0'/POINT(0,0) LIMIT 0;
+ERROR HY000: Illegal parameter data types varchar and geometry for operation '/'
+CREATE TABLE t1 AS SELECT '0' MOD POINT(0,0) LIMIT 0;
+ERROR HY000: Illegal parameter data types varchar and geometry for operation '%'
+SET debug_dbug='-d,num_op';
diff --git a/mysql-test/r/gis.result b/mysql-test/r/gis.result
index 44194b9..b2ed394 100644
--- a/mysql-test/r/gis.result
+++ b/mysql-test/r/gis.result
@@ -3749,5 +3749,70 @@ ERROR HY000: Illegal parameter data type geometry for operation 'ceiling'
 SELECT FLOOR(POINT(1,1));
 ERROR HY000: Illegal parameter data type geometry for operation 'floor'
 #
+# MDEV-12238 Add Type_handler::Item_func_{plus|minus|mul|div|mod}_fix_length_and_dec()
+#
+CREATE TABLE t1 (a GEOMETRY);
+SELECT POINT(1,1) + 1;
+ERROR HY000: Illegal parameter data types geometry and bigint for operation '+'
+SELECT POINT(1,1) - 1;
+ERROR HY000: Illegal parameter data types geometry and bigint for operation '-'
+SELECT POINT(1,1) * 1;
+ERROR HY000: Illegal parameter data types geometry and bigint for operation '*'
+SELECT POINT(1,1) / 1;
+ERROR HY000: Illegal parameter data types geometry and bigint for operation '/'
+SELECT POINT(1,1) MOD 1;
+ERROR HY000: Illegal parameter data types geometry and bigint for operation '%'
+SELECT 1 + POINT(1,1);
+ERROR HY000: Illegal parameter data types bigint and geometry for operation '+'
+SELECT 1 - POINT(1,1);
+ERROR HY000: Illegal parameter data types bigint and geometry for operation '-'
+SELECT 1 * POINT(1,1);
+ERROR HY000: Illegal parameter data types bigint and geometry for operation '*'
+SELECT 1 / POINT(1,1);
+ERROR HY000: Illegal parameter data types bigint and geometry for operation '/'
+SELECT 1 MOD POINT(1,1);
+ERROR HY000: Illegal parameter data types bigint and geometry for operation '%'
+SELECT a + 1 FROM t1;
+ERROR HY000: Illegal parameter data types geometry and bigint for operation '+'
+SELECT a - 1 FROM t1;
+ERROR HY000: Illegal parameter data types geometry and bigint for operation '-'
+SELECT a * 1 FROM t1;
+ERROR HY000: Illegal parameter data types geometry and bigint for operation '*'
+SELECT a / 1 FROM t1;
+ERROR HY000: Illegal parameter data types geometry and bigint for operation '/'
+SELECT a MOD 1 FROM t1;
+ERROR HY000: Illegal parameter data types geometry and bigint for operation '%'
+SELECT 1 + a FROM t1;
+ERROR HY000: Illegal parameter data types bigint and geometry for operation '+'
+SELECT 1 - a FROM t1;
+ERROR HY000: Illegal parameter data types bigint and geometry for operation '-'
+SELECT 1 * a FROM t1;
+ERROR HY000: Illegal parameter data types bigint and geometry for operation '*'
+SELECT 1 / a FROM t1;
+ERROR HY000: Illegal parameter data types bigint and geometry for operation '/'
+SELECT 1 MOD a FROM t1;
+ERROR HY000: Illegal parameter data types bigint and geometry for operation '%'
+SELECT COALESCE(a) + 1 FROM t1;
+ERROR HY000: Illegal parameter data types geometry and bigint for operation '+'
+SELECT COALESCE(a) - 1 FROM t1;
+ERROR HY000: Illegal parameter data types geometry and bigint for operation '-'
+SELECT COALESCE(a) * 1 FROM t1;
+ERROR HY000: Illegal parameter data types geometry and bigint for operation '*'
+SELECT COALESCE(a) / 1 FROM t1;
+ERROR HY000: Illegal parameter data types geometry and bigint for operation '/'
+SELECT COALESCE(a) MOD 1 FROM t1;
+ERROR HY000: Illegal parameter data types geometry and bigint for operation '%'
+SELECT 1 + COALESCE(a) FROM t1;
+ERROR HY000: Illegal parameter data types bigint and geometry for operation '+'
+SELECT 1 - COALESCE(a) FROM t1;
+ERROR HY000: Illegal parameter data types bigint and geometry for operation '-'
+SELECT 1 * COALESCE(a) FROM t1;
+ERROR HY000: Illegal parameter data types bigint and geometry for operation '*'
+SELECT 1 / COALESCE(a) FROM t1;
+ERROR HY000: Illegal parameter data types bigint and geometry for operation '/'
+SELECT 1 MOD COALESCE(a) FROM t1;
+ERROR HY000: Illegal parameter data types bigint and geometry for operation '%'
+DROP TABLE t1;
+#
 # End of 10.3 tests
 #
diff --git a/mysql-test/t/gis-debug.test b/mysql-test/t/gis-debug.test
index a34dd62..588bc70 100644
--- a/mysql-test/t/gis-debug.test
+++ b/mysql-test/t/gis-debug.test
@@ -46,3 +46,68 @@ DROP TABLE t1;
 
 SET SESSION debug_dbug="-d,Predicant_to_list_comparator";
 SET SESSION debug_dbug="-d,Item_func_in";
+
+
+--echo #
+--echo # MDEV-12238 Add Type_handler::Item_func_{plus|minus|mul|div|mod}_fix_length_and_dec()
+--echo #
+
+# This tests is to check that operators '+' and '*' are commutative,
+# while operators '/', '-' and 'MOD' are not commutative.
+#
+# It forces substitution of type_aggregator_for_{plus|minus|mul|div|mod} to
+# type_aggregator_for_result / type_aggregator_non_commutative_test,
+# which have pairs:
+#  (GEOMETRY,GEOMETRY)->GEOMETRY
+#  (GEOMETRY,VARCHAR)->GEOMETRY
+# Note, they don't not have a pair:
+#  (VARCHAR,GEOMETRY)->GEOMETRY
+#
+# Commutative operators should work for all these argument type combinations:
+# (GEOMETRY,GEOMETRY), (GEOMETRY,VARCHAR), (VARCHAR,GEOMETRY).
+# Non-commutative operators should work for:
+# (GEOMETRY,GEOMETRY), (GEOMETRY,VARCHAR),
+# but should fail for (VARCHAR,GEOMETRY).
+#
+# Note, LIMIT 0 is needed to avoid calling str_op(), which does DBUG_ASSERT(0).
+
+SET debug_dbug='+d,num_op';
+
+# (GEOMETRY,GEOMETRY) gives GEOMETRY for all operators
+CREATE TABLE t1 AS SELECT
+  POINT(0,0)+POINT(0,0),
+  POINT(0,0)-POINT(0,0),
+  POINT(0,0)*POINT(0,0),
+  POINT(0,0)/POINT(0,0),
+  POINT(0,0) MOD POINT(0,0) LIMIT 0;
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+# (GEOMETRY,VARCHAR) gives GEOMETRY for all operators
+CREATE TABLE t1 AS SELECT
+  POINT(0,0)+'0',
+  POINT(0,0)-'0',
+  POINT(0,0)*'0',
+  POINT(0,0)/'0',
+  POINT(0,0) MOD '0' LIMIT 0;
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+# (VARCHAR,GEOMETRY) gives GEOMETRY for commutative operators
+CREATE TABLE t1 AS SELECT
+  '0'+POINT(0,0),
+  '0'*POINT(0,0) LIMIT 0;
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+# (VARCHAR,GEOMETRY) gives an error for non-commutative operators
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+CREATE TABLE t1 AS SELECT '0'-POINT(0,0) LIMIT 0;
+
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+CREATE TABLE t1 AS SELECT '0'/POINT(0,0) LIMIT 0;
+
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+CREATE TABLE t1 AS SELECT '0' MOD POINT(0,0) LIMIT 0;
+
+SET debug_dbug='-d,num_op';
diff --git a/mysql-test/t/gis.test b/mysql-test/t/gis.test
index d651dde..fc1e3e1 100644
--- a/mysql-test/t/gis.test
+++ b/mysql-test/t/gis.test
@@ -1916,5 +1916,80 @@ SELECT CEILING(POINT(1,1));
 SELECT FLOOR(POINT(1,1));
 
 --echo #
+--echo # MDEV-12238 Add Type_handler::Item_func_{plus|minus|mul|div|mod}_fix_length_and_dec()
+--echo #
+
+CREATE TABLE t1 (a GEOMETRY);
+
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT POINT(1,1) + 1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT POINT(1,1) - 1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT POINT(1,1) * 1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT POINT(1,1) / 1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT POINT(1,1) MOD 1;
+
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT 1 + POINT(1,1);
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT 1 - POINT(1,1);
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT 1 * POINT(1,1);
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT 1 / POINT(1,1);
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT 1 MOD POINT(1,1);
+
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT a + 1 FROM t1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT a - 1 FROM t1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT a * 1 FROM t1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT a / 1 FROM t1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT a MOD 1 FROM t1;
+
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT 1 + a FROM t1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT 1 - a FROM t1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT 1 * a FROM t1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT 1 / a FROM t1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT 1 MOD a FROM t1;
+
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT COALESCE(a) + 1 FROM t1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT COALESCE(a) - 1 FROM t1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT COALESCE(a) * 1 FROM t1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT COALESCE(a) / 1 FROM t1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT COALESCE(a) MOD 1 FROM t1;
+
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT 1 + COALESCE(a) FROM t1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT 1 - COALESCE(a) FROM t1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT 1 * COALESCE(a) FROM t1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT 1 / COALESCE(a) FROM t1;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT 1 MOD COALESCE(a) FROM t1;
+
+DROP TABLE t1;
+
+
+--echo #
 --echo # End of 10.3 tests
 --echo #
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 3a9f61e..8d7c446 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -713,47 +713,31 @@ bool Item_func_connection_id::fix_fields(THD *thd, Item **ref)
 }
 
 
-/**
-  Check arguments here to determine result's type for a numeric
-  function of two arguments.
-*/
-
-void Item_num_op::fix_length_and_dec(void)
+bool Item_num_op::fix_type_handler(const Type_aggregator *aggregator)
 {
-  DBUG_ENTER("Item_num_op::fix_length_and_dec");
-  DBUG_PRINT("info", ("name %s", func_name()));
   DBUG_ASSERT(arg_count == 2);
-  Item_result r0= args[0]->cast_to_int_type_handler()->cmp_type();
-  Item_result r1= args[1]->cast_to_int_type_handler()->cmp_type();
+  const Type_handler *h0= args[0]->cast_to_int_type_handler();
+  const Type_handler *h1= args[1]->cast_to_int_type_handler();
+  if (!aggregate_for_num_op(aggregator, h0, h1))
+    return false;
+  my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
+           h0->name().ptr(), h1->name().ptr(), func_name());
+  return true;
+}
 
-  if (r0 == REAL_RESULT || r1 == REAL_RESULT ||
-      r0 == STRING_RESULT || r1 ==STRING_RESULT)
-  {
-    count_real_length(args, arg_count);
-    max_length= float_length(decimals);
-    set_handler_by_result_type(REAL_RESULT);
-  }
-  else if (r0 == DECIMAL_RESULT || r1 == DECIMAL_RESULT ||
-           r0 == TIME_RESULT || r1 == TIME_RESULT)
-  {
-    set_handler_by_result_type(DECIMAL_RESULT);
-    result_precision();
-    fix_decimals();
-    if ((r0 == TIME_RESULT || r1 == TIME_RESULT) && decimals == 0)
-      set_handler_by_result_type(INT_RESULT);
-  }
-  else
+
+void Item_func_plus::fix_length_and_dec(void)
+{
+  DBUG_ENTER("Item_func_plus::fix_length_and_dec");
+  DBUG_PRINT("info", ("name %s", func_name()));
+  const Type_aggregator *aggregator= &type_aggregator_for_plus;
+  DBUG_EXECUTE_IF("num_op", aggregator= &type_aggregator_for_result;);
+  DBUG_ASSERT(aggregator->is_commutative());
+  if (!fix_type_handler(aggregator))
   {
-    DBUG_ASSERT(r0 == INT_RESULT && r1 == INT_RESULT);
-    set_handler_by_result_type(INT_RESULT);
-    result_precision();
-    decimals= 0;
+    Item_func_plus::type_handler()->Item_func_plus_fix_length_and_dec(this);
+    DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
   }
-  DBUG_PRINT("info", ("Type: %s",
-             (result_type() == REAL_RESULT ? "REAL_RESULT" :
-              result_type() == DECIMAL_RESULT ? "DECIMAL_RESULT" :
-              result_type() == INT_RESULT ? "INT_RESULT" :
-              "--ILLEGAL!!!--")));
   DBUG_VOID_RETURN;
 }
 
@@ -1283,11 +1267,6 @@ void Item_func_additive_op::result_precision()
   DBUG_ASSERT(arg1_int >= 0);
   DBUG_ASSERT(arg2_int >= 0);
 
-  /* Integer operations keep unsigned_flag if one of arguments is unsigned */
-  if (result_type() == INT_RESULT)
-    unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag;
-  else
-    unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag;
   max_length= my_decimal_precision_to_length_no_truncation(precision, decimals,
                                                            unsigned_flag);
 }
@@ -1297,16 +1276,30 @@ void Item_func_additive_op::result_precision()
   The following function is here to allow the user to force
   subtraction of UNSIGNED BIGINT to return negative values.
 */
-
-void Item_func_minus::fix_length_and_dec()
+void Item_func_minus::fix_unsigned_flag()
 {
-  Item_num_op::fix_length_and_dec();
   if (unsigned_flag &&
       (current_thd->variables.sql_mode & MODE_NO_UNSIGNED_SUBTRACTION))
     unsigned_flag=0;
 }
 
 
+void Item_func_minus::fix_length_and_dec()
+{
+  DBUG_ENTER("Item_func_minus::fix_length_and_dec");
+  DBUG_PRINT("info", ("name %s", func_name()));
+  const Type_aggregator *aggregator= &type_aggregator_for_minus;
+  DBUG_EXECUTE_IF("num_op", aggregator= &type_aggregator_non_commutative_test;);
+  DBUG_ASSERT(!aggregator->is_commutative());
+  if (!fix_type_handler(aggregator))
+  {
+    Item_func_minus::type_handler()->Item_func_minus_fix_length_and_dec(this);
+    DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
+  }
+  DBUG_VOID_RETURN;
+}
+
+
 double Item_func_minus::real_op()
 {
   double value= args[0]->val_real() - args[1]->val_real();
@@ -1514,13 +1507,8 @@ my_decimal *Item_func_mul::decimal_op(my_decimal *decimal_value)
 
 void Item_func_mul::result_precision()
 {
-  /* Integer operations keep unsigned_flag if one of arguments is unsigned */
-  if (result_type() == INT_RESULT)
-    unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag;
-  else
-    unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag;
   decimals= MY_MIN(args[0]->decimal_scale() + args[1]->decimal_scale(),
-                DECIMAL_MAX_SCALE);
+                   DECIMAL_MAX_SCALE);
   uint est_prec = args[0]->decimal_precision() + args[1]->decimal_precision();
   uint precision= MY_MIN(est_prec, DECIMAL_MAX_PRECISION);
   max_length= my_decimal_precision_to_length_no_truncation(precision, decimals,
@@ -1528,6 +1516,22 @@ void Item_func_mul::result_precision()
 }
 
 
+void Item_func_mul::fix_length_and_dec(void)
+{
+  DBUG_ENTER("Item_func_mul::fix_length_and_dec");
+  DBUG_PRINT("info", ("name %s", func_name()));
+  const Type_aggregator *aggregator= &type_aggregator_for_mul;
+  DBUG_EXECUTE_IF("num_op", aggregator= &type_aggregator_for_result;);
+  DBUG_ASSERT(aggregator->is_commutative());
+  if (!fix_type_handler(aggregator))
+  {
+    Item_func_mul::type_handler()->Item_func_mul_fix_length_and_dec(this);
+    DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
+  }
+  DBUG_VOID_RETURN;
+}
+
+
 double Item_func_div::real_op()
 {
   DBUG_ASSERT(fixed == 1);
@@ -1589,53 +1593,51 @@ void Item_func_div::result_precision()
   uint precision=MY_MIN(args[0]->decimal_precision() + 
                      args[1]->divisor_precision_increment() + prec_increment,
                      DECIMAL_MAX_PRECISION);
-
-  /* Integer operations keep unsigned_flag if one of arguments is unsigned */
-  if (result_type() == INT_RESULT)
-    unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag;
-  else
-    unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag;
   decimals= MY_MIN(args[0]->decimal_scale() + prec_increment, DECIMAL_MAX_SCALE);
   max_length= my_decimal_precision_to_length_no_truncation(precision, decimals,
                                                            unsigned_flag);
 }
 
 
-void Item_func_div::fix_length_and_dec()
+void Item_func_div::fix_length_and_dec_double(void)
+{
+  Item_num_op::fix_length_and_dec_double();
+  decimals= MY_MAX(args[0]->decimals, args[1]->decimals) + prec_increment;
+  set_if_smaller(decimals, NOT_FIXED_DEC);
+  uint tmp= float_length(decimals);
+  if (decimals == NOT_FIXED_DEC)
+    max_length= tmp;
+  else
+  {
+    max_length=args[0]->max_length - args[0]->decimals + decimals;
+    set_if_smaller(max_length, tmp);
+  }
+}
+
+
+void Item_func_div::fix_length_and_dec_int(void)
+{
+  set_handler(&type_handler_newdecimal);
+  DBUG_PRINT("info", ("Type changed: %s", type_handler()->name().ptr()));
+  Item_num_op::fix_length_and_dec_decimal();
+}
+
+
+void Item_func_div::fix_length_and_dec(void)
 {
   DBUG_ENTER("Item_func_div::fix_length_and_dec");
+  DBUG_PRINT("info", ("name %s", func_name()));
   prec_increment= current_thd->variables.div_precincrement;
-  Item_num_op::fix_length_and_dec();
-  switch (Item_func_div::result_type()) {
-  case REAL_RESULT:
+  maybe_null= 1; // devision by zero
+
+  const Type_aggregator *aggregator= &type_aggregator_for_div;
+  DBUG_EXECUTE_IF("num_op", aggregator= &type_aggregator_non_commutative_test;);
+  DBUG_ASSERT(!aggregator->is_commutative());
+  if (!fix_type_handler(aggregator))
   {
-    decimals=MY_MAX(args[0]->decimals,args[1]->decimals)+prec_increment;
-    set_if_smaller(decimals, NOT_FIXED_DEC);
-    uint tmp=float_length(decimals);
-    if (decimals == NOT_FIXED_DEC)
-      max_length= tmp;
-    else
-    {
-      max_length=args[0]->max_length - args[0]->decimals + decimals;
-      set_if_smaller(max_length,tmp);
-    }
-    break;
-  }
-  case INT_RESULT:
-    set_handler_by_result_type(DECIMAL_RESULT);
-    DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT"));
-    result_precision();
-    break;
-  case DECIMAL_RESULT:
-    result_precision();
-    fix_decimals();
-    break;
-  case STRING_RESULT:
-  case ROW_RESULT:
-  case TIME_RESULT:
-    DBUG_ASSERT(0);
+    Item_func_div::type_handler()->Item_func_div_fix_length_and_dec(this);
+    DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
   }
-  maybe_null= 1; // devision by zero
   DBUG_VOID_RETURN;
 }
 
@@ -1807,9 +1809,18 @@ void Item_func_mod::result_precision()
 
 void Item_func_mod::fix_length_and_dec()
 {
-  Item_num_op::fix_length_and_dec();
-  maybe_null= 1;
-  unsigned_flag= args[0]->unsigned_flag;
+  DBUG_ENTER("Item_func_mod::fix_length_and_dec");
+  DBUG_PRINT("info", ("name %s", func_name()));
+  maybe_null= true; // division by zero
+  const Type_aggregator *aggregator= &type_aggregator_for_mod;
+  DBUG_EXECUTE_IF("num_op", aggregator= &type_aggregator_non_commutative_test;);
+  DBUG_ASSERT(!aggregator->is_commutative());
+  if (!fix_type_handler(aggregator))
+  {
+    Item_func_mod::type_handler()->Item_func_mod_fix_length_and_dec(this);
+    DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
+  }
+  DBUG_VOID_RETURN;
 }
 
 
diff --git a/sql/item_func.h b/sql/item_func.h
index 8b53631..b951a41 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -694,7 +694,31 @@ class Item_num_op :public Item_func_numhybrid
   {
     print_op(str, query_type);
   }
-  void fix_length_and_dec();
+  bool fix_type_handler(const Type_aggregator *aggregator);
+  void fix_length_and_dec_double()
+  {
+    count_real_length(args, arg_count);
+    max_length= float_length(decimals);
+  }
+  void fix_length_and_dec_decimal()
+  {
+    unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag;
+    result_precision();
+    fix_decimals();
+  }
+  void fix_length_and_dec_int()
+  {
+    unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag;
+    result_precision();
+    decimals= 0;
+  }
+  void fix_length_and_dec_temporal()
+  {
+    set_handler(&type_handler_newdecimal);
+    fix_length_and_dec_decimal();
+    if (decimals == 0)
+      set_handler(&type_handler_longlong);
+  }
   bool need_parentheses_in_default() { return true; }
 };
 
@@ -862,6 +886,7 @@ class Item_func_plus :public Item_func_additive_op
     Item_func_additive_op(thd, a, b) {}
   const char *func_name() const { return "+"; }
   enum precedence precedence() const { return ADD_PRECEDENCE; }
+  void fix_length_and_dec();
   longlong int_op();
   double real_op();
   my_decimal *decimal_op(my_decimal *);
@@ -880,6 +905,22 @@ class Item_func_minus :public Item_func_additive_op
   double real_op();
   my_decimal *decimal_op(my_decimal *);
   void fix_length_and_dec();
+  void fix_unsigned_flag();
+  void fix_length_and_dec_double()
+  {
+    Item_func_additive_op::fix_length_and_dec_double();
+    fix_unsigned_flag();
+  }
+  void fix_length_and_dec_decimal()
+  {
+    Item_func_additive_op::fix_length_and_dec_decimal();
+    fix_unsigned_flag();
+  }
+  void fix_length_and_dec_int()
+  {
+    Item_func_additive_op::fix_length_and_dec_int();
+    fix_unsigned_flag();
+  }
   Item *get_copy(THD *thd, MEM_ROOT *mem_root)
   { return get_item_copy<Item_func_minus>(thd, mem_root, this); }
 };
@@ -896,6 +937,7 @@ class Item_func_mul :public Item_num_op
   double real_op();
   my_decimal *decimal_op(my_decimal *);
   void result_precision();
+  void fix_length_and_dec();
   bool check_partition_func_processor(void *int_arg) {return FALSE;}
   bool check_vcol_func_processor(void *arg) { return FALSE;}
   Item *get_copy(THD *thd, MEM_ROOT *mem_root)
@@ -914,6 +956,8 @@ class Item_func_div :public Item_num_op
   const char *func_name() const { return "/"; }
   enum precedence precedence() const { return MUL_PRECEDENCE; }
   void fix_length_and_dec();
+  void fix_length_and_dec_double();
+  void fix_length_and_dec_int();
   void result_precision();
   Item *get_copy(THD *thd, MEM_ROOT *mem_root)
   { return get_item_copy<Item_func_div>(thd, mem_root, this); }
@@ -953,6 +997,22 @@ class Item_func_mod :public Item_num_op
   enum precedence precedence() const { return MUL_PRECEDENCE; }
   void result_precision();
   void fix_length_and_dec();
+  void fix_length_and_dec_double()
+  {
+    Item_num_op::fix_length_and_dec_double();
+    unsigned_flag= args[0]->unsigned_flag;
+  }
+  void fix_length_and_dec_decimal()
+  {
+    Item_num_op::fix_length_and_dec_decimal();
+    unsigned_flag= args[0]->unsigned_flag;
+  }
+  void fix_length_and_dec_int()
+  {
+    max_length= MY_MAX(args[0]->max_length, args[1]->max_length);
+    decimals= 0;
+    unsigned_flag= args[0]->unsigned_flag;
+  }
   bool check_partition_func_processor(void *int_arg) {return FALSE;}
   bool check_vcol_func_processor(void *arg) { return FALSE;}
   Item *get_copy(THD *thd, MEM_ROOT *mem_root)
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index 4d4694d..ad5af75 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -57,8 +57,18 @@ Type_handler_geometry    type_handler_geometry;
 #endif
 
 
-Type_aggregator type_aggregator_for_result;
-Type_aggregator type_aggregator_for_comparison;
+Type_aggregator_commutative type_aggregator_for_result;
+Type_aggregator_commutative type_aggregator_for_comparison;
+Type_aggregator_commutative type_aggregator_for_plus;
+Type_aggregator_commutative type_aggregator_for_mul;
+
+Type_aggregator type_aggregator_for_minus;
+Type_aggregator type_aggregator_for_div;
+Type_aggregator type_aggregator_for_mod;
+
+#ifndef DBUG_OFF
+Type_aggregator type_aggregator_non_commutative_test;
+#endif
 
 
 class Static_data_initializer
@@ -93,6 +103,15 @@ class Static_data_initializer
     type_aggregator_for_comparison.add(&type_handler_geometry,
                                        &type_handler_long_blob,
                                        &type_handler_long_blob);
+#ifndef DBUG_OFF
+    type_aggregator_non_commutative_test.add(&type_handler_geometry,
+                                             &type_handler_geometry,
+                                             &type_handler_geometry);
+    type_aggregator_non_commutative_test.add(&type_handler_geometry,
+                                             &type_handler_varchar,
+                                             &type_handler_long_blob);
+#endif
+
 #endif
   }
 };
@@ -455,6 +474,63 @@ Type_handler_hybrid_field_type::aggregate_for_comparison(const Type_handler *h)
 }
 
 
+const Type_handler *
+Type_handler::aggregate_for_num_op_traditional(const Type_handler *h0,
+                                               const Type_handler *h1)
+{
+  Item_result r0= h0->cmp_type();
+  Item_result r1= h1->cmp_type();
+
+  if (r0 == REAL_RESULT || r1 == REAL_RESULT ||
+      r0 == STRING_RESULT || r1 ==STRING_RESULT)
+    return &type_handler_double;
+
+  if (r0 == TIME_RESULT || r1 == TIME_RESULT)
+    return &type_handler_datetime;
+
+  if (r0 == DECIMAL_RESULT || r1 == DECIMAL_RESULT)
+    return &type_handler_newdecimal;
+
+  DBUG_ASSERT(r0 == INT_RESULT && r1 == INT_RESULT);
+  return &type_handler_longlong;
+}
+
+
+const Type_aggregator::Pair*
+Type_aggregator::find_pair(const Type_handler *handler1,
+                           const Type_handler *handler2) const
+{
+  for (uint i= 0; i < m_array.elements(); i++)
+  {
+    const Pair& el= m_array.at(i);
+    if (el.eq(handler1, handler2) ||
+        (m_is_commutative && el.eq(handler2, handler1)))
+      return &el;
+  }
+  return NULL;
+}
+
+
+bool
+Type_handler_hybrid_field_type::aggregate_for_num_op(const Type_aggregator *agg,
+                                                     const Type_handler *h0,
+                                                     const Type_handler *h1)
+{
+  const Type_handler *hres;
+  if (h0->is_traditional_type() && h1->is_traditional_type())
+  {
+    set_handler(Type_handler::aggregate_for_num_op_traditional(h0, h1));
+    return false;
+  }
+  if ((hres= agg->find_handler(h0, h1)))
+  {
+    set_handler(hres);
+    return false;
+  }
+  return true;
+}
+
+
 /***************************************************************************/
 
 const Type_handler *
@@ -2396,3 +2472,248 @@ bool Type_handler_geometry::
 #endif
 
 /***************************************************************************/
+
+bool Type_handler_row::
+       Item_func_plus_fix_length_and_dec(Item_func_plus *item) const
+{
+  DBUG_ASSERT(0);
+  return true;
+}
+
+
+bool Type_handler_int_result::
+       Item_func_plus_fix_length_and_dec(Item_func_plus *item) const
+{
+  item->fix_length_and_dec_int();
+  return false;
+}
+
+
+bool Type_handler_real_result::
+       Item_func_plus_fix_length_and_dec(Item_func_plus *item) const
+{
+  item->fix_length_and_dec_double();
+  return false;
+}
+
+
+bool Type_handler_decimal_result::
+       Item_func_plus_fix_length_and_dec(Item_func_plus *item) const
+{
+  item->fix_length_and_dec_decimal();
+  return false;
+}
+
+
+bool Type_handler_temporal_result::
+       Item_func_plus_fix_length_and_dec(Item_func_plus *item) const
+{
+  item->fix_length_and_dec_temporal();
+  return false;
+}
+
+
+bool Type_handler_string_result::
+       Item_func_plus_fix_length_and_dec(Item_func_plus *item) const
+{
+  item->fix_length_and_dec_double();
+  return false;
+}
+
+/***************************************************************************/
+
+bool Type_handler_row::
+       Item_func_minus_fix_length_and_dec(Item_func_minus *item) const
+{
+  DBUG_ASSERT(0);
+  return true;
+}
+
+
+bool Type_handler_int_result::
+       Item_func_minus_fix_length_and_dec(Item_func_minus *item) const
+{
+  item->fix_length_and_dec_int();
+  return false;
+}
+
+
+bool Type_handler_real_result::
+       Item_func_minus_fix_length_and_dec(Item_func_minus *item) const
+{
+  item->fix_length_and_dec_double();
+  return false;
+}
+
+
+bool Type_handler_decimal_result::
+       Item_func_minus_fix_length_and_dec(Item_func_minus *item) const
+{
+  item->fix_length_and_dec_decimal();
+  return false;
+}
+
+
+bool Type_handler_temporal_result::
+       Item_func_minus_fix_length_and_dec(Item_func_minus *item) const
+{
+  item->fix_length_and_dec_temporal();
+  return false;
+}
+
+
+bool Type_handler_string_result::
+       Item_func_minus_fix_length_and_dec(Item_func_minus *item) const
+{
+  item->fix_length_and_dec_double();
+  return false;
+}
+
+/***************************************************************************/
+
+bool Type_handler_row::
+       Item_func_mul_fix_length_and_dec(Item_func_mul *item) const
+{
+  DBUG_ASSERT(0);
+  return true;
+}
+
+
+bool Type_handler_int_result::
+       Item_func_mul_fix_length_and_dec(Item_func_mul *item) const
+{
+  item->fix_length_and_dec_int();
+  return false;
+}
+
+
+bool Type_handler_real_result::
+       Item_func_mul_fix_length_and_dec(Item_func_mul *item) const
+{
+  item->fix_length_and_dec_double();
+  return false;
+}
+
+
+bool Type_handler_decimal_result::
+       Item_func_mul_fix_length_and_dec(Item_func_mul *item) const
+{
+  item->fix_length_and_dec_decimal();
+  return false;
+}
+
+
+bool Type_handler_temporal_result::
+       Item_func_mul_fix_length_and_dec(Item_func_mul *item) const
+{
+  item->fix_length_and_dec_temporal();
+  return false;
+}
+
+
+bool Type_handler_string_result::
+       Item_func_mul_fix_length_and_dec(Item_func_mul *item) const
+{
+  item->fix_length_and_dec_double();
+  return false;
+}
+
+/***************************************************************************/
+
+bool Type_handler_row::
+       Item_func_div_fix_length_and_dec(Item_func_div *item) const
+{
+  DBUG_ASSERT(0);
+  return true;
+}
+
+
+bool Type_handler_int_result::
+       Item_func_div_fix_length_and_dec(Item_func_div *item) const
+{
+  item->fix_length_and_dec_int();
+  return false;
+}
+
+
+bool Type_handler_real_result::
+       Item_func_div_fix_length_and_dec(Item_func_div *item) const
+{
+  item->fix_length_and_dec_double();
+  return false;
+}
+
+
+bool Type_handler_decimal_result::
+       Item_func_div_fix_length_and_dec(Item_func_div *item) const
+{
+  item->fix_length_and_dec_decimal();
+  return false;
+}
+
+
+bool Type_handler_temporal_result::
+       Item_func_div_fix_length_and_dec(Item_func_div *item) const
+{
+  item->fix_length_and_dec_temporal();
+  return false;
+}
+
+
+bool Type_handler_string_result::
+       Item_func_div_fix_length_and_dec(Item_func_div *item) const
+{
+  item->fix_length_and_dec_double();
+  return false;
+}
+
+/***************************************************************************/
+
+bool Type_handler_row::
+       Item_func_mod_fix_length_and_dec(Item_func_mod *item) const
+{
+  DBUG_ASSERT(0);
+  return true;
+}
+
+
+bool Type_handler_int_result::
+       Item_func_mod_fix_length_and_dec(Item_func_mod *item) const
+{
+  item->fix_length_and_dec_int();
+  return false;
+}
+
+
+bool Type_handler_real_result::
+       Item_func_mod_fix_length_and_dec(Item_func_mod *item) const
+{
+  item->fix_length_and_dec_double();
+  return false;
+}
+
+
+bool Type_handler_decimal_result::
+       Item_func_mod_fix_length_and_dec(Item_func_mod *item) const
+{
+  item->fix_length_and_dec_decimal();
+  return false;
+}
+
+
+bool Type_handler_temporal_result::
+       Item_func_mod_fix_length_and_dec(Item_func_mod *item) const
+{
+  item->fix_length_and_dec_temporal();
+  return false;
+}
+
+
+bool Type_handler_string_result::
+       Item_func_mod_fix_length_and_dec(Item_func_mod *item) const
+{
+  item->fix_length_and_dec_double();
+  return false;
+}
+
+/***************************************************************************/
diff --git a/sql/sql_type.h b/sql/sql_type.h
index bc8b170..c6ba563 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -39,6 +39,11 @@ class Item_func_round;
 class Item_func_int_val;
 class Item_func_abs;
 class Item_func_neg;
+class Item_func_plus;
+class Item_func_minus;
+class Item_func_mul;
+class Item_func_div;
+class Item_func_mod;
 class cmp_item;
 class in_vector;
 class Type_std_attributes;
@@ -287,6 +292,9 @@ class Type_handler
   static const
   Type_handler *aggregate_for_result_traditional(const Type_handler *h1,
                                                  const Type_handler *h2);
+  static const
+  Type_handler *aggregate_for_num_op_traditional(const Type_handler *h1,
+                                                 const Type_handler *h2);
 
   virtual const Name name() const= 0;
   virtual enum_field_types field_type() const= 0;
@@ -459,6 +467,21 @@ class Type_handler
 
   virtual bool
   Item_func_neg_fix_length_and_dec(Item_func_neg *func) const= 0;
+
+  virtual bool
+  Item_func_plus_fix_length_and_dec(Item_func_plus *func) const= 0;
+
+  virtual bool
+  Item_func_minus_fix_length_and_dec(Item_func_minus *func) const= 0;
+
+  virtual bool
+  Item_func_mul_fix_length_and_dec(Item_func_mul *func) const= 0;
+
+  virtual bool
+  Item_func_div_fix_length_and_dec(Item_func_div *func) const= 0;
+
+  virtual bool
+  Item_func_mod_fix_length_and_dec(Item_func_mod *func) const= 0;
 };
 
 
@@ -614,6 +637,11 @@ class Type_handler_row: public Type_handler
   bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
   bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const;
   bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const;
+  bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const;
+  bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const;
+  bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const;
+  bool Item_func_div_fix_length_and_dec(Item_func_div *) const;
+  bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const;
 };
 
 
@@ -686,6 +714,11 @@ class Type_handler_real_result: public Type_handler_numeric
   bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
   bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const;
   bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const;
+  bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const;
+  bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const;
+  bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const;
+  bool Item_func_div_fix_length_and_dec(Item_func_div *) const;
+  bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const;
 };
 
 
@@ -732,6 +765,11 @@ class Type_handler_decimal_result: public Type_handler_numeric
   bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
   bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const;
   bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const;
+  bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const;
+  bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const;
+  bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const;
+  bool Item_func_div_fix_length_and_dec(Item_func_div *) const;
+  bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const;
 };
 
 
@@ -777,6 +815,11 @@ class Type_handler_int_result: public Type_handler_numeric
   bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
   bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const;
   bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const;
+  bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const;
+  bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const;
+  bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const;
+  bool Item_func_div_fix_length_and_dec(Item_func_div *) const;
+  bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const;
 };
 
 
@@ -826,6 +869,11 @@ class Type_handler_temporal_result: public Type_handler
   bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
   bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const;
   bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const;
+  bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const;
+  bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const;
+  bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const;
+  bool Item_func_div_fix_length_and_dec(Item_func_div *) const;
+  bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const;
 };
 
 
@@ -889,6 +937,11 @@ class Type_handler_string_result: public Type_handler
   bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
   bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const;
   bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const;
+  bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const;
+  bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const;
+  bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const;
+  bool Item_func_div_fix_length_and_dec(Item_func_div *) const;
+  bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const;
 };
 
 
@@ -1406,6 +1459,8 @@ class Type_handler_hybrid_field_type
   bool aggregate_for_result(const Type_handler *other);
   bool aggregate_for_result(const char *funcname,
                             Item **item, uint nitems, bool treat_bit_as_number);
+  bool aggregate_for_num_op(const class Type_aggregator *aggregator,
+                            const Type_handler *h0, const Type_handler *h1);
 };
 
 
@@ -1435,6 +1490,7 @@ extern Type_handler_bit type_handler_bit;
 
 class Type_aggregator
 {
+  bool m_is_commutative;
   class Pair
   {
   public:
@@ -1454,18 +1510,10 @@ class Type_aggregator
   };
   Dynamic_array<Pair> m_array;
   const Pair* find_pair(const Type_handler *handler1,
-                        const Type_handler *handler2) const
-  {
-    for (uint i= 0; i < m_array.elements(); i++)
-    {
-      const Pair& el= m_array.at(i);
-      if (el.eq(handler1, handler2) || el.eq(handler2, handler1))
-        return &el;
-    }
-    return NULL;
-  }
+                        const Type_handler *handler2) const;
 public:
-  Type_aggregator()
+  Type_aggregator(bool is_commutative= false)
+   :m_is_commutative(is_commutative)
   { }
   bool add(const Type_handler *handler1,
            const Type_handler *handler2,
@@ -1479,9 +1527,31 @@ class Type_aggregator
     const Pair* el= find_pair(handler1, handler2);
     return el ? el->m_result : NULL;
   }
+  bool is_commutative() const { return m_is_commutative; }
+};
+
+
+class Type_aggregator_commutative: public Type_aggregator
+{
+public:
+  Type_aggregator_commutative()
+   :Type_aggregator(true)
+  { }
 };
 
-extern Type_aggregator type_aggregator_for_result;
-extern Type_aggregator type_aggregator_for_comparison;
+
+extern Type_aggregator_commutative type_aggregator_for_result;
+extern Type_aggregator_commutative type_aggregator_for_comparison;
+
+extern Type_aggregator_commutative type_aggregator_for_plus;
+extern Type_aggregator_commutative type_aggregator_for_mul;
+
+extern Type_aggregator type_aggregator_for_minus;
+extern Type_aggregator type_aggregator_for_div;
+extern Type_aggregator type_aggregator_for_mod;
+
+#ifndef DBUG_OFF
+extern Type_aggregator type_aggregator_non_commutative_test;
+#endif
 
 #endif /* SQL_TYPE_H_INCLUDED */
