Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-41344

Timestamp conversion does not work for TRADITIONAL mode

    XMLWordPrintable

Details

    • Can result in unexpected behaviour

    Description

      Field_timestamp::get_copy_func() silently skips the row_end 2038→2106 conversion added by MDEV-32188 when the session's sql_mode includes NO_ZERO_DATE or NO_ZERO_IN_DATE (e.g. sql_mode=TRADITIONAL), or when the source/destination fields are not eq_def().

      ALTER TABLE ... FORCE (and any other copy-algorithm ALTER) reports success and gives no error or warning, but the old-format row_end sentinel is left unconverted.

      Reproduce

      Add "traditional" combination to versioning.old_timestamp test:

      --- /dev/null
      +++ mysql-test/suite/versioning/t/old_timestamp.combinations
      @@ -0,0 +1,4 @@
      +[normal]
      +
      +[traditional]
      +sql-mode=TRADITIONAL
      

      Result

      --- /home/midenok/src/mariadb/11.8/src/mysql-test/suite/versioning/r/old_timestamp.result       2026-08-11 16:23:10.685440488 +0300
      +++ /home/midenok/src/mariadb/11.8/src/mysql-test/suite/versioning/r/old_timestamp.reject       2026-09-29 13:06:31.109875890 +0300
      @@ -90,17 +90,17 @@
       set @@time_zone="+00:00";
       select a,row_end from sv_basic for system_time all;
       a      row_end
      -1      2106-02-07 06:28:15.999999
      +1      2038-01-19 03:14:07.999999
       2      2023-12-19 14:23:22.305292
      -3      2106-02-07 06:28:15.999999
      +3      2038-01-19 03:14:07.999999
       select a,row_end from sv_partition for system_time all;
       a      row_end
      -1      2106-02-07 06:28:15.999999
      +1      2038-01-19 03:14:07.999999
       2      2023-12-19 14:23:22.332359
      -3      2106-02-07 06:28:15.999999
      +3      2038-01-19 03:14:07.999999
       select a,row_foo_end from sv_explicit for system_time all;
       a      row_foo_end
      -1      2106-02-07 06:28:15.999999
      +1      2038-01-19 03:14:07.999999
       2      2023-12-19 14:23:22.359113
      -3      2106-02-07 06:28:15.999999
      +3      2038-01-19 03:14:07.999999
      

      Root cause

      Field_temporal::get_copy_func() (sql/field_conv.cc) returns do_field_datetime instead of do_field_eq whenever:

      • sql_mode has NO_ZERO_DATE or NO_ZERO_IN_DATE set, or
      • the two fields are not eq_def()

      Field::Copy_func *Field_timestamp::get_copy_func(const Field *from) const
      {
        Field::Copy_func *copy= Field_temporal::get_copy_func(from);
        if (from->type() == MYSQL_TYPE_TIMESTAMP)
        {
          if (copy == do_field_datetime)
            return do_field_timestamp;                 // <-- returns here first
          if (copy == do_field_eq &&
              from->table->file->check_versioned_compatibility() &&
              (flags & VERS_ROW_END) && (from->flags & VERS_ROW_END))
            return do_field_versioned_timestamp;        // <-- never reached
        }
        return copy;
      }
      

      The do_field_datetime check is evaluated before the VERS_ROW_END check, so whenever the generic path picks do_field_datetime, the function returns immediately and the versioned-timestamp byte-conversion branch is never reached. do_field_timestamp/do_field_datetime perform a plain semantic value copy via save_in_field(), which has no knowledge of the row_end sentinel and faithfully re-stores the old (2038) value unchanged.

      The bug only manifests for the system-versioned row_end column, since that is the only field carrying the VERS_ROW_END flag; ordinary TIMESTAMP columns are unaffected (they never had a special-case branch to lose).

      Fix

      Check the VERS_ROW_END conversion need first, independently of what the generic path picked, since row_end is server-maintained and never zero:

      Field::Copy_func *Field_timestamp::get_copy_func(const Field *from) const
      {
        Field::Copy_func *copy= Field_temporal::get_copy_func(from);
        if (from->type() == MYSQL_TYPE_TIMESTAMP)
        {
          if ((flags & VERS_ROW_END) && (from->flags & VERS_ROW_END) &&
              from->table->file->check_versioned_compatibility() &&
              !from->table->vers_new_format())
            return do_field_versioned_timestamp;
          if (copy == do_field_datetime)
            return do_field_timestamp;
        }
        return copy;
      }
      

      Attachments

        Activity

          People

            midenok Aleksey Midenkov
            midenok Aleksey Midenkov
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Time Tracking

                Estimated:
                Original Estimate - 0d
                0d
                Remaining:
                Remaining Estimate - 0d
                0d
                Logged:
                Time Spent - 2.5h
                2.5h

                Git Integration

                  Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.