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

slave_run_triggers_for_rbr: assertion `trigger_table->pos_in_table_list-> trg_event_map & trg2bit(event)' fails when one statement's row events are of mixed kinds

    XMLWordPrintable

Details

    • Can result in hang or crash
    • Q4/2026 Replic. Development

    Description

      DESCRIPTION
      ===========
      On a slave with slave_run_triggers_for_rbr enabled, the per-table trigger
      event map is computed from the FIRST row event of a statement group only.
      The block that assigns it is guarded by

      sql/log_event_server.cc:5001 if (!thd->open_tables)

      with the assignment at

      sql/log_event_server.cc:5075 tables->trg_event_map= new_trg_event_map;

      taken from that event's get_trg_event_map(). Once the tables are open, later
      row events of the same group skip the block, so the map still describes the
      first event's kind.

      Any statement whose group mixes row-event kinds therefore fires triggers of a
      kind absent from the map, and

      sql/sql_trigger.cc:2840
      DBUG_ASSERT(trigger_table->pos_in_table_list->trg_event_map &
      trg2bit(event));

      fails. In a release build the assert is compiled out and the trigger runs
      anyway - but add_tables_and_routines_for_triggers() (sql/sql_trigger.cc:2940)
      walked the same stale map at open time, so that trigger's body was never
      prelocked. Its referenced tables and routines are not open or locked for the
      statement. The assert's own comment states the invariant being violated:
      "This trigger must have been processed by the pre-locking algorithm."

      Two ways to produce a mixed group, both verified to crash a debug slave:

      1. INSERT ... ON DUPLICATE KEY UPDATE on a plain table, where one row updates
      and another inserts: Update_rows then Write_rows. Nothing to do with
      system versioning.

      2. UPDATE of a row in a system-versioned table. Measured event sequence for
      such an UPDATE is Update_rows (live row) + Write_rows (archived history
      row, from vers_insert_history_row). The master fires only BEFORE/AFTER
      UPDATE for the whole statement, so the slave-side INSERT triggers are
      also spurious, independently of the assert.

      REPRODUCE (case 1, plain table)
      ===============================
      master: create table t (a int primary key, b int) engine=innodb;
      create table trg_log (id int auto_increment primary key,
      note varchar(64)) engine=innodb;
      insert into t values (1,1);
      slave: set @@global.slave_run_triggers_for_rbr= YES;
      create trigger t_bi before insert on t for each row
      insert into trg_log (note) values (concat('B INSERT a=', NEW.a));
      create trigger t_bu before update on t for each row
      insert into trg_log (note) values (concat('B UPDATE a=', OLD.a));
      master: insert into t values (1,10), (2,20)
      on duplicate key update b= values(b);

      => slave asserts in Table_triggers_list::process_triggers()

      REPRODUCE (case 2, system-versioned table)
      ==========================================
      Same setup, with
      create table tv (a int primary key, b int)
      with system versioning engine=innodb;
      triggers for insert and update on the slave, then
      master: insert into tv values (1,1); – ok, INSERT triggers fire
      master: update tv set b=2 where a=1; – slave asserts

      NOTES
      =====

      • Predates CDR. Update_rows_log_event::get_trg_event_map() returned only
        trg2bit(TRG_EVENT_UPDATE) before the CDR work; the CDR change added
        conflict bits only, never TRG_EVENT_INSERT.
      • Fixing the assert alone leaves the prelocking violation. A correct fix has
        to make the map right BEFORE open_and_lock_tables, and the group's later
        event kinds are not known at that point - so it needs a static
        over-approximation (e.g. every Rows_log_event advertising
        INSERT|UPDATE|DELETE while slave_run_triggers_for_rbr is on), at the cost
        of prelocking more triggers than a given statement needs.
      • Not verified on branches other than 12.3, nor on a community build, though
        neither reproducer uses an enterprise-only feature.

      Attachments

        Activity

          People

            Elkin Andrei Elkin
            Elkin Andrei Elkin
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:

              Time Tracking

                Estimated:
                Original Estimate - 0d
                0d
                Remaining:
                Time Spent - 2d Remaining Estimate - 1d
                1d
                Logged:
                Time Spent - 2d Remaining Estimate - 1d
                2d

                Git Integration

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