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

Multi-table UPDATE JOIN leaves a stale STORED generated column after a BEFORE UPDATE trigger changes its base column

    XMLWordPrintable

Details

    • Not for Release Notes
    • Hide
      On the MariaDB multi-table {{UPDATE ... JOIN}} execution path, a {{BEFORE
      UPDATE}} trigger can change a base column while the row's {{STORED}} generated column remains calculated from the value before the trigger change. The successful UPDATE therefore persists a row that violates its own generated expression.
      Show
      On the MariaDB multi-table {{UPDATE ... JOIN}} execution path, a {{BEFORE UPDATE}} trigger can change a base column while the row's {{STORED}} generated column remains calculated from the value before the trigger change. The successful UPDATE therefore persists a row that violates its own generated expression.

    Description

      Environment

      • Product: MariaDB Server
      • Observed affected versions: 10.3, 10.6, and 12.2.2
      • Storage engine: InnoDB
      • Clean comparison: MySQL 8.0.36 computes the expected generated value for the
        same multi-table update.

      Minimal reproduction

      DROP DATABASE IF EXISTS mdev_multitable_generated;
      CREATE DATABASE mdev_multitable_generated;
      USE mdev_multitable_generated;
       
      CREATE TABLE p(
        id   INT PRIMARY KEY,
        flag INT NOT NULL
      ) ENGINE=InnoDB;
       
      CREATE TABLE c(
        id  INT PRIMARY KEY,
        pid INT NOT NULL,
        v   INT NOT NULL,
        g   INT GENERATED ALWAYS AS (v * 2) STORED,
        FOREIGN KEY (pid) REFERENCES p(id)
      ) ENGINE=InnoDB;
       
      DELIMITER //
      CREATE TRIGGER c_bu BEFORE UPDATE ON c FOR EACH ROW
      BEGIN
        SET NEW.v = NEW.v + 10;
      END//
      DELIMITER ;
       
      INSERT INTO p VALUES (1, 0), (2, 1);
      INSERT INTO c(id, pid, v) VALUES (10, 1, 5), (20, 2, 7);
       
      UPDATE p JOIN c ON c.pid = p.id
      SET p.flag = p.flag + 1,
          c.v = c.v + 1
      WHERE p.flag >= 0;
       
      SELECT id, pid, v, g, v * 2 AS expected_g, g = v * 2 AS invariant_holds
      FROM c
      ORDER BY id;
      

      Expected result

      The UPDATE first proposes v=6 and v=8. The trigger adds 10, so the
      persisted values should be v=16 and v=18 and the generated values must
      be 32 and 36:

      id  pid  v   g   expected_g  invariant_holds
      10  1    16  32  32          1
      20  2    18  36  36          1
      

      Actual result on MariaDB 10.3, 10.6, and 12.2.2

      id  pid  v   g   expected_g  invariant_holds
      10  1    16  12  32          0
      20  2    18  16  36          0
      

      The persisted g values are 6 * 2 and 8 * 2: they reflect the
      pre-trigger proposal rather than the final value stored in v.

      Relevant controls

      • On the same MariaDB schema, the single-table statement
        UPDATE c SET v = v + 1 WHERE id IN (10, 20) invokes the same trigger and
        persists g=32 and g=36.
      • MySQL 8.0.36 persists g=32 and g=36 for the multi-table statement.
      • A transaction savepoint/rollback removes both the base-column change and the
        generated value together; the observed problem is the successful
        materialization before rollback, not rollback cleanup.

      Constraint consequence (same mechanism)

      Adding CHECK (g < 20) to c makes the consequence externally visible:
      the multi-table update above is accepted by affected MariaDB versions with
      v=16, g=12, although a correctly recomputed g=32 would violate the
      constraint. MySQL 8.0.36 rejects the update. I regard this as an impact of
      the same stale-materialization mechanism, not a separate issue report.

      Repetition and nearby issues checked

      The minimal multi-table signal reproduced three times on each of MariaDB 10.3,
      10.6, and 12.2.2. The single-table control was clean. I checked nearby
      reports MDEV-36026, MDEV-36191, MDEV-36717, and MDEV-39711; they concern other
      INSERT ... SELECT, metadata, or system-versioning paths and do not describe
      a successful UPDATE ... JOIN persisting a stored generated value computed
      before a BEFORE UPDATE trigger mutation.

      Attachments

        Issue Links

          Activity

            People

              Unassigned Unassigned
              JaysonL Zhensheng Luo
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Git Integration

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