Bug #103781 PREPARE DATE_ADD/DATE_SUB incorrectly returns DATETIME
Submitted: 22 May 2021 20:49 Modified: 29 Oct 2021 16:32
Reporter: Max Nyman Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: DML Severity:S3 (Non-critical)
Version:8.0.23/8.0.25 OS:Ubuntu
Assigned to: CPU Architecture:Any

[22 May 2021 20:49] Max Nyman
Description:
When using prepared statements, the DATE_ADD and DATE_SUB functions return value is in DateTime format, even if calculations involve only YEAR, MONTH, and DAY parts (that is, no time parts).

This behavior is changed from at least MySQL version 8.0.21, in which the statement only returned the date part.

PREPARE stmt1 FROM "SELECT DATE_ADD(?, INTERVAL 1 DAY) AS a";
SET @pc = '2021-05-22';
EXECUTE stmt1 USING @pc;

Returns # a -> '2021-05-23 00:00:00.000000'

As a note, the below statement yields another result:

PREPARE stmt2 FROM "SELECT DATE_ADD('2021-05-22', INTERVAL 1 DAY) AS a";
EXECUTE stmt2;

Returns # a -> '2021-05-23'

How to repeat:
PREPARE stmt1 FROM "SELECT DATE_ADD(?, INTERVAL 1 DAY)";

SET @pc = '2021-05-22';

EXECUTE stmt1 USING @pc;

Suggested fix:
Returning only the date part when calculations do not involve any time parts.
[23 May 2021 1:54] MySQL Verification Team
Thank you for the bug report.
[3 Jun 2021 21:05] Roy Lyseng
The behavior has been changed because we now do type resolving based on the prepared statement only. For DATE_ADD, if the first argument is a dynamic parameter, its type is assumed to be DATETIME, since it is the most comprehensive temporal type, and since a DATE value can easily be converted to a DATETIME value.

However, you can tell MySQL that the first argument is a DATE value, and hence the returned type is a DATE value, with the syntax:

  PREPARE stmt1 FROM "SELECT DATE_ADD(CAST(? AS DATE), INTERVAL 1 DAY)";
[29 Oct 2021 16:32] Jon Stephens
Documented fix as follows in the MySQL 8.0.28 changelog:

      
    When using prepared statements, the DATE_ADD() and DATE_SUB()
    functions returned DATETIME values, even when calculations
    involved combinations of YEAR, MONTH, or DAY parts only (that
    is, no time parts).

    Previous to implementing single preparation of prepared
    statements in MySQL 8.0.22, TIME values were returned in such
    cases; before this was done, values used as arguments and their
    types were used to determine the result type of certain temporal
    functions at resolution time, such as DATE_ADD(), DATE_SUB(),
    and ADDTIME(). Afterwards, user variable references and dynamic
    parameters are considered constant for the lifetime of one
    execution only, requiring that the return type be determined in
    another fashion, in this case from the function type. For
    example, the default resolved type for DATE_ADD() was deemed to
    be DATETIME if the first argument was a dynamic parameter, since
    a DATETIME accomodates all temporal values and implicit
    reprepare can be avoided.

    The change just described represents a regression; the problem
    is better solved by deriving a more precise resolved data type,
    and performing a reprepare only if that does not match the
    actual value of the parameter. (Such functionality was already
    in use in the MySQL server for numeric parameters.) The better
    solution is implemented by this fix.

    We now parse string and numeric values when temporal values are
    expected. When a valid temporal value is found, the value is
    converted. This fix also improves determination of resolved data
    types for temporal functions.

    The DATE_ADD() and DATE_SUB() functions (and their synonyms
    functions ADDDATE() and SUBDATE()) are changed by this fix as
    follows:

        If the first argument is a dynamic parameter, its resolved
        type is DATE if the second argument is an interval that
        contains some combination of YEAR, MONTH, or DAY values
        only; otherwise, its type is DATETIME.
      
        If the first argument is resolved as DATETIME, the resolved
        type of the function is also DATETIME.

        If the first argument is DATE, the resolved type of the
        function is also DATE, unless the interval argument uses
        HOUR, MINUTE, or SECOND, in which case it is DATETIME.
      
        If the first argument is TIME, the resolved type is also
        TIME, unless the interval argument uses YEAR, MONTH, or DAY,
        in which case the resolved type of the function is DATETIME.
  
    If none of the preceding conditions are met, the function is
    resolved as VARCHAR (as in MySQL 8.0.21 and earlier).
  
    The ADDTIME() and SUBTIME() functions are changed as follows:

        If the first argument is a dynamic parameter, the resolved
        type is TIME, rather than DATETIME.

        Otherwise, the resolved type of the function is derived from
        the resolved type of the first argument.

    In addition, for DATE_ADD() and DATE_SUB(), if the resolved type
    of the first argument is DATE, and a DATETIME value is provided,
    the statement is reprepared so that the function has the
    resolved type DATETIME. Behavior is unchanged when a TIME value
    is provided.

    For ADDTIME() and SUBTIME(), there are no forced reprepares.

Also noted the behaviour change(s) in the descriptions of the affected functions in the 8.0 Manual.

Closed.

Closed.