WL#529: Add CAST to FLOAT/DOUBLE/REAL

Affects: Server-8.0   —   Status: Complete

MySQL will add another CAST function to support cast operations to FLOATING
point data types according to the SQL Standard.

Rationale:
Our current CAST capabilities do not permit users to cast values to floating
point data types as required by standard SQL and supported by other DBMSs. Since
implicit CASTs support a greater variety of cast possibilities, the explicit
CAST support should be augmented to match it. 

For example: 
SELECT CAST(1/3 AS DOUBLE); # not allowed at this moment

Will return:

mysql> SELECT CAST(1/3 AS DOUBLE);
Field   1:  `CAST(1/3 AS DOUBLE)`
Catalog:    `def`
Database:   ``
Table:      ``
Org_table:  ``
Type:       DOUBLE
Collation:  binary (63)
Length:     22
Max_length: 11
Decimals:   31
Flags:      BINARY NUM 


+---------------------+
| CAST(1/3 AS DOUBLE) |
+---------------------+
|         0.333333333 |
+---------------------+
1 row in set (0,00 sec)

FR1: CAST expressions must also support as  floating point data 
     types: FLOAT(*), DOUBLE, DOUBLE PRECISION(**), REAL(***)

      ::= CAST   AS 
                                   
      ::= DOUBLE | DOUBLE PRECISION | REAL |
                       FLOAT [ N  ];

     Example:
       CAST (1/3 AS FLOAT);
                    ^^^^^
       CAST (1/3 AS FLOAT(22));
                    ^^^^^^^^^
       CAST (1/3 AS DOUBLE);
                    ^^^^^^
       CAST (1/3 AS DOUBLE PRECISION);
                    ^^^^^^^^^^^^^^^^
       CAST (1/3 AS REAL);
                    ^^^^
Notes:(*) FLOAT is ambiguous in MySQL:  it is both the 32bit floating point data
          type (which is REAL in SQL standard), and it can be used to specify
          a floating point type with the minimum specified binary precision.
    (**)  DOUBLE PRECISION - a synonym for DOUBLE (in MySQL)
    (***) REAL - synonym for DOUBLE - unless REAL_AS_FLOAT sql_mode is
          specified.

FR2: for FLOAT(N) as , if N is specified then, if N> 53 or N < 0, 
     error should be returned.

FR3: for FLOAT(N) as , if N is specified and N <= 24, then the 
     cast will be to FLOAT

     ex: CAST(1/3 AS FLOAT(10)) <=> CAST (1/3 AS FLOAT)

FR4: for FLOAT(N) as , if N is specified and N > 24, then the cast 
     will be to DOUBLE

     ex: CAST(1/3 AS FLOAT(24)) <=> CAST (1/3 AS DOUBLE)

FR5: if  is REAL and sql_mode is set to REAL_AS_FLOAT then the cast
     will be to FLOAT ex: CAST(1/3 AS REAL) <=> CAST (1/3 AS FLOAT)

FR6: if  is REAL and sql_mode is NOT set to REAL_AS_FLOAT then the 
     cast will be to DOUBLE ex: CAST(1/3 AS REAL) <=> CAST (1/3 AS DOUBLE)

Before discussing the cast function to float/double let's first mention how
MySQL defines FLOAT/DOUBLE/REAL.

- FLOAT/DOUBLE/DOUBLE PRECISION/REAL
- FLOAT(N), where N BETWEEN 0-53 (*)
- FLOAT/DOUBLE/REAL (M,D) (**)

Since "FLOAT/DOUBLE/REAL (M,D)" is going to be deprecated soon, 
CAST (x AS floating-point-data-type) will only need the handle the first two
cases.

-----
(*) According to the SQL standard N should be the binary precision specifier,
and the binary precision should be equal to or greater than the value of the
specified precision (N). MySQL chose to translate this to either REAL (FLOAT) or
DOUBLE.
Ex. FLOAT(N) where N= 0-24 will be stored as FLOAT and N=25-53 will be stored as
DOUBLE.

(**) Non-standard syntax  where (M,D) means that values can be stored with up to
M digits in total, of which D digits may be after the decimal point.
---

With regards to the source of the cast, we have the following specifications in
 the SQL2016 standard:
The following chart shows the standard SQL possibilities for casting 
between approximate numeric and the other data types. "Y” indicates that the
combination is syntactically valid without restriction; “M” indicates that the
combination is valid (subject to other Syntax Rules in this Sub-clause being
satisfied;) and “N” indicate that the combination is not valid.

Source data type:   EN  AN  DF  C  D  T  TS  B  RW
Allowed:             Y   Y   Y  Y  N  N   N  N   N

Where:
EN = Exact Numeric
AN = Approximate Numeric
DF = Decimal Floating-Point
C = Character (Fixed- or Variable-Length, or Character Large Object)
D = Date
T = Time
TS = Timestamp
B = Binary (Fixed- or Variable-Length or Binary Large Object)
RW = Row type

Where client-facing MySQL data types are:

EN = YEAR, TINYINT, SMALLINT, MEDIUMINT, INT, INTEGER, BIGINT,(BIT, BOOL, BOOLEAN)
AN = FLOAT, DOUBLE, DOUBLE PRECISION, REAL
DF = DECIMAL, DEC
C = CHAR, VARCHAR, TINYTEXT, TEXT, MEDIUMTEXT, LONGTEXT
D = DATE, DATETIME
T = TIME
TS = TIMESTAMP
B = BINARY, VARBINARY, TINYBLOB, BLOB, MEDIUMBLOB, LONGBLOB, ENUM, SET, JSON,
GEOMETRY (BIT is INTEGER in MySQL)
RW = N/A

MySQL support for cast will be extended beyond the SQL2016 standard and will
only reject GEOMETRY.
Item_typecast_real class will be created to store the CAST details

create_func_cast will handle the creation of Item_typecast_real and validation
of the optional N parameter so that:
- if N > 53 or N < 0, error

Renamed existing typecast classes:

INT_RESULT:
Item_func_signed => Item_typecast_signed
Item_func_unsigned => Item_typecast_unsigned

DECIMAL_RESULT:
Item_decimal_typecast => Item_typecast_decimal

REAL_RESULT:
Item_typecast_real (added in WL#529)

STRING_RESULT(date/time related):
Item_date_typecast => Item_typecast_date
Item_time_typecast => Item_typecast_time
Item_datetime_typecast => Item_typecast_datetime

STRING_RESULT(will not be used by this WL):
Item_func_binary => Item_typecast _binary
Item_json_typecast => Item_typecast_json
Item_char_typecast => Item_typecast_char

Changes to the parser:

cast_type:
     [....]
        | real_type opt_precision
          {
            $$.target = YYTHD->variables.sql_mode & MODE_REAL_AS_FLOAT ?
              ITEM_CAST_FLOAT : ITEM_CAST_DOUBLE;
            $$.charset= nullptr;
            $$.length= 0;
            $$.dec= 0;
          }
        | FLOAT_SYM float_options
          {
            $$.target = ITEM_CAST_FLOAT;
            $$.charset= nullptr;
            $$.length= $2.length;
            $$.dec= 0;
          }