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

Dynamic-column functions truncate a numeric column number to 32 bits before the 0..65535 check: COLUMN_CREATE(4294967297, 'a') creates column 1

    XMLWordPrintable

Details

    Description

      A numeric dynamic-column number must be in 0..65535; 65536 and -1 are rejected
      with ERROR 1921 "Illegal value used as argument of dynamic column function". A
      number of 2^32 or more is not rejected: it is first truncated to 32 bits, and the
      function then writes, adds or deletes a different, existing column:

      SELECT COLUMN_JSON(COLUMN_CREATE(65535, 'a'));        -- {"65535":"a"}
      SELECT COLUMN_JSON(COLUMN_CREATE(65536, 'a'));        -- ERROR 1921
      SELECT COLUMN_JSON(COLUMN_CREATE(-1, 'a'));           -- ERROR 1921
      SELECT COLUMN_JSON(COLUMN_CREATE(4294967296, 'a'));   -- {"0":"a"}
      SELECT COLUMN_JSON(COLUMN_CREATE(4294967297, 'a'));   -- {"1":"a"}
       
      SELECT COLUMN_JSON(COLUMN_ADD(COLUMN_CREATE(1, 'a'), 4294967298, 'b'));
                                                            -- {"1":"a","2":"b"}
      SELECT COLUMN_JSON(COLUMN_ADD(COLUMN_CREATE(1, 'a', 2, 'x'), 4294967298, 'b'));
                                                            -- {"1":"a","2":"b"}   (column 2 overwritten)
      SELECT COLUMN_JSON(COLUMN_DELETE(COLUMN_CREATE(1, 'a', 2, 'b'), 4294967297));
                                                            -- {"2":"b"}   (column 1 deleted)
      

      COLUMN_DELETE with 2^32+1 deletes column 1, and COLUMN_ADD with 2^32+2
      overwrites column 2 if it exists, so an out-of-range number silently changes
      data. (COLUMN_GET and COLUMN_EXISTS with 4294967297 return NULL – they take a
      different path and are not affected.)

      Where (main, 82964737): sql/item_strfunc.cc,
      Item_func_dyncol_create::prepare_arguments(), line 4915:

            keys_num[i]= (uint) args[i * 2]->val_int();
      

      The 65535 check in the dynamic-column library sees the truncated value.
      COLUMN_ADD (Item_func_dyncol_add, a subclass) and COLUMN_DELETE (built by
      create_func_dyncol_delete() in sql/item_create.cc as an Item_func_dyncol_add with
      NULL values) go through the same prepare_arguments().

      Not the same code path as MDEV-40971 / MDEV-40987 (JSON path indexes, PERIOD_ADD,
      SUBSTRING_INDEX, ELT): this is the column-number argument of the dynamic-column functions, in Item_func_dyncol_create::prepare_arguments() (sql/item_strfunc.cc);
      a fix there does not change this one.

      ================================================================================
      STEPS TO REPRODUCE
      ================================================================================

      SELECT COLUMN_JSON(COLUMN_CREATE(65536, 'a'));                                -- 1
      SELECT COLUMN_JSON(COLUMN_CREATE(4294967297, 'a'));                           -- 2
      SELECT COLUMN_JSON(COLUMN_DELETE(COLUMN_CREATE(1, 'a', 2, 'b'), 4294967297)); -- 3
      SELECT COLUMN_JSON(COLUMN_ADD(COLUMN_CREATE(1, 'a'), 4294967298, 'b'));       -- 4
      

      ================================================================================
      EXPECTED
      ================================================================================

      1.  ERROR 1921 Illegal value used as argument of dynamic column function
      2.  ERROR 1921
      3.  ERROR 1921
      4.  ERROR 1921
      

      ================================================================================
      ACTUAL (13.2.0 main 82964737; identical on 13.0.2, 11.8.9, 10.11.19, 10.6.28)
      ================================================================================

      1.  ERROR 1921 Illegal value used as argument of dynamic column function
      2.  {"1":"a"}
      3.  {"2":"b"}
      4.  {"1":"a","2":"b"}
      

      ================================================================================
      SUGGESTED FIX
      ================================================================================

      Check the longlong value (and unsigned_flag) against 0..65535 – or at least
      against 0..UINT_MAX32 – before the (uint) cast in prepare_arguments(), and raise
      ER_DYN_COL_DATA (1921) as for 65536.

      ================================================================================
      ATTACHMENTS
      ================================================================================

      repro.py (pymysql; exits 1 while the recorded wrong answers are returned).
      VERIFY-main-82964737-2026-10-14.txt: transcript on main and the four release images.

      Attachments

        Activity

          People

            sanja Oleksandr Byelkin
            kehan5800 Ke Han
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:

              Git Integration

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