File: trigger_function_lock_compare.inc

package info (click to toggle)
mysql-8.0 8.0.43-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,273,924 kB
  • sloc: cpp: 4,684,605; ansic: 412,450; pascal: 108,398; java: 83,641; perl: 30,221; cs: 27,067; sql: 26,594; sh: 24,181; python: 21,816; yacc: 17,169; php: 11,522; xml: 7,388; javascript: 7,076; makefile: 2,194; lex: 1,075; awk: 670; asm: 520; objc: 183; ruby: 97; lisp: 86
file content (189 lines) | stat: -rw-r--r-- 6,959 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# Compare locks taken for $statements in various contexts i.e
# When executed as a
# a. Normal statement inside a transaction.
# b. Substatement inside a trigger.
# c. Substatement inside a stored function.
# These operations are performed under all four isolation levels:
# READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE

# Usage:
# Set $statements to the query you want to execute in the
# above three contexts and compare locks taken.
# We set $skip_lock_check parameter to 1 to skip testing behavior in
# REPEATABLE READ mode for some buggy cases which are not fixed yet.

call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT");

--connect (con1, localhost, root,,)
# The main table, used for monitoring the locking strategy
CREATE TABLE t1 (
  id INT PRIMARY KEY,
  val varchar(4) NOT NULL,
  KEY idx_val (val)
) ENGINE=InnoDB;
# Preload the table with some data
INSERT INTO t1 VALUES (10, 'a'), (20, 'b');

# An additional table to enable scenarios with more complicated queries
CREATE TABLE t2 LIKE t1;
INSERT INTO t2 SELECT * FROM t1;

# A table to cause a TRIGGER to excecute $statements
CREATE TABLE t_triggerable (
  id INT PRIMARY KEY
) ENGINE=InnoDB;

DELIMITER //;
eval
CREATE TRIGGER bef_ins_t_triggerable BEFORE INSERT ON t_triggerable FOR EACH ROW
BEGIN
  $statements;
END;//

eval
CREATE FUNCTION statements_in_function() RETURNS INTEGER DETERMINISTIC
BEGIN
  $statements;
  RETURN 1;
END;//
DELIMITER ;//

# Helper table for lock comparison in regular case and trigger/function case.
CREATE TABLE stronger_or_equal(weaker VARCHAR(30), stronger VARCHAR(30));
INSERT INTO stronger_or_equal VALUES
("S", "S"),
("S", "X"),
("X", "X"),
("S,GAP", "S"),
("S,GAP", "X"),
("X,GAP", "X"),
("S,GAP", "S,GAP"),
("S,GAP", "X,GAP"),
("X,GAP", "X,GAP"),
("S,REC_NOT_GAP", "S"),
("S,REC_NOT_GAP", "X"),
("X,REC_NOT_GAP", "X"),
("S,REC_NOT_GAP", "S,REC_NOT_GAP"),
("S,REC_NOT_GAP", "X,REC_NOT_GAP"),
("X,REC_NOT_GAP", "X,REC_NOT_GAP"),
("X,INSERT_INTENTION", "X,INSERT_INTENTION"),
("X,GAP,INSERT_INTENTION", "X,GAP,INSERT_INTENTION");

--connect (con2, localhost, root,,)
--disable_warnings

--let $isolation_levels=READ UNCOMMITTED,READ COMMITTED,REPEATABLE READ,SERIALIZABLE
while ($isolation_levels)
{
  --let $isolation_level=`SELECT SUBSTRING_INDEX('$isolation_levels', ',', 1)`
  --let $isolation_levels= `SELECT SUBSTRING('$isolation_levels', LENGTH('$isolation_level') + 2)`

  --let $RUC_or_RC= `SELECT '$isolation_level'='READ UNCOMMITTED' OR '$isolation_level'='READ COMMITTED'`
  # InnoDB is limited to row-logging when transaction isolation level is READ COMMITTED or READ UNCOMMITTED.
  --let $incompatible_with_binlog_format= `SELECT '$binlog_format'='STATEMENT' AND '$RUC_or_RC'=1`

  if ( !$incompatible_with_binlog_format )
  {
    --eval SET SESSION TRANSACTION ISOLATION LEVEL $isolation_level
    --echo # Isolation level: $isolation_level

    --let $contexts=regular_case,trigger,function
    while ($contexts)
    {
      --let $context= `SELECT SUBSTRING_INDEX('$contexts', ',', 1)`
      --let $contexts= `SELECT SUBSTRING('$contexts', LENGTH('$context') + 2)`
      --let $skip_assert= 0;

      BEGIN;
      if ( $context == "regular_case" ) {
        --eval $statements
      }
      if ( $context == "trigger" ) {
        INSERT INTO t_triggerable VALUES (3);
      }
      if ( $context == "function" ) {
        --disable_result_log
        SELECT statements_in_function();
        --enable_result_log

        if ( $isolation_level == "REPEATABLE READ" ) {
          if ( $skip_lock_check ) {
            --let $skip_assert= 1
          }
        }
      }

      --connection con1
        --let $need_locks_data=1
        while ($need_locks_data) {
          eval CREATE TABLE locks_in_$context
            SELECT LOCK_MODE, LOCK_DATA, LOCK_TYPE
            FROM performance_schema.data_locks
            WHERE OBJECT_NAME='t1';

          --let $need_locks_data=0
          if (`SELECT COUNT(*) FROM locks_in_$context WHERE LOCK_DATA IS NULL AND LOCK_TYPE="RECORD"`) {
            # Sporadically, P_S can not access record's page in buffer pool and reconstruct LOCK_DATA values
            --eval DROP TABLE locks_in_$context
            --let $need_locks_data=1
            # wait a little to not cause furious spinning and allow for release of latches on page, if any
            --sleep 1
            # Try to bring back the pages into buffer pool using a non-locking SELECT, if they were missing
            --disable_result_log
            SELECT * FROM t1;
            --enable_result_log
          }
        }

      --connection con2
        ROLLBACK;

      if ( $context != "regular_case" ) {

        if ( $RUC_or_RC ) {
          --echo # Comparing locks taken for the above query in regular case and when inside a $context.
          --echo # Locks taken in $context should always be same as that in regular case.
          --let $diff_tables = locks_in_regular_case, locks_in_$context
          --source include/diff_tables.inc
        }
        if ( !$RUC_or_RC ) {
		  # Unlike for READ UNCOMMITTED/COMMITTED modes the locks taken inside of stored
          # function/trigger in REPEATABLE READ/SERIALIZABLE modes can be stronger than
          # those taken in regular case. This is due to fact that we support SBR
          # in these isolation modes. In case of SBR, statement which is run in regular case
          # and is not logged can use relaxed locking, while the same statement run from
          # trigger/stored function will be always "logged" due to logging of triggering
          # statement or function invocation and thus require stricter locks.
          if ( !$skip_assert ) {
            --echo # Comparing locks taken for the above query in regular case and when inside a $context.
            let $assert_text= Locks taken in $context should always be stronger or equal to locks taken in regular case;
            let $assert_cond= "
              [SELECT count(*) FROM locks_in_regular_case LEFT JOIN
                (SELECT * FROM stronger_or_equal JOIN locks_in_$context
                 ON locks_in_$context.LOCK_MODE = stronger WHERE locks_in_$context.LOCK_TYPE="RECORD") `st_or_eq`
               ON locks_in_regular_case.LOCK_MODE = weaker AND locks_in_regular_case.LOCK_DATA = st_or_eq.LOCK_DATA
               WHERE locks_in_regular_case.LOCK_TYPE="RECORD" AND st_or_eq.LOCK_MODE IS NULL]" = 0;
            source include/assert.inc;
          }
        }
        --echo
        eval DROP TABLE locks_in_$context;
      }
    }
    DROP TABLE locks_in_regular_case;
  }
}

--enable_warnings
--disconnect con2
--source include/wait_until_disconnected.inc
--disconnect con1
--source include/wait_until_disconnected.inc
--disable_query_log

--connection default
DROP TABLE stronger_or_equal;
DROP FUNCTION statements_in_function;
DROP TABLE t_triggerable;
DROP TABLE t1;
DROP TABLE t2;