Re: [Maria-developers] Please review MDEV-9653 Assertion `length || !scale' failed in uint my_decimal_length_to_precision(uint, uint, bool)

2016-03-20 Thread Sergei Golubchik
Hi, Alexander!

On Mar 16, Alexander Barkov wrote:
> Hello Sergei,
> 
> Please review a patch for:
> 
> MDEV-9653 Assertion `length || !scale' failed in uint
> my_decimal_length_to_precision(uint, uint, bool)
> 
> Thanks!

> diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
> index 2783a05..73a47ac 100644
> --- a/sql/item_cmpfunc.cc
> +++ b/sql/item_cmpfunc.cc
> @@ -3086,10 +3086,28 @@ void Item_func_case::agg_str_lengths(Item* arg)
>  
>  void Item_func_case::agg_num_lengths(Item *arg)
>  {
> -  uint len= my_decimal_length_to_precision(arg->max_length, arg->decimals,
> -   arg->unsigned_flag) - 
> arg->decimals;
> -  set_if_bigger(max_length, len); 
> -  set_if_bigger(decimals, arg->decimals);
> +  /*
> +In a query like this:
> +SELECT CASE WHEN TRUE
> + THEN COALESCE(CAST(NULL AS UNSIGNED))
> + ELSE 40
> +   END;
> +"arg" corresponding to Item_func_coalesce has:
> +  arg->max_length==0
> +  arg->decimals==31 (NOT_FIXED_DEC).

why is it NOT_FIXED_DEC? it seems to me that it has to be 0 here.

Regards,
Sergei
Chief Architect MariaDB
and secur...@mariadb.org

___
Mailing list: https://launchpad.net/~maria-developers
Post to : maria-developers@lists.launchpad.net
Unsubscribe : https://launchpad.net/~maria-developers
More help   : https://help.launchpad.net/ListHelp


Re: [Maria-developers] Please review MDEV-9653 Assertion `length || !scale' failed in uint my_decimal_length_to_precision(uint, uint, bool)

2016-03-20 Thread Alexander Barkov
Hi Sergei,

It appeared to be very easy to fix all hybrid functions to use the same
attribute aggregation code, and therefore get rid of duplicate
implementations for Item_func_case_abbreviation2 and
Item_func_case.



This patch fixes:
- The original problem reported in MDEV-9653

- An additional problem, see new comments about COALESCE and IF
  returning 1.100 instead of just 1.1

- A new problem that I found:
  MDEV-9752 Wrong data type for COALEASCE(?,1) in prepared statements


Note, the patch does not cover LEAST/GREATEST. They have another
version of their own aggregation code. I don't want to touch them
in this fix. Let's do them separately.


Note, I also removed Item_func_case::agg_str_lengths().
It was a dead code.

Thanks.


On 03/17/2016 03:06 PM, Alexander Barkov wrote:
> Hi Sergei,
> 
> On 03/17/2016 01:14 PM, Sergei Golubchik wrote:
>> Hi, Alexander!
>>
>> On Mar 17, Alexander Barkov wrote:

 why is it NOT_FIXED_DEC? it seems to me that it has to be 0 here.
>>>
>>> Perhaps my example in the comments made you think that
>>> it happens in a very special case with CAST AS UNSIGNED.
>>> It also happens in simpler cases:
>>>
>>> SELECT CASE WHEN TRUE
>>> THEN COALESCE(NULL)
>>> ELSE 40
>>> END;
>>>
>>> I guess I should fix the example to this ^^^.
>>
>> Yes, please.
>>
>> More questions:
>>
>> 1. Perhaps my_decimal_length_to_precision() then? It's used not only in
>>Item_func_case::agg_num_lengths. Quick grepping finds more
>>potentially dangerous places. For example
>>Item_decimal_typecast::print.
> 
> I could not find places where my_decimal_length_to_precision() can be
> called with (0,NOT_FIXED_DEC).
> 
> Item_decimal_typecast::print() is safe.
> Item_decimal_typecast::max_length and Item_decimal_typecast::decimals
> are set to valid values in constructor. The latter cannot be NOT_FIXED_DEC.
> 
> 
>>
>> 2. Why my_decimal_length_to_precision is only used in Item_func_case?
>>How do other functions aggregate types? Perhaps CASE needs
>>to be changed to work like other similar functions do?
> 
> There is some code duplication in hybrid type functions:
> 
> - Item_func_case::agg_num_lengths()
> 
> - Item_func::count_decimal_length(), which is called from
>   Item_func_coalesce::fix_length_and_dec()
> 
> - Item_func_case_abbreviation2::fix_length_and_dec()
> 
> I earlier created a task for this for 10.2:
> https://jira.mariadb.org/browse/MDEV-9406
> 
> 
> I found new bugs when replying to this letter:
> 
> SELECT COALESCE(COALESCE(NULL), 1.1);
> SELECT IF(0, COALESCE(NULL), 1.1);
> 
> return 1.100, which is wrong.
> 
> I'll come up with a new patch soon.
> 
> Thanks.
> 
> 
>>
>> Regards,
>> Sergei
>> Chief Architect MariaDB
>> and secur...@mariadb.org
>>
diff --git a/mysql-test/r/func_hybrid_type.result b/mysql-test/r/func_hybrid_type.result
index 95a8a82..eeaa79e 100644
--- a/mysql-test/r/func_hybrid_type.result
+++ b/mysql-test/r/func_hybrid_type.result
@@ -2175,10 +2175,10 @@ def	case_a_b	12	19	19	Y	128	0	63
 def	case_b_a	12	19	19	Y	128	0	63
 def	coalesce_a_b	12	19	19	Y	128	0	63
 def	coalesce_b_a	12	19	19	Y	128	0	63
-def	if___a_b	12	10	19	Y	128	0	63
-def	if___b_a	12	10	19	Y	128	0	63
-def	ifnull___a_b	12	10	19	Y	128	0	63
-def	ifnull___b_a	12	10	19	Y	128	0	63
+def	if___a_b	12	19	19	Y	128	0	63
+def	if___b_a	12	19	19	Y	128	0	63
+def	ifnull___a_b	12	19	19	Y	128	0	63
+def	ifnull___b_a	12	19	19	Y	128	0	63
 def	leasta_b	12	10	19	Y	128	0	63
 def	leastb_a	12	10	19	Y	128	0	63
 def	greatest_a_b	12	10	19	Y	128	0	63
@@ -3396,5 +3396,36 @@ c1
 DROP TABLE t2;
 DROP TABLE t1;
 #
+# MDEV-9653 Assertion `length || !scale' failed in uint my_decimal_length_to_precision(uint, uint, bool)
+#
+SELECT CASE 0 WHEN 1 THEN (CASE 2 WHEN 3 THEN NULL END) WHEN 4 THEN 5 END;
+CASE 0 WHEN 1 THEN (CASE 2 WHEN 3 THEN NULL END) WHEN 4 THEN 5 END
+NULL
+SELECT CASE 0 WHEN 1 THEN (COALESCE(NULL)) WHEN 4 THEN 5 END;
+CASE 0 WHEN 1 THEN (COALESCE(NULL)) WHEN 4 THEN 5 END
+NULL
+SELECT CASE WHEN TRUE THEN COALESCE(NULL) ELSE 4 END;
+CASE WHEN TRUE THEN COALESCE(NULL) ELSE 4 END
+NULL
+SELECT COALESCE(COALESCE(NULL), 1.1) AS c0, IF(0, COALESCE(NULL), 1.1) AS c1;
+Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
+def	c0	246	4	3	Y	32896	1	63
+def	c1	246	4	3	Y	32896	1	63
+c0	c1
+1.1	1.1
+#
+# MDEV-9752 Wrong data type for COALEASCE(?,1) in prepared statements
+#
+PREPARE stmt FROM "CREATE TABLE t1 AS SELECT CONCAT(COALESCE(?,1)) AS a, CONCAT(CASE WHEN TRUE THEN ? ELSE 1 END) AS b";
+SET @a=1;
+EXECUTE stmt USING @a,@a;
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `a` varchar(21) DEFAULT NULL,
+  `b` varchar(21) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+#
 # End of 10.1 tests
 #
diff --git a/mysql-test/t/func_hybrid_type.test 

Re: [Maria-developers] Please review MDEV-9653 Assertion `length || !scale' failed in uint my_decimal_length_to_precision(uint, uint, bool)

2016-03-19 Thread Sergei Golubchik
Hi, Alexander!

Looks very good!

Just one comment - instead of a special treatment for MYSQL_TYPE_NULL, I
would try to set decimal=0 in Item_null. It already sets max_length=0,
and I cannot think of any reason why we might want decimal to be 31 for it.

On Mar 17, Alexander Barkov wrote:
> Hi Sergei,
> 
> It appeared to be very easy to fix all hybrid functions to use the same
> attribute aggregation code, and therefore get rid of duplicate
> implementations for Item_func_case_abbreviation2 and
> Item_func_case.
> 
> This patch fixes:
> - The original problem reported in MDEV-9653
> 
> - An additional problem, see new comments about COALESCE and IF
>   returning 1.100 instead of just 1.1
> 
> - A new problem that I found:
>   MDEV-9752 Wrong data type for COALEASCE(?,1) in prepared statements
> 
> 
> Note, the patch does not cover LEAST/GREATEST. They have another
> version of their own aggregation code. I don't want to touch them
> in this fix. Let's do them separately.
> 
> Note, I also removed Item_func_case::agg_str_lengths().
> It was a dead code.

Regards,
Sergei
Chief Architect MariaDB
and secur...@mariadb.org

___
Mailing list: https://launchpad.net/~maria-developers
Post to : maria-developers@lists.launchpad.net
Unsubscribe : https://launchpad.net/~maria-developers
More help   : https://help.launchpad.net/ListHelp


Re: [Maria-developers] Please review MDEV-9653 Assertion `length || !scale' failed in uint my_decimal_length_to_precision(uint, uint, bool)

2016-03-19 Thread Sergei Golubchik
Hi, Alexander!

On Mar 17, Alexander Barkov wrote:
> > 
> > why is it NOT_FIXED_DEC? it seems to me that it has to be 0 here.
> 
> Perhaps my example in the comments made you think that
> it happens in a very special case with CAST AS UNSIGNED.
> It also happens in simpler cases:
> 
> SELECT CASE WHEN TRUE
> THEN COALESCE(NULL)
> ELSE 40
> END;
> 
> I guess I should fix the example to this ^^^.

Yes, please.

More questions:

1. Perhaps my_decimal_length_to_precision() then? It's used not only in
   Item_func_case::agg_num_lengths. Quick grepping finds more
   potentially dangerous places. For example
   Item_decimal_typecast::print.

2. Why my_decimal_length_to_precision is only used in Item_func_case?
   How do other functions aggregate types? Perhaps CASE needs
   to be changed to work like other similar functions do?

Regards,
Sergei
Chief Architect MariaDB
and secur...@mariadb.org

___
Mailing list: https://launchpad.net/~maria-developers
Post to : maria-developers@lists.launchpad.net
Unsubscribe : https://launchpad.net/~maria-developers
More help   : https://help.launchpad.net/ListHelp


Re: [Maria-developers] Please review MDEV-9653 Assertion `length || !scale' failed in uint my_decimal_length_to_precision(uint, uint, bool)

2016-03-19 Thread Alexander Barkov
Hi Sergei,


On 03/17/2016 01:16 AM, Sergei Golubchik wrote:
> Hi, Alexander!
> 
> On Mar 16, Alexander Barkov wrote:
>> Hello Sergei,
>>
>> Please review a patch for:
>>
>> MDEV-9653 Assertion `length || !scale' failed in uint
>> my_decimal_length_to_precision(uint, uint, bool)
>>
>> Thanks!
> 
>> diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
>> index 2783a05..73a47ac 100644
>> --- a/sql/item_cmpfunc.cc
>> +++ b/sql/item_cmpfunc.cc
>> @@ -3086,10 +3086,28 @@ void Item_func_case::agg_str_lengths(Item* arg)
>>  
>>  void Item_func_case::agg_num_lengths(Item *arg)
>>  {
>> -  uint len= my_decimal_length_to_precision(arg->max_length, arg->decimals,
>> -   arg->unsigned_flag) - 
>> arg->decimals;
>> -  set_if_bigger(max_length, len); 
>> -  set_if_bigger(decimals, arg->decimals);
>> +  /*
>> +In a query like this:
>> +SELECT CASE WHEN TRUE
>> + THEN COALESCE(CAST(NULL AS UNSIGNED))
>> + ELSE 40
>> +   END;
>> +"arg" corresponding to Item_func_coalesce has:
>> +  arg->max_length==0
>> +  arg->decimals==31 (NOT_FIXED_DEC).
> 
> why is it NOT_FIXED_DEC? it seems to me that it has to be 0 here.

Perhaps my example in the comments made you think that
it happens in a very special case with CAST AS UNSIGNED.
It also happens in simpler cases:

SELECT CASE WHEN TRUE
THEN COALESCE(NULL)
ELSE 40
END;

I guess I should fix the example to this ^^^.


Explicit NULL has STRING_RESULT as cmp_type() and result_type().
So it behaves like strings. It's all around the code.


For COALESCE, IF, IFULL, CASE with explicit NULL input
decimals is set to NOT_FIXED_DEC in here:


bool Item_func::count_string_result_length(enum_field_types field_type_arg,
   Item **items, uint nitems)
{
  if (agg_arg_charsets_for_string_result(collation, items, nitems, 1))
return true;
  if (is_temporal_type(field_type_arg))
count_datetime_length(items, nitems);
  else
  {
decimals= NOT_FIXED_DEC;
count_only_length(items, nitems);
  }
  return false;
}


But the problem is that it happens not only here.
It's also done that way in all string functions:

For example:

MariaDB [test]> SELECT LEFT(null,1);
Field   1:  `LEFT(null,1)`
Type:   VAR_STRING
Collation:  binary (63)
Length: 0
Max_length: 0
Decimals:   31  -- NOT_FIXED_DEC
Flags:  BINARY


MariaDB [test]> SELECT CONCAT(null);
Field   1:  `CONCAT(null)`
Type:   VAR_STRING
Collation:  binary (63)
Length: 0
Max_length: 0
Decimals:   31  -- NOT_FIXED_DEC
Flags:  BINARY

It has always been that way, for years.


I guess other non-function Item types do the same for an explicit NULL
input.


Fixing all Items to have decimals==0 in case
of explicit NULL input looks too dangerous in 10.1.
(btw, in 10.2 it should be easier).


So in 10,1 I decided to fix Item_func_case::agg_num_lengths.

> 
> Regards,
> Sergei
> Chief Architect MariaDB
> and secur...@mariadb.org
> 

___
Mailing list: https://launchpad.net/~maria-developers
Post to : maria-developers@lists.launchpad.net
Unsubscribe : https://launchpad.net/~maria-developers
More help   : https://help.launchpad.net/ListHelp


Re: [Maria-developers] Please review MDEV-9653 Assertion `length || !scale' failed in uint my_decimal_length_to_precision(uint, uint, bool)

2016-03-19 Thread Alexander Barkov
Hi Sergei,

On 03/17/2016 01:14 PM, Sergei Golubchik wrote:
> Hi, Alexander!
> 
> On Mar 17, Alexander Barkov wrote:
>>>
>>> why is it NOT_FIXED_DEC? it seems to me that it has to be 0 here.
>>
>> Perhaps my example in the comments made you think that
>> it happens in a very special case with CAST AS UNSIGNED.
>> It also happens in simpler cases:
>>
>> SELECT CASE WHEN TRUE
>> THEN COALESCE(NULL)
>> ELSE 40
>> END;
>>
>> I guess I should fix the example to this ^^^.
> 
> Yes, please.
> 
> More questions:
> 
> 1. Perhaps my_decimal_length_to_precision() then? It's used not only in
>Item_func_case::agg_num_lengths. Quick grepping finds more
>potentially dangerous places. For example
>Item_decimal_typecast::print.

I could not find places where my_decimal_length_to_precision() can be
called with (0,NOT_FIXED_DEC).

Item_decimal_typecast::print() is safe.
Item_decimal_typecast::max_length and Item_decimal_typecast::decimals
are set to valid values in constructor. The latter cannot be NOT_FIXED_DEC.


> 
> 2. Why my_decimal_length_to_precision is only used in Item_func_case?
>How do other functions aggregate types? Perhaps CASE needs
>to be changed to work like other similar functions do?

There is some code duplication in hybrid type functions:

- Item_func_case::agg_num_lengths()

- Item_func::count_decimal_length(), which is called from
  Item_func_coalesce::fix_length_and_dec()

- Item_func_case_abbreviation2::fix_length_and_dec()

I earlier created a task for this for 10.2:
https://jira.mariadb.org/browse/MDEV-9406


I found new bugs when replying to this letter:

SELECT COALESCE(COALESCE(NULL), 1.1);
SELECT IF(0, COALESCE(NULL), 1.1);

return 1.100, which is wrong.

I'll come up with a new patch soon.

Thanks.


> 
> Regards,
> Sergei
> Chief Architect MariaDB
> and secur...@mariadb.org
> 

___
Mailing list: https://launchpad.net/~maria-developers
Post to : maria-developers@lists.launchpad.net
Unsubscribe : https://launchpad.net/~maria-developers
More help   : https://help.launchpad.net/ListHelp


[Maria-developers] Please review MDEV-9653 Assertion `length || !scale' failed in uint my_decimal_length_to_precision(uint, uint, bool)

2016-03-19 Thread Alexander Barkov
Hello Sergei,

Please review a patch for:

MDEV-9653 Assertion `length || !scale' failed in uint
my_decimal_length_to_precision(uint, uint, bool)

Thanks!
diff --git a/mysql-test/r/func_hybrid_type.result b/mysql-test/r/func_hybrid_type.result
index 95a8a82..29f9f46 100644
--- a/mysql-test/r/func_hybrid_type.result
+++ b/mysql-test/r/func_hybrid_type.result
@@ -3396,5 +3396,17 @@ c1
 DROP TABLE t2;
 DROP TABLE t1;
 #
+# MDEV-9653 Assertion `length || !scale' failed in uint my_decimal_length_to_precision(uint, uint, bool)
+#
+SELECT CASE 0 WHEN 1 THEN (CASE 2 WHEN 3 THEN NULL END) WHEN 4 THEN 5 END;
+CASE 0 WHEN 1 THEN (CASE 2 WHEN 3 THEN NULL END) WHEN 4 THEN 5 END
+NULL
+SELECT CASE 0 WHEN 1 THEN (COALESCE(NULL)) WHEN 4 THEN 5 END;
+CASE 0 WHEN 1 THEN (COALESCE(NULL)) WHEN 4 THEN 5 END
+NULL
+SELECT CASE WHEN TRUE THEN COALESCE(NULL) ELSE 4 END;
+CASE WHEN TRUE THEN COALESCE(NULL) ELSE 4 END
+NULL
+#
 # End of 10.1 tests
 #
diff --git a/mysql-test/t/func_hybrid_type.test b/mysql-test/t/func_hybrid_type.test
index 047e5f7..cc5d067 100644
--- a/mysql-test/t/func_hybrid_type.test
+++ b/mysql-test/t/func_hybrid_type.test
@@ -434,5 +434,13 @@ DROP TABLE t1;
 
 
 --echo #
+--echo # MDEV-9653 Assertion `length || !scale' failed in uint my_decimal_length_to_precision(uint, uint, bool)
+--echo #
+SELECT CASE 0 WHEN 1 THEN (CASE 2 WHEN 3 THEN NULL END) WHEN 4 THEN 5 END;
+SELECT CASE 0 WHEN 1 THEN (COALESCE(NULL)) WHEN 4 THEN 5 END;
+SELECT CASE WHEN TRUE THEN COALESCE(NULL) ELSE 4 END;
+
+
+--echo #
 --echo # End of 10.1 tests
 --echo #
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 2783a05..73a47ac 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -3086,10 +3086,28 @@ void Item_func_case::agg_str_lengths(Item* arg)
 
 void Item_func_case::agg_num_lengths(Item *arg)
 {
-  uint len= my_decimal_length_to_precision(arg->max_length, arg->decimals,
-   arg->unsigned_flag) - arg->decimals;
-  set_if_bigger(max_length, len); 
-  set_if_bigger(decimals, arg->decimals);
+  /*
+In a query like this:
+SELECT CASE WHEN TRUE
+ THEN COALESCE(CAST(NULL AS UNSIGNED))
+ ELSE 40
+   END;
+"arg" corresponding to Item_func_coalesce has:
+  arg->max_length==0
+  arg->decimals==31 (NOT_FIXED_DEC).
+my_decimal_length_to_precision() does not expect this kind of input
+and would crash on DBUG_ASSERT. See MDEV-9653.
+So let's skip length calculation for explicit NULLs and
+for expressions derived from explicit NULLs.
+  */
+  if (arg->field_type() != MYSQL_TYPE_NULL)
+  {
+uint len= my_decimal_length_to_precision(arg->max_length, arg->decimals,
+ arg->unsigned_flag) -
+ arg->decimals;
+set_if_bigger(max_length, len);
+set_if_bigger(decimals, arg->decimals);
+  }
   unsigned_flag= unsigned_flag && arg->unsigned_flag; 
 }
 
___
Mailing list: https://launchpad.net/~maria-developers
Post to : maria-developers@lists.launchpad.net
Unsubscribe : https://launchpad.net/~maria-developers
More help   : https://help.launchpad.net/ListHelp